Por Gabriel Landau en Elastic Security. Modificación de EDRSandblast: consulte el archivo README original a continuación.
Integra GodFault en EDR Sandblast, logrando el mismo resultado sin el uso de controladores vulnerables.
C:UsersuserDesktopOffsets>EDRSandblast.exe --kernelmode cmd
______ _____ _____ _____ _ _ _ _
| ____| __ | __ / ____| | | | | | | |
| |__ | | | | |__) | (___ __ _ _ __ __| | |__ | | __ _ ___| |_
| __| | | | | _ / ___ / _` | '_ / _` | '_ | |/ _` / __| __|
| |____| |__| | | ____) | (_| | | | | (_| | |_) | | (_| __ | |_
|______|_____/|_| _|_____/ __,_|_| |_|__,_|_.__/|_|__,_|___/__|
D3FC0N 30 Edition | Thomas DIOT (@_Qazeer) & Maxime MEIGNAN (@th3m4ks)
[!] If kernel mode bypass is enabled, it is recommended to enable usermode bypass as well (e.g. to unhook the NtLoadDriver API call)
[===== KERNEL MODE =====]
[+] Setting up prerequisites for the kernel read/write primitives...
[+] Loading kernel related offsets from the CSV file
[*] System's ntoskrnl.exe file version is: ntoskrnl_22621-1702.exe
[+] Offsets are available for this version of ntoskrnl.exe (ntoskrnl_22621-1702.exe)!
[+] Checking if any EDR kernel notify rountines are set for image loading, process and thread creations...
[+] [NotifyRountines] Enumerating process creation callbacks
[+] Running command: GodFault.exe -t 2684
[?] Server does not appear to be running. Attempting to install it...
[+] CSRSS PID is 748
[+] Testing initial ability to acquire PROCESS_ALL_ACCESS to System: Failure
[+] Ready. Spawning WinTcb.
[+] SpawnPPL: Waiting for child process to finish.
[+] Thread 2684 (KTHREAD FFFF910961E4C080) has been blessed by GodFault
[+] [NotifyRountines] fffff8034df25500 [cng.sys + 0x5500]
[+] [NotifyRountines] fffff8034e9efdc0 [WdFilter.sys + 0x4fdc0]
[+] [NotifyRountines] Found callback belonging to EDR driver WdFilter.sys
[+] [NotifyRountines] fffff803487bc460 [ksecdd.sys + 0x1c460]
[+] [NotifyRountines] fffff8034eff3fd0 [tcpip.sys + 0x13fd0]
[+] [NotifyRountines] fffff8034f5ed980 [iorate.sys + 0xd980]
[+] [NotifyRountines] fffff8034dea8890 [CI.dll + 0x88890]
[+] [NotifyRountines] fffff803525079f0 [dxgkrnl.sys + 0x179f0]
[+] [NotifyRountines] fffff80352be0a70 [vm3dmp.sys + 0x10a70]
[+] [NotifyRountines] fffff8036ebccd00 [peauth.sys + 0x3cd00]
[+] [NotifyRountines] fffff8036eda1550 [wtd.sys + 0x1550]
[+] [NotifyRountines] Found a total of 1 EDR / security products driver(s)
[+] [NotifyRountines] Enumerating thread creation callbacks
[+] [NotifyRountines] fffff8034e9f15c0 [WdFilter.sys + 0x515c0]
[+] [NotifyRountines] Found callback belonging to EDR driver WdFilter.sys
[+] [NotifyRountines] fffff8034e9f1350 [WdFilter.sys + 0x51350]
[+] [NotifyRountines] Found callback belonging to EDR driver WdFilter.sys
[+] [NotifyRountines] fffff8036eb71010 [mmcss.sys + 0x1010]
[+] [NotifyRountines] Found a total of 2 EDR / security products driver(s)
[+] [NotifyRountines] Enumerating image loading callbacks
[+] [NotifyRountines] fffff8034e9f0820 [WdFilter.sys + 0x50820]
[+] [NotifyRountines] Found callback belonging to EDR driver WdFilter.sys
[+] [NotifyRountines] fffff80352ab5710 [ahcache.sys + 0x25710]
[+] [NotifyRountines] Found a total of 1 EDR / security products driver(s)
[+] Checking if EDR callbacks are registered on processes and threads handle creation/duplication...
[+] [ObjectCallblacks] Enumerating Process object callbacks :
[+] [ObjectCallblacks] Callback at FFFF800C5E2F2940 for handle creations & duplications:
[+] [ObjectCallblacks] Status: Enabled
[+] [ObjectCallblacks] Preoperation at 0xfffff8034e9eda30 [WdFilter.sys + 0x4da30]
[+] [ObjectCallblacks] Callback belongs to an EDR and is enabled!
[+] [ObjectCallblacks] Enumerating Thread object callbacks :
[+] [ObjectCallblacks] Object callbacks are present !
[+] [ETWTI] Checking the ETW Threat Intelligence Provider state...
[+] [ETWTI] ETW Threat Intelligence Provider is ENABLED!
[+] Process is NOT "safe" to launch our payload, removing monitoring and starting another process...
[+] [ETWTI] Disabling the ETW Threat Intel provider by patching ProviderEnableInfo at 0xffff91095ce8c430 with 0x00.
[+] [ETWTI] The ETW Threat Intel provider was successfully disabled!
[+] Removing kernel callbacks registered by EDR for process creation, thread creation and image loading...
[+] [NotifyRountines] Removing process creation callbacks
[+] [NotifyRountines] Removing callback of EDR driver "WdFilter.sys" [callback addr: 0xfffff8034970c2a8 | callback struct: 0xffff91095dbf3a5f | callback function: 0xfffff8034e9efdc0]
[+] [NotifyRountines] Removing thread creation callbacks
[+] [NotifyRountines] Removing callback of EDR driver "WdFilter.sys" [callback addr: 0xfffff8034970c4a0 | callback struct: 0xffff91095dbf3b1f | callback function: 0xfffff8034e9f15c0]
[+] [NotifyRountines] Removing callback of EDR driver "WdFilter.sys" [callback addr: 0xfffff8034970c4a8 | callback struct: 0xffff91095dbf3b4f | callback function: 0xfffff8034e9f1350]
[+] [NotifyRountines] Removing image loading callbacks
[+] [NotifyRountines] Removing callback of EDR driver "WdFilter.sys" [callback addr: 0xfffff8034970c6a0 | callback struct: 0xffff91095dbf3e4f | callback function: 0xfffff8034e9f0820]
[+] Disabling kernel callbacks registered by EDR for process and thread opening or handle duplication...
[+] [ObjectCallblacks] Disabling WdFilter.sys callback...
[+] All EDR drivers were successfully removed from Kernel callbacks!
==================================================
Starting a new unmonitored process...
==================================================
[!] If kernel mode bypass is enabled, it is recommended to enable usermode bypass as well (e.g. to unhook the NtLoadDriver API call)
[===== KERNEL MODE =====]
[+] Setting up prerequisites for the kernel read/write primitives...
[+] Loading kernel related offsets from the CSV file
[*] System's ntoskrnl.exe file version is: ntoskrnl_22621-1702.exe
[+] Offsets are available for this version of ntoskrnl.exe (ntoskrnl_22621-1702.exe)!
[+] Checking if any EDR kernel notify rountines are set for image loading, process and thread creations...
[+] [NotifyRountines] Enumerating process creation callbacks
[+] Running command: GodFault.exe -t 8344
[+] Thread 8344 (KTHREAD FFFF91096169F080) has been blessed by GodFault
[+] Initial blessing successful
[+] [NotifyRountines] fffff8034df25500 [cng.sys + 0x5500]
[+] [NotifyRountines] fffff803487bc460 [ksecdd.sys + 0x1c460]
[+] [NotifyRountines] fffff8034eff3fd0 [tcpip.sys + 0x13fd0]
[+] [NotifyRountines] fffff8034f5ed980 [iorate.sys + 0xd980]
[+] [NotifyRountines] fffff8034dea8890 [CI.dll + 0x88890]
[+] [NotifyRountines] fffff803525079f0 [dxgkrnl.sys + 0x179f0]
[+] [NotifyRountines] fffff80352be0a70 [vm3dmp.sys + 0x10a70]
[+] [NotifyRountines] fffff8036ebccd00 [peauth.sys + 0x3cd00]
[+] [NotifyRountines] fffff8036eda1550 [wtd.sys + 0x1550]
[+] [NotifyRountines] No EDR driver(s) found!
[+] [NotifyRountines] Enumerating thread creation callbacks
[+] [NotifyRountines] fffff8036eb71010 [mmcss.sys + 0x1010]
[+] [NotifyRountines] No EDR driver(s) found!
[+] [NotifyRountines] Enumerating image loading callbacks
[+] [NotifyRountines] fffff80352ab5710 [ahcache.sys + 0x25710]
[+] [NotifyRountines] No EDR driver(s) found!
[+] Checking if EDR callbacks are registered on processes and threads handle creation/duplication...
[+] [ObjectCallblacks] Enumerating Process object callbacks :
[+] [ObjectCallblacks] Callback at FFFF800C5E2F2940 for handle creations & duplications:
[+] [ObjectCallblacks] Status: Disabled
[+] [ObjectCallblacks] Preoperation at 0xfffff8034e9eda30 [WdFilter.sys + 0x4da30]
[+] [ObjectCallblacks] Callback belongs to an EDR but is disabled.
[+] [ObjectCallblacks] Enumerating Thread object callbacks :
[+] [ObjectCallblacks] Object callbacks are not found !
[+] [ETWTI] Checking the ETW Threat Intelligence Provider state...
[+] [ETWTI] ETW Threat Intelligence Provider is DISABLED!
[+] Process is "safe" to launch our payload
[+] Kernel callbacks have normally been removed, starting cmd.exe
WARNING: EDR kernel callbacks will be restored after exiting the cmd prompt (by typing exit)
WARNING: While unlikely, the longer the callbacks are removed, the higher the chance of being detected / causing a BSoD upon restore is!
Microsoft Windows [Version 10.0.22621.1702]
(c) Microsoft Corporation. All rights reserved.
C:UsersuserDesktopOffsets>
? ¿REAME original a continuación?
EDRSandBlast
es una herramienta escrita en C
que convierte en arma un controlador firmado vulnerable para evitar las detecciones EDR (notificar devoluciones de llamadas de rutina, devoluciones de llamadas de objetos y proveedor ETW TI
) y protecciones LSASS
. También se implementan múltiples técnicas de desenganche de la zona de usuario para evadir el monitoreo de la zona de usuario.
A partir del lanzamiento, se utilizó una combinación de técnicas de usuario ( --usermode
) y Kernel-land ( --kernelmode
) para volcar la memoria LSASS
bajo el escrutinio de EDR, sin ser bloqueada ni generar eventos relacionados con "OS Credential Dumping" en el producto (nube). ) consola. Las pruebas se realizaron en 3 productos EDR distintos y tuvieron éxito en cada caso.
Los productos EDR utilizan devoluciones de llamada de "Notificar rutinas" del kernel en Windows para que el kernel les notifique la actividad del sistema, como la creación de procesos e hilos y la carga de imágenes ( exe
/ DLL
).
Estas devoluciones de llamada del kernel se definen desde el kernel, generalmente desde el controlador que implementa las devoluciones de llamada, utilizando una serie de API documentadas ( nt!PsSetCreateProcessNotifyRoutine
, nt!PsSetCreateThreadNotifyRoutine
, etc.). Estas API agregan rutinas de devolución de llamada proporcionadas por el controlador a conjuntos de rutinas no documentadas en el espacio del kernel:
PspCreateProcessNotifyRoutine
para la creación de procesosPspCreateThreadNotifyRoutine
para la creación de hilosPspLoadImageNotifyRoutine
para carga de imágenes EDRSandBlast
enumera las rutinas definidas en esas matrices y elimina cualquier rutina de devolución de llamada vinculada a una lista predefinida de controladores EDR (se admiten más de 1000 controladores de productos de seguridad; consulte la sección Detección de controladores EDR). La enumeración y eliminación son posibles mediante la explotación de un Primitiva arbitraria de lectura/escritura de memoria del Kernel proporcionada por la explotación de un controlador vulnerable (consulte la sección Controladores vulnerables).
Las compensaciones de las matrices antes mencionadas se recuperan mediante múltiples técnicas; consulte la sección Compensaciones.
Los productos EDR (e incluso EPP) a menudo registran "devoluciones de llamada de objetos" mediante el uso de la API del kernel nt!ObRegisterCallbacks
. Estas devoluciones de llamada permiten que el producto de seguridad sea notificado en cada generación de identificador en tipos de objetos específicos (las devoluciones de llamada de objetos relacionados con procesos, subprocesos y escritorios ahora son compatibles con Windows). Puede ocurrir una generación de identificador al abrir un objeto (llamada a OpenProcess
, OpenThread
, etc.), así como también una duplicación de identificadores (llamada a DuplicateHandle
, etc.).
Al ser notificado por el núcleo sobre cada una de estas operaciones, un producto de seguridad puede analizar la legitimidad de la creación del identificador ( por ejemplo, un proceso desconocido está intentando abrir LSASS ) e incluso bloquearlo si se detecta una amenaza.
En cada registro de devolución de llamada utilizando ObRegisterCallbacks
, se agrega un nuevo elemento a la lista de doble enlace CallbackList
presente en el objeto _OBJECT_TYPE
que describe el tipo de objeto afectado por la devolución de llamada (ya sea un proceso, un subproceso o un escritorio). Desafortunadamente, estos elementos se describen mediante una estructura que Microsoft no documenta ni publica en archivos de símbolos. Sin embargo, estudiarlo desde varias versiones ntoskrnl.exe
parece indicar que la estructura no cambió entre (al menos) las versiones 10240 y 22000 de Windows 10 (de 2015 a 2022).
La estructura mencionada, que representa un registro de devolución de llamada de un objeto, es la siguiente:
typedef struct OB_CALLBACK_ENTRY_t {
LIST_ENTRY CallbackList ; // linked element tied to _OBJECT_TYPE.CallbackList
OB_OPERATION Operations ; // bitfield : 1 for Creations, 2 for Duplications
BOOL Enabled ; // self-explanatory
OB_CALLBACK * Entry ; // points to the structure in which it is included
POBJECT_TYPE ObjectType ; // points to the object type affected by the callback
POB_PRE_OPERATION_CALLBACK PreOperation ; // callback function called before each handle operation
POB_POST_OPERATION_CALLBACK PostOperation ; // callback function called after each handle operation
KSPIN_LOCK Lock ; // lock object used for synchronization
} OB_CALLBACK_ENTRY ;
La estructura OB_CALLBACK
mencionada anteriormente tampoco está documentada y está definida por lo siguiente:
typedef struct OB_CALLBACK_t {
USHORT Version ; // usually 0x100
USHORT OperationRegistrationCount ; // number of registered callbacks
PVOID RegistrationContext ; // arbitrary data passed at registration time
UNICODE_STRING AltitudeString ; // used to determine callbacks order
struct OB_CALLBACK_ENTRY_t EntryItems [ 1 ]; // array of OperationRegistrationCount items
WCHAR AltitudeBuffer [ 1 ]; // is AltitudeString.MaximumLength bytes long, and pointed by AltitudeString.Buffer
} OB_CALLBACK ;
Para deshabilitar las devoluciones de llamadas de objetos registrados en EDR, se implementan tres técnicas en EDRSandblast
; sin embargo sólo uno está habilitado por el momento.
Enabled
de OB_CALLBACK_ENTRY
Esta es la técnica predeterminada habilitada en EDRSandblast
. Para detectar y deshabilitar las devoluciones de llamadas de objetos relacionados con EDR, se explora la lista CallbackList
ubicada en los objetos _OBJECT_TYPE
vinculados a los tipos Proceso y Subproceso . Ambos _OBJECT_TYPE
están señalados por símbolos globales públicos en el kernel, PsProcessType
y PsThreadType
.
Se supone que cada elemento de la lista se ajusta a la estructura OB_CALLBACK_ENTRY
descrita anteriormente (suposición que parece ser válida al menos en todas las compilaciones de Windows 10 al momento de escribir este artículo). Las funciones definidas en los campos PreOperation
y PostOperation
están ubicadas para verificar si pertenecen a un controlador EDR y, de ser así, las devoluciones de llamada simplemente se desactivan alternando el indicador Enabled
.
Si bien es una técnica bastante segura, tiene el inconveniente de depender de una estructura no documentada; Para reducir el riesgo de manipulación insegura de esta estructura, se realizan comprobaciones básicas para validar que algunos campos tengan los valores esperados:
Enabled
es TRUE
o FALSE
( no se ría, un BOOL
es un int
, por lo que podría ser cualquier cosa que no sea 1
o 0
);Operations
son OB_OPERATION_HANDLE_CREATE
, OB_OPERATION_HANDLE_DUPLICATE
o ambas;ObjectType
apunta a PsProcessType
o PsThreadType
. CallbackList
de hilos y procesos Otra estrategia que no se basa en una estructura no documentada (y por lo tanto es teóricamente más robusta contra los cambios del kernel NT) es la desvinculación de toda la CallbackList
tanto para procesos como para subprocesos. El objeto _OBJECT_TYPE
es el siguiente:
struct _OBJECT_TYPE {
LIST_ENTRY TypeList ;
UNICODE_STRING Name ;
[...]
_OBJECT_TYPE_INITIALIZER TypeInfo ;
[...]
LIST_ENTRY CallbackList ;
}
Hacer que los punteros Flink
y Blink
de CallbackList
LIST_ENTRY
apunten al propio LIST_ENTRY
efectivamente deja la lista vacía. Dado que la estructura _OBJECT_TYPE
se publica en los símbolos del núcleo, la técnica no se basa en estructuras/compensaciones codificadas. Sin embargo, tiene algunos inconvenientes.
El primero no puede desactivar únicamente las devoluciones de llamadas desde EDR; de hecho, la técnica afecta a todas las devoluciones de llamadas de objetos que podrían haber sido registradas por software "legítimo". Sin embargo, debe tenerse en cuenta que ningún componente preinstalado en Windows 10 utiliza las devoluciones de llamadas de objetos (en el momento de escribir este artículo), por lo que deshabilitarlas no debería afectar la estabilidad de la máquina (aún más si la deshabilitación es solo temporal).
El segundo inconveniente es que la operación de manejo de procesos o subprocesos es muy frecuente (casi continua) en el funcionamiento normal del sistema operativo. Como tal, si la primitiva de escritura del kernel utilizada no puede realizar una escritura QWORD
"atómicamente", existe una buena posibilidad de que el kernel acceda al puntero _OBJECT_TYPE.CallbackList.Flink
en medio de su sobrescritura. Por ejemplo, el controlador vulnerable MSI RTCore64.sys
solo puede realizar una escritura DWORD
a la vez, por lo que se necesitarán 2 IOCTL distintos para sobrescribir el puntero, entre los cuales el kernel tiene una alta probabilidad de usarlo (lo que resulta en un bloqueo). Por otro lado, el controlador DELL vulnerable DBUtil_2_3.sys
puede realizar escrituras de tamaños arbitrarios en un IOCTL, por lo que utilizar este método no corre el riesgo de provocar un bloqueo.
Una última técnica que encontramos fue deshabilitar por completo el soporte de devoluciones de llamadas de objetos para subprocesos y procesos. Dentro de la estructura _OBJECT_TYPE
correspondiente a los tipos de proceso y subproceso reside un campo TypeInfo
, siguiendo la estructura documentada _OBJECT_TYPE_INITIALIZER
. Este último contiene un campo de bits ObjectTypeFlags
, cuyo indicador SupportsObjectCallbacks
determina si el tipo de objeto descrito (Proceso, Subproceso, Escritorio, Token, Archivo, etc.) admite el registro de devolución de llamada de objeto o no. Como se indicó anteriormente, solo los tipos de objetos Proceso, Subproceso y Escritorio admiten estas devoluciones de llamada en una instalación de Windows al momento de escribir este artículo.
Dado que ObpCreateHandle
u ObDuplicateObject
verifican el bit SupportsObjectCallbacks
incluso antes de leer CallbackList
(y antes de ejecutar devoluciones de llamada, por supuesto), invertir el bit en el tiempo de ejecución del kernel deshabilita efectivamente la ejecución de todas las devoluciones de llamadas de objetos.
El principal inconveniente del método es simplemente que KPP (" PatchGuard ") monitorea la integridad de algunas (¿todas?) estructuras _OBJECT_TYPE
y activa una 0x109 Bug Check
con el parámetro 4 igual a 0x8
, lo que significa que se ha alterado una estructura de tipo de objeto.
Sin embargo, realizar la desactivación/reactivación (y la acción "maliciosa" intermedia) lo suficientemente rápido debería ser suficiente para "competir" con PatchGuard (a menos que tenga mala suerte y se realice una verificación periódica justo en el momento equivocado).
El proveedor ETW Microsoft-Windows-Threat-Intelligence
registra datos sobre los usos de algunas API de Windows comúnmente utilizadas con fines malintencionados. Esto incluye la API nt!MiReadWriteVirtualMemory
, llamada por nt!NtReadVirtualMemory
(que se utiliza para volcar la memoria LSASS
) y monitoreada por la función nt!EtwTiLogReadWriteVm
.
Los productos EDR pueden consumir los registros producidos por el proveedor ETW TI
a través de servicios o procesos que se ejecutan como, respectivamente, SERVICE_LAUNCH_PROTECTED_ANTIMALWARE_LIGHT
o PS_PROTECTED_ANTIMALWARE_LIGHT
, y asociados con un controlador Early Launch Anti Malware (ELAM)
.
Como lo publicó slaeryan
en una publicación de blog CNO Development Labs
, el proveedor ETW TI
se puede deshabilitar por completo parcheando, en la memoria del kernel, su atributo ProviderEnableInfo
a 0x0
. Consulte la excelente publicación del blog antes mencionada para obtener más información sobre la técnica.
De manera similar a la eliminación de devoluciones de llamadas del kernel, las compensaciones necesarias ntoskrnl.exe
( nt!EtwThreatIntProvRegHandleOffset
, GuidEntry
de _ETW_REG_ENTRY
y ProviderEnableInfo
de _ETW_GUID_ENTRY
) se calculan en el archivo NtoskrnlOffsets.csv
para varias versiones del kernel de Windows.
Para monitorear fácilmente las acciones realizadas por los procesos, los productos EDR a menudo implementan un mecanismo llamado enlace de usuario . Primero, los productos EDR registran una devolución de llamada del kernel (generalmente devoluciones de llamada de carga de imágenes o creación de procesos , ver arriba) que les permite recibir notificaciones cada vez que se inicia un proceso.
Cuando Windows carga un proceso, y antes de que realmente comience, el EDR puede inyectar alguna DLL personalizada en el espacio de direcciones del proceso, que contiene su lógica de monitoreo. Mientras se carga, esta DLL inyecta " ganchos " al inicio de cada función que debe ser monitoreada por el EDR. En tiempo de ejecución, cuando el proceso bajo vigilancia llama a las funciones monitoreadas, estos enlaces redirigen el flujo de control a algún código de supervisión presente en la DLL del EDR, lo que le permite inspeccionar argumentos y valores de retorno de estas llamadas.
La mayoría de las veces, las funciones monitoreadas son llamadas al sistema (como NtReadVirtualMemory
, NtOpenProcess
, etc.), cuyas implementaciones residen en ntdll.dll
. La interceptación de llamadas a funciones Nt*
permite que los productos estén lo más cerca posible del límite de la zona de usuario/tierra del núcleo (mientras permanecen en la zona de usuario), pero también se pueden monitorear funciones de algunas DLL de nivel superior.
A continuación se muestran ejemplos de la misma función, antes y después de engancharse al producto EDR:
NtProtectVirtualMemory proc near
mov r10 , rcx
mov eax , 50h
test byte ptr ds : 7FFE0308h , 1
jnz short loc_18009D1E5
syscall
retn
loc_18009D1E5:
int 2Eh
retn
NtProtectVirtualMemory endp
NtProtectVirtualMemory proc near
jmp sub_7FFC74490298 ; --> "hook", jump to EDR analysis function
int 3 ; overwritten instructions
int 3 ; overwritten instructions
int 3 ; overwritten instructions
test byte_7FFE0308 , 1 ; <-- execution resumes here after analysis
jnz short loc_7FFCB44AD1E5
syscall
retn
loc_7FFCB44AD1E5:
int 2Eh
retn
NtProtectVirtualMemory endp
Los ganchos de la zona de usuario tienen la "debilidad" de estar ubicados en la memoria de la zona de usuario, lo que significa que son directamente observables y modificables por el proceso bajo escrutinio. Para detectar automáticamente enlaces en el espacio de direcciones del proceso, la idea principal es comparar las diferencias entre la DLL original en el disco y la biblioteca que reside en la memoria, que ha sido potencialmente alterada por un EDR. Para realizar esta comparación, EDRSandblast sigue los siguientes pasos:
InLoadOrderModuleList
ubicado en el PEB
(para evitar llamar a cualquier API que pueda ser monitoreada y sospechosa) Nota: El proceso se puede generalizar para encontrar diferencias en cualquier parte de las secciones que no se pueden escribir y no solo al inicio de las funciones exportadas, por ejemplo, si los productos EDR comienzan a aplicar ganchos en medio de la función :) Por lo tanto, la herramienta no lo utiliza. se ha implementado en findDiffsInNonWritableSections
.
Para evitar el monitoreo realizado por estos ganchos, son posibles múltiples técnicas, y cada una tiene ventajas e inconvenientes.
El método más intuitivo para evitar el monitoreo basado en ganchos es eliminar los ganchos. Dado que los ganchos están presentes en la memoria a la que puede acceder el proceso mismo, para eliminar un gancho, el proceso puede simplemente:
Este enfoque es bastante simple y se puede utilizar para eliminar todos los anzuelos detectados de una sola vez. Realizado por una herramienta ofensiva al principio, esto permite que el resto del código desconozca por completo el mecanismo de enganche y funcione normalmente sin ser monitoreado.
Sin embargo, tiene dos inconvenientes principales. El EDR probablemente esté monitoreando el uso de NtProtectVirtualMemory
, por lo que usarlo para cambiar los permisos de la página donde se instalaron los ganchos es (al menos conceptualmente) una mala idea. Además, si el EDR ejecuta un hilo y verifica periódicamente la integridad de los enlaces, esto también podría desencadenar alguna detección.
Para obtener detalles de implementación, verifique la ruta del código de la función unhook()
cuando unhook_method
sea UNHOOK_WITH_NTPROTECTVIRTUALMEMORY
.
Nota importante: para simplificar, esta técnica se implementa en EDRSandblast como técnica base utilizada para mostrar las otras técnicas de derivación; cada uno de ellos demuestra cómo obtener una versión no supervisada de NtProtectVirtualMemory
, pero luego realiza la misma operación (desenganchando un gancho específico).
Para omitir un gancho específico, es posible simplemente "saltar" y ejecutar el resto de la función tal como está. Primero, los bytes originales de la función monitoreada, que han sido sobrescritos por el EDR para instalar el gancho, deben recuperarse del archivo DLL. En nuestro ejemplo de código anterior, estos serían los bytes correspondientes a las siguientes instrucciones:
mov r10 , rcx
mov eax , 50h
Identificar estos bytes es una tarea sencilla ya que podemos realizar una diferenciación limpia de las versiones de memoria y de disco de la biblioteca, como se describió anteriormente. Luego, ensamblamos una instrucción de salto diseñada para redirigir el flujo de control al código que sigue inmediatamente al enlace, en la dirección NtProtectVirtualMemory + sizeof(overwritten_instructions)
jmp NtProtectVirtualMemory + 8
Finalmente, concatenamos estos códigos de operación, los almacenamos en la memoria (nuevamente) ejecutable y mantenemos un puntero hacia ellos. Este objeto se llama " trampolín " y luego puede usarse como un puntero de función, estrictamente equivalente a la función NtProtectVirtualMemory
original.
El principal beneficio de esta técnica, como todas las técnicas siguientes, es que el gancho nunca se borra, por lo que cualquier verificación de integridad realizada en los ganchos por el EDR debería pasar. Sin embargo, requiere asignar memoria grabable y luego ejecutable, lo cual es típico de una asignación de código shell, lo que atrae el escrutinio del EDR.
Para obtener detalles de implementación, verifique la ruta del código de la función unhook()
cuando unhook_method
sea UNHOOK_WITH_INHOUSE_NTPROTECTVIRTUALMEMORY_TRAMPOLINE
. Recuerde que la técnica solo se muestra en nuestra implementación y, al final, se utiliza para eliminar ganchos de la memoria, como se muestra en cada técnica a continuación.
El producto EDR, para que su gancho funcione, debe guardar en algún lugar de la memoria los códigos de operación que ha eliminado. Lo peor ( o "mejor", desde el punto de vista del atacante ), para utilizar eficazmente las instrucciones originales, es probable que EDR se haya asignado un trampolín en algún lugar para ejecutar la función original después de haber interceptado la llamada.
Este trampolín se puede buscar y utilizar como reemplazo de la función enganchada, sin necesidad de asignar memoria ejecutable, ni llamar a ninguna API excepto VirtualQuery
, que muy probablemente no se monitorea por ser una función inocua.
Para encontrar el trampolín en la memoria, navegamos por todo el espacio de direcciones usando VirtualQuery
buscando memoria comprometida y ejecutable. Para cada una de esas regiones de memoria, la escaneamos para buscar una instrucción de salto que apunte a la dirección siguiendo las instrucciones sobrescritas ( NtProtectVirtualMemory+8
en nuestro ejemplo anterior). Luego, el trampolín se puede usar para llamar a la función de gancho sin activar el gancho.
Esta técnica funciona sorprendentemente bien ya que recupera casi todos los trampolines en EDR probados. Para obtener detalles de implementación, verifique la ruta del código de la función unhook()
cuando unhook_method
sea UNHOOK_WITH_EDR_NTPROTECTVIRTUALMEMORY_TRAMPOLINE
.
Otro método sencillo para obtener acceso a una versión no supervisada de la función NtProtectVirtualMemory
es cargar una versión duplicada de la biblioteca ntdll.dll
en el espacio de direcciones del proceso. Dado que se pueden cargar dos DLL idénticas en el mismo proceso, siempre que tengan nombres diferentes, simplemente podemos copiar el archivo ntdll.dll
legítimo en otra ubicación, cargarlo usando LoadLibrary
(o volver a implementar el proceso de carga) y acceder a la función usando GetProcAddress
Por ejemplo.
Esta técnica es muy sencilla de entender e implementar, y tiene buenas posibilidades de éxito, ya que la mayoría de los productos EDR no reinstalan ganchos en las DLL recién cargadas una vez que el proceso se está ejecutando. Sin embargo, el principal inconveniente es que copiar archivos binarios firmados por Microsoft con un nombre diferente a menudo se considera sospechoso para los productos EDR.
No obstante, esta técnica se implementa en EDRSandblast
. Para obtener detalles de implementación, verifique la ruta del código de la función unhook()
cuando unhook_method
sea UNHOOK_WITH_DUPLICATE_NTPROTECTVIRTUALMEMORY
.
Para utilizar funciones relacionadas con llamadas al sistema, un programa puede volver a implementar llamadas al sistema (en ensamblaje) para llamar a las funciones correspondientes del sistema operativo sin tocar el código en ntdll.dll
, que podría ser monitoreado por el EDR. Esto evita por completo cualquier enlace de usuario realizado en funciones de llamada al sistema en ntdll.dll
.
Sin embargo, esto tiene algunos inconvenientes. Primero, esto implica poder conocer la lista de números de llamadas al sistema de funciones que necesita el programa, que cambia para cada versión de Windows. Sin embargo, esto se mitiga mediante la implementación de múltiples heurísticas que se sabe que funcionan en todas las versiones anteriores de Windows NT (clasificación de las exportaciones Zw*
de ntdll
, búsqueda de mov rax, #syscall_number
en la función ntdll
asociada, etc.) y comprobando que todos devuelven el mismo resultado (consulte Syscalls.c
para obtener más detalles).
Además, las funciones que técnicamente no son llamadas al sistema (por ejemplo, LoadLibraryX
/ LdrLoadDLL
) también podrían monitorearse y no pueden simplemente reimplementarse usando una llamada al sistema.
La técnica de llamadas directas al sistema se implementa en EDRSandblast. Como se indicó anteriormente, solo se usa para ejecutar NtProtectVirtualMemory
de forma segura y eliminar todos los ganchos detectados.
Para obtener detalles de implementación, verifique la ruta del código de la función unhook()
cuando unhook_method
sea UNHOOK_WITH_DIRECT_SYSCALL
.
Como se indicó anteriormente, cada acción que necesita lectura o escritura en la memoria del kernel depende de un controlador vulnerable para proporcionar esta primitiva. En EDRSanblast, agregar soporte para un nuevo controlador que proporcione la primitiva de lectura/escritura se puede hacer "fácilmente", solo es necesario implementar tres funciones:
ReadMemoryPrimitive_DRIVERNAME(SIZE_T Size, DWORD64 Address, PVOID Buffer)
, que copia bytes Size
de Address
del kernel Address al Buffer
del usuario;WriteMemoryPrimitive_DRIVERNAME(SIZE_T Size, DWORD64 Address, PVOID Buffer)
, que copia bytes Size
del Buffer
del usuario a Address
del kernel Address;CloseDriverHandle_DRIVERNAME()
que garantiza que todos los identificadores del controlador estén cerrados (necesario antes de la operación de desinstalación que, por el momento, es independiente del controlador). Como ejemplo, EDRSandblast admite actualmente dos controladores, RTCore64.sys
(SHA256: 01AA278B07B58DC46C84BD0B1B5C8E9EE4E62EA0BF7A695862444AF32E87F1FD
) y DBUtils_2_3.sys
(SHA256: 0296e2ce999e67c76352613a718e11516fe1b0efc3ffdb8918fc999dd76a73a5
). El siguiente código en KernelMemoryPrimitives.h
debe actualizarse si es necesario cambiar el controlador vulnerable utilizado o si se implementa uno nuevo.
#define RTCore 0
#define DBUtil 1
// Select the driver to use with the following #define
#define VULN_DRIVER RTCore
#if VULN_DRIVER == RTCore
#define DEFAULT_DRIVER_FILE TEXT("RTCore64.sys")
#define CloseDriverHandle CloseDriverHandle_RTCore
#define ReadMemoryPrimitive ReadMemoryPrimitive_RTCore
#define WriteMemoryPrimitive WriteMemoryPrimitive_RTCore
#elif VULN_DRIVER == DBUtil
#define DEFAULT_DRIVER_FILE TEXT("DBUtil_2_3.sys")
#define CloseDriverHandle CloseDriverHandle_DBUtil
#define ReadMemoryPrimitive ReadMemoryPrimitive_DBUtil
#define WriteMemoryPrimitive WriteMemoryPrimitive_DBUtil
#endif
Actualmente se utilizan múltiples técnicas para determinar si un controlador o proceso específico pertenece a un producto EDR o no.
En primer lugar, el nombre del conductor puede utilizarse simplemente para ese fin. De hecho, Microsoft asigna números específicos llamados "Altitudes" para todos los controladores que necesitan insertar devoluciones de llamada en el kernel. Esto permite un orden determinista en la ejecución de las devoluciones de llamada, independiente del orden de registro, pero solo basado en el uso del controlador. En MSDN se puede encontrar una lista de (proveedores de) conductores que han reservado una altitud específica. Como consecuencia, Microsoft ofrece una lista casi completa de nombres de controladores de seguridad vinculados a productos de seguridad, principalmente en las listas "FSFilter Anti-Virus" y "FSFilter Activity Monitor". Estas listas de nombres de controladores están integradas en EDRSandblast, así como contribuciones adicionales.
Además, los ejecutables EDR y DLL suelen estar firmados digitalmente utilizando el certificado de firma del proveedor. Por lo tanto, verificar el firmante de un ejecutable o DLL asociado a un proceso puede permitir identificar rápidamente los productos EDR.
Además, los controladores deben estar firmados directamente por Microsoft para poder cargarlos en el espacio del kernel. Si bien el proveedor del conductor no es directamente el firmante del conductor, parecería que el nombre del proveedor todavía está incluido dentro de un atributo de la firma; Sin embargo, esta técnica de detección aún debe investigarse e implementarse.
Finalmente, cuando se enfrenta a un EDR desconocido para EDRSandblast, el mejor enfoque es ejecutar la herramienta en modo "auditoría" y verificar la lista de controladores que tienen devoluciones de llamada del kernel registradas; luego se puede agregar el nombre del controlador a la lista, volver a compilar la herramienta y ejecutarla nuevamente.
El mecanismo Local Security Authority (LSA) Protection
, introducido por primera vez en Windows 8.1 y Windows Server 2012 R2, aprovecha la tecnología Protected Process Light (PPL)
para restringir el acceso al proceso LSASS
. La protección PPL
regula y restringe operaciones, como la inyección de memoria o el volcado de memoria de procesos protegidos, incluso desde un proceso que tenga el privilegio SeDebugPrivilege
. Según el modelo de protección de procesos, solo los procesos que se ejecutan con niveles de protección más altos pueden realizar operaciones en procesos protegidos.
La estructura _EPROCESS
, utilizada por el kernel de Windows para representar un proceso en la memoria del kernel, incluye un campo _PS_PROTECTION
que define el nivel de protección de un proceso a través de sus atributos Type
( _PS_PROTECTED_TYPE
) y Signer
( _PS_PROTECTED_SIGNER
).
Al escribir en la memoria del kernel, el proceso EDRSandblast puede actualizar su propio nivel de protección a PsProtectedSignerWinTcb-Light
. Este nivel es suficiente para volcar la memoria del proceso LSASS
, ya que "domina" a PsProtectedSignerLsa-Light
, el nivel de protección del proceso LSASS
que se ejecuta con el mecanismo RunAsPPL
.
EDRSandBlast
implementa la autoprotección de la siguiente manera:
NtQuerySystemInformation
para encontrar el identificador abierto en el proceso actual y la dirección de la estructura EPROCESS
del proceso actual en la memoria del kernel.Micro-Star MSI Afterburner
para sobrescribir el campo _PS_PROTECTION
del proceso actual en la memoria del kernel. Los desplazamientos del campo _PS_PROTECTION
relativos a la estructura EPROCESS
(definida por la versión ntoskrnl
en uso) se calculan en el archivo NtoskrnlOffsets.csv
. Microsoft Credential Guard
es una tecnología de aislamiento basada en virtualización, introducida en Windows 10 (Enterprise edition)
de Microsoft que impide el acceso directo a las credenciales almacenadas en el proceso LSASS
.
Cuando se activa Credentials Guard
, se crea un proceso LSAIso
( LSA aislado ) en Virtual Secure Mode
, una característica que aprovecha las extensiones de virtualización de la CPU para proporcionar seguridad adicional de los datos en la memoria. El acceso al proceso LSAIso
está restringido incluso para un acceso con el contexto de seguridad NT AUTHORITYSYSTEM
. Al procesar un hash, el proceso LSA
realiza una llamada RPC
al proceso LSAIso
y espera a que continúe el resultado LSAIso
. Por lo tanto, el proceso LSASS
no contendrá ningún secreto y almacenará LSA Isolated Data
.
Como se indica en una investigación original realizada por N4kedTurtle
: " Wdigest
se puede habilitar en un sistema con Credential Guard parcheando los valores de g_fParameter_useLogonCredential
y g_IsCredGuardEnabled
en la memoria". La activación de Wdigest
dará como resultado que las credenciales de texto sin cifrar se almacenen en la memoria LSASS
para cualquier nuevo inicio de sesión interactivo (sin necesidad de reiniciar el sistema). Consulte la publicación original del blog de investigación para obtener más detalles sobre esta técnica.
EDRSandBlast
simplemente hace que el PoC original sea un poco más compatible con opsec y brinda soporte para varias versiones wdigest.dll
(a través de compensaciones calculadas para g_fParameter_useLogonCredential
y g_IsCredGuardEnabled
).
Para realizar de manera confiable operaciones de omisión de monitoreo del kernel, EDRSandblast necesita saber exactamente dónde leer y escribir la memoria del kernel. Esto se hace utilizando compensaciones de variables globales dentro de la imagen de destino (ntoskrnl.exe, wdigest.dll), así como también compensaciones de campos específicos en estructuras cuyas definiciones publica Microsoft en archivos de símbolos. Estas compensaciones son específicas de cada compilación de las imágenes de destino y deben recopilarse al menos una vez para una versión de plataforma específica.
La elección de utilizar compensaciones "codificadas" en lugar de búsquedas de patrones para localizar las estructuras y variables utilizadas por EDRSandblast se justifica por el hecho de que las API no documentadas responsables de la adición o eliminación de devoluciones de llamadas del Kernel están sujetas a cambios y que cualquier intento de leer o escribir el Kernel La memoria en la dirección incorrecta puede (y a menudo ocurrirá) resultar en una Bug Check
( Blue Screen of Death
). Una falla de la máquina no es aceptable tanto en escenarios de pruebas de penetración normales como de equipo rojo, ya que una máquina que falla es muy visible para los defensores y perderá cualquier credencial que todavía estuviera en la memoria en el momento del ataque.
Para recuperar compensaciones para cada versión específica de Windows, se implementan dos enfoques.
Las compensaciones necesarias ntoskrnl.exe
y wdigest.dll
se pueden extraer utilizando el script de Python ExtractOffsets.py
proporcionado, que se basa en radare2
y r2pipe
para descargar y analizar símbolos de archivos PDB, y extrae las compensaciones necesarias de ellos. Luego, las compensaciones se almacenan en archivos CSV para que EDRSandblast las utilice posteriormente.
Para admitir una amplia gama de compilaciones de Windows listas para usar, Winbindex hace referencia a muchas versiones de los archivos binarios ntoskrnl.exe
y wdigest.dll
, y ExtractOffsets.py
. Esto permite extraer compensaciones de casi todos los archivos que alguna vez se publicaron en los paquetes de actualización de Windows (hasta la fecha, hay más de 450 versiones ntoskrnl.exe
y más de 30 wdigest.dll
disponibles y precalculadas).
Se ha implementado una opción adicional en EDRSandBlast
para permitir que el programa descargue los archivos .pdb
necesarios desde Microsoft Symbol Server, extraiga las compensaciones requeridas e incluso actualice los archivos .csv
correspondientes, si están presentes.
El uso de la opción --internet
simplifica mucho la ejecución de la herramienta, al tiempo que introduce un riesgo adicional de OpSec, ya que se descarga un archivo .pdb
y se coloca en el disco durante el proceso. Esto es requerido por las funciones dbghelp.dll
utilizadas para analizar la base de datos de símbolos; sin embargo, es posible que en el futuro se implemente un análisis completo de PDB en memoria para eliminar este requisito y reducir el espacio que ocupa la herramienta.
El controlador RTCore64.sys
vulnerable se puede recuperar en:
http://download-eu2.guru3d.com/afterburner/%5BGuru3D.com%5D-MSIAfterburnerSetup462Beta2.zip