Best EDR Of The Market: herramienta para aprender a evadir AV/EDR

Best EDR Of The Market (en adelante BEOTM) de @Xacone (Yazid) es un EDR de código abierto diseñado para servir como campo de pruebas para comprender y evitar algunos de los mecanismos de detección empleados por muchos EDR conocidos.

A continuación veremos descripción general muy rápida de las técnicas que implementa BEOTM sin entrar en detalle sobre los métodos de evasión.  

DLL hooking

 
BEOTM realiza la inyección de DLL en múltiples niveles interceptando funciones sensibles como las utilizadas para la asignación de memoria, creación/manipulación de procesos o subprocesos, cambiando los derechos de acceso a los grupos de memoria, etc. El hooking se logra inyectando la DLL en el proceso de destino.
Una vez inyectada, la DLL redirigirá las llamadas de funciones hookeadas a sus propias rutinas internas para inspeccionar su contenido y luego decidir si continúa o no con la llamada invocando la rutina original.

Eso sí, BEOTM se limita (por ahora) a mostrar un mensaje a través de la salida estándar (para hooks de NTDLL) o a mostrar un cuadro de mensaje (para hooks Kernelbase/Kernel32) alertando que la rutina ha sido interceptada.

NT-Level hooking VS Reflective DLL Loader: 


Kernel32-Level hooking VS proceso legítimo:

Rutinas/funciones hookeadas

Las funciones que el EDR hookea a través de las DLL inyectadas se describen en el archivo "TrigerringFunctions.json". La versión inicial de BEOTM incluye archivos DLL precompilados, pero se pueden compilar otros archivos DLL con las funciones que queramos interceptar.

Las DLL inyectables de BEOTM se basan en Microsoft Detours como interfaz para la interceptación de llamadas API.

A continuación vemos una descripción detallada de lo que sucede cuando la DLL intercepta NtAllocateVirtualMemory:

Threads Call Stack Monitoring

Este método implica la monitorización continua del registro de instrucciones RIP de cada hilo. Cuando el registro RIP apunta a la dirección de una rutina/función exportada desde cualquier DLL o a la dirección de un área contenida en la implementación de esta exportación, y el usuario ha especificado en el archivo TrigerringFunctions.json que desea monitorizar esa rutina/función, el hilo se pausa y su pila de llamadas se desenrola. Se analizan todos los parámetros de todas las rutinas que apila. Si una secuencia de bytes en un parámetro coincide con un patrón predefinido en el archivo YaroRules.json, el proceso se detiene y se genera una alerta.

A diferencia del hooking DLL y el hooking IAT, de hecho hay una respuesta con este método: podemos especificar en el archivo TrigerringFunctions.json qué funciones queremos monitorizar en el nivel de la pila de llamadas del subproceso y podemos definir los patrones que deseamos identificar en YaroRules.json.

Threads Call Stack Monitoring VS NT-Level XOR-Encrypted Shellcode Loader:

Threads Call Stack Monitoring VS Reflective DLL Loader: 

Echemos un vistazo a bajo nivel que sucede, aquí hay un programa simple que asigna un pool de memoria virtual de 4096 bytes a través de VirtualAlloc:


int main() {
    size_t memSize = 4096; // 4 Ko

    LPVOID pMemory = VirtualAlloc(nullptr, memSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
    return 0;
}
los valores de los cuatro parámetros se transmiten a los registros r8, r9, rcx y rdx que luego se pasarán a la pila:

KERNELBASE!VirtualAlloc+0x22:
00007ffb`9bdd1402 4183e0c0        and     r8d,0FFFFFFC0h
00007ffb`9bdd1406 44894c2428      mov     dword ptr [rsp+28h],r9d
00007ffb`9bdd140b 4489442420      mov     dword ptr [rsp+20h],r8d
00007ffb`9bdd1410 4c8d4c2448      lea     r9,[rsp+48h]
00007ffb`9bdd1415 4533c0          xor     r8d,r8d
00007ffb`9bdd1418 488d542440      lea     rdx,[rsp+40h]
00007ffb`9bdd141d 498d48ff        lea     rcx,[r8-1]
00007ffb`9bdd1421 48ff1500ea1f00  call    qword ptr [KERNELBASE!_imp_NtAllocateVirtualMemory (00007ffb`9bfcfe28)]
00007ffb`9bdd1428 0f1f440000      nop     dword ptr [rax+rax]
00007ffb`9bdd142d 85c0            test    eax,eax
00007ffb`9bdd142f 780b            js      KERNELBASE!VirtualAlloc+0x5c (00007ffb`9bdd143c)

Estos mismos parámetros se pasarán al NtAllocateVirtualMemory subyacente:

Si establecemos un punto de interrupción en ntdll!NtAllocateVirtualMemory en el loader del shellcode y desenrolamos la pila de llamadas cuando se alcanza el punto de interrupción, veremos que se pasa una dirección como parámetro a la rutina, correspondiente a un puntero al shellcode dexoreado.

Aquí hay un diagrama condicional global de cómo funciona la monitorización de la pila de llamadas de subprocesos en BOTM:

Import Address Table (IAT) Hooking

Este método opera en el nivel de la tabla de direcciones de importación (IAT) del proceso de destino al sobrescribir las direcciones de importaciones legítimas con las direcciones de exportaciones de una DLL inyectada. Si bien puede parecer similar al hooking de una DLL debido a la inyección de una DLL, no lo es en absoluto, ya que este método no implica una instrucción 'jmp' para una función de trampolín.

Se muestra un cuadro de mensaje para cada función interceptada.

IAT-hooking VS Reflective DLL Loader: 


Algunos podrían (quizás) preguntarse cómo se preservan los registros con este método de interceptación para ser analizados posteriormente, incluso con la adición de un cuadro de mensaje o cualquier instrucción que potencialmente pueda corromper los valores de los registros y distorsionar los valores de los parámetros. Bueno, todo lo que se necesita es activar IDA para obtener la respuesta: este método no impide la preservación y copia de seguridad de los valores de los registros r8, r9, rcx y rdx en otros registros (rbx, rdi, rsi).

SSN Crushing

Bueno, eso es puramente imaginativo y nada realista, simplemente consiste en sobrescribir/anular el valor de Syscall / SSN (Número de servicio del sistema) en el nivel NT con 0x21.

Esto hará que el proceso deje de funcionar en la mayoría de los casos si tocas rutinas importantes, ya sea porque el programa ha detectado un error de integridad en la suma de comprobación ntdll que ha cargado en la memoria o porque simplemente se ha dañado la llamada al sistema.

Entonces, ¿cuál es el punto de eso? Simplemente por intentar, desde el punto de vista de un atacante, revertir el valor de la llamada al sistema y anularlo con su valor real que habrías obtenido por cualquier medio

Reglas Yaro

El archivo YaroRules.json no contiene más que los patrones que desea identificar. Puedes divertirte buscando patrones interesantes comunes a muchos shellcodes/payloads útiles, como en este ejemplo que identifica patrones relacionados con el acceso al PEB y el recorrido de entradas LDR para un shellcode TCP inverso generado por metasploit.

También se puede agregar ciertos patrones de artefactos maliciosos conocidos (por ejemplo, Mimikatz).  

YaroRules.json contiene 5 patrones predeterminados correspondientes al patrón de acceso a las entradas PEB y PEB_LDR_DATA para un shellcode inverso_tcp generado por Metasploit. También hay otros 3 patrones correspondientes a áreas en el encabezado de DOS de un objeto en formato PE.


{
    "Patterns": [
        "d2 65 48 8b 52 60 48 8b 52 18 48 8b 52 20 48 8b 72 50 48",
        "49 be 77 73 32 5f 33 32 00 00",
        "4d 5a",
        "4D 5A 90 00 03 00 00 00 04 00 00 00 FF FF 00 00 B8 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00",
        "00 F0 00 00 00 0E 1F BA 0E 00 B4 09 CD 21 B8 01 4C CD 21 54 68 69 73 20 70 72 6F 67 72 61 6D 20 63 61 6E 6E 6F 74 20 62 65 20 72 75 6E 20 69 6E 20 44 4F 53 20 6D 6F 64 65 2E"
    ]
}

Fuente: https://xacone.github.io/BestEdrOfTheMarket.html

Proyecto: https://github.com/Xacone/BestEdrOfTheMarket


Comentarios