Construyendo un Loader Evasivo: Integrando Havoc, Donut e Inyección Early Bird

¡Ey! Si estás buceando en esto, seguro que te flipa el mundo de la evasión de defensas y las técnicas que parecen de película pero que funcionan en el mundo real. En este post, te desgloso con todo lujo de detalles cómo armar un loader que coge un implante de Havoc, lo pasa por Donut para shellcode, lo cifra con AES-256 para que no lo huelan los scanners, y lo inyecta en un proceso inocente usando syscalls. Todo con un enfoque en ser sigiloso, saltando hooks de EDRs y manteniendo el C2 vivo sin dramas.

No es la octava maravilla, pero es un combo sólido para red teaming o labs educativos. Como de costumbre: úsalo con cabeza, nada de locuras ilegales, ¿vale? Vamos al grano, que si no me lío a charlar.

Objetivo y Por Qué esta chulo

La idea es crear un implante que se conecte a un C2 sin que los AVs o EDRs lo pillen. Havoc es un framework open-source chulísimo, tipo Cobalt Strike pero gratis y con menos mierdas (e eleguido havoc para esta documentacion por las llamadas a sys, que sinceramente las hace muy bien, peromi corazon sigue siendo de sliver) . Configuramos un listener HTTPS para que el tráfico vaya cifrado y no levante sospechas.

Generamos un .exe, lo convertimos en shellcode con Donut (position-independent, para inyecciones perfectas), lo ciframos para ofuscar, y lo metemos en un loader en C que usa Early Bird APC Queue: encola una llamada asíncrona en el hilo de un proceso suspendido como Notepad.exe. Syscalls directas de ntdll.dll para evitar hooks en user-mode – los EDRs como CrowdStrike se vuelven idiotas con eso.

Al final, tienes un binario que pasa desapercibido en el windows defender y otros AVs, inyecta el código en algo legítimo y mantiene la conexión. En plan: ejecutas el loader, Notepad se despierta con el implante dentro, y tú controlas desde el C2. Pura magia evasiva!!!

Configuración del Framework Havoc

Empezamos por Havoc, que tiene una interfaz guay para tunear todo. Montamos un listener HTTPS , puerto 443, no me e querido complicar por el momento en este apartado del listener.

Para el payload, agente "Demon", x64, Windows Exe. Config específica:

  • Sleep: 45 segundos – intervalo para beacons, menos ruido.

  • Jitter: 35% – variación random en sleeps, para no ser predecible.

  • Indirect Syscall: On – resuelve syscalls de forma indirecta, bye hooks.

  • Stack Duplication: On – duplica stack en ops sensibles, contra análisis de memoria.

  • Sleep Technique: Zilean – ofusca sleeps con loops que parecen código normal.

  • Sleep Jmp Gadget: None – sin extras, keep it simple.

  • Proxy Loading: None (LdrLoadDll) – carga estándar, sin proxies.

  • Amsi/Etwp Patch: Hardware breakpoints – parchea AMSI y ETW con breakpoints hardware, más stealth que patches directos.

  • Injection:

    • Alloc: Native/Syscall – memoria via syscalls.

    • Execute: Native/Syscall – ejecución syscall-based.

    • Spawn64: C:\Windows\System32\RuntimeBroker.exe – para x64 spawning.

    • Spawn32: C:\Windows\SysWOW64\RuntimeBroker.exe – para x86.

Havoc genera el .exe con estos tweaks, listo para evasión. Pero crudo lo cazarían, así que siguiente paso.

Transformación a Shellcode con Donut

Cogemos el .exe y lo pasamos por Donut, que lo convierte en shellcode PIC (position-independent code). Donut mete un stub que resuelve APIs en runtime, maneja relocs y arranca el entrypoint sin fijarse en direcciones.

Lo corremos con flags para binario raw, sin compresión. Sale un .bin autónomo, perfecto para inyectar, pero aún detectable – hora de cifrar.

Añadir que donut no es la unica que puede hacer esto. Yo la elegui porque era la que mas a mano tenia

Cifrado con AES-256 y Generación de Header

Aquí entramos en lo técnico: usamos un script en Python para cifrar el shellcode con AES-256 en modo CBC (Cipher Block Chaining), que es un cifrado simétrico por bloques con una clave de 256 bits (32 bytes), ofreciendo un nivel de seguridad alto contra ataques de fuerza bruta gracias a su espacio de claves de 2^256 posibilidades. El modo CBC introduce encadenamiento: cada bloque plaintext se XORea con el ciphertext del bloque anterior antes de cifrar, lo que proporciona difusión y confusión, haciendo que patrones repetitivos en el input se ofusquen completamente en el output.

Primero, generamos una clave aleatoria de 32 bytes usando bytes random seguros (cryptográficamente fuertes, no pseudo-random), y un Initialization Vector (IV) de 16 bytes, que actúa como nonce para inicializar el chaining y prevenir que inputs idénticos produzcan outputs idénticos, evitando así ataques como known-plaintext o replay.

Leemos el archivo .bin como bytes crudos (plaintext), aplicamos padding PKCS7 para ajustar la longitud a múltiplos de 16 bytes (tamaño de bloque de AES): esto implica agregar bytes de valor igual al número de bytes de padding necesarios (por ejemplo, si faltan 5 bytes, agregamos cinco 0x05), asegurando que el decrypt pueda removerlo correctamente sin corrupción de datos.

Creamos una instancia del cipher AES con la clave, modo CBC y IV, y ciframos el plaintext padded, resultando en un ciphertext del mismo tamaño o múltiplo de 16. Mostramos longitudes para verificar: original vs. cifrado (con padding).

Luego, convertimos la clave, IV y ciphertext a formato de array C: una cadena de bytes en hex como {0xAB, 0xCD, ...}, usando una función que itera byte a byte y formatea con "0x%02x". Esto se escribe en un archivo header .h con pragmas para inclusión única (#pragma once), definiendo constantes unsigned char[] para clave, IV y payload, más un size_t para la longitud del payload cifrado.

Ventajas técnicas: el cifrado hace que el shellcode parezca ruido random, evadiendo firmas estáticas de AVs basadas en patrones o hashes; la aleatoriedad de clave/IV hace cada build único; integrar en .h evita lecturas de disco runtime, reduciendo IOCs (Indicators of Compromise); PKCS7 asegura integridad post-decrypt.

Implementación del Loader en C: Inyección Remota vía Syscalls

Ahora el meollo técnico: el loader en C incluye el header .h con el payload cifrado, y maneja descifrado e inyección remota usando syscalls directas para bypass hooks de user-mode en EDRs, que suelen interceptar APIs de alto nivel como VirtualAllocEx.

Primero, incluimos headers de Windows para tipos como NTSTATUS, y definimos typedefs para las syscalls de ntdll.dll: NtAllocateVirtualMemory (asigna mem remota), NtWriteVirtualMemory (escribe en mem remota), NtProtectVirtualMemory (cambia protecciones), NtQueueApcThread (encola APC), NtResumeThread (reanuda hilo). Estas se resuelven en runtime con GetModuleHandleA("ntdll.dll") y GetProcAddress, evitando imports estáticos que delaten el binario en análisis PE.

Descifrado: inicializamos un contexto AES con clave e IV (usando una lib como aes.h para CBC decrypt), y decryptamos el payload in-place o en buffer temporal. CBC decrypt implica XORear cada bloque decrypt con el anterior ciphertext, removiendo padding post-decrypt para recuperar el shellcode original.

Creamos proceso objetivo: usamos CreateProcessA para lanzar Notepad.exe (path fijo C:\Windows\System32\notepad.exe, firmado por MS para confianza en Defender) con flag CREATE_SUSPENDED, que pausa el hilo principal antes de ejecutar código usuario. Esto da handles al proceso (pi.hProcess) y hilo (pi.hThread), con PID para logging.

Inyección paso a paso:

  1. Allocación remota: Llamamos NtAllocateVirtualMemory con handle proceso, base address NULL (deja que kernel elija), tamaño del shellcode (size_payload), flags MEM_COMMIT | MEM_RESERVE (reserva y commitea páginas), protecciones iniciales PAGE_READWRITE (RW para escritura segura). Verificamos NT_SUCCESS(status) para manejar errores.

  2. Escritura remota: NtWriteVirtualMemory copia el shellcode decrypt de buffer local a la dirección remota allocada, especificando bytes a escribir y chequeando bytes escritos.

  3. Cambio de protecciones: NtProtectVirtualMemory ajusta la región a PAGE_EXECUTE_READ (RX: ejecutable, legible, no escribible), guardando old_protect. Esto minimiza ventana de ataque (no RWX, que alerta heurísticas).

  4. Encolado APC: NtQueueApcThread encola una Asynchronous Procedure Call en el hilo suspendido, con ApcRoutine apuntando a la dirección remota del shellcode, y argumentos NULL. APCs se ejecutan en alertable state, perfecto para Early Bird: corre antes de que el hilo reanude código normal.

  5. Reanudación: NtResumeThread despierta el hilo, ejecutando APC inmediatamente, inyectando el implante en contexto legítimo.

Cleanup: cerramos handles con CloseHandle para no dejar leaks o traces en proceso tree. Si cualquier syscall falla (NTSTATUS < 0), terminamos el proceso hijo con TerminateProcess para evitar estados zombies o detecciones parciales.

Técnicamente, syscalls evaden hooks porque saltan wrappers de kernel32/win32u, yendo directo a kernel via ntdll; Early Bird aprovecha el startup sequence de procesos Windows, donde APCs encoladas pre-resume se priorizan; usar Notepad reduce sospechas por reputation scoring en AVs.

Compilación y Estrategias de Evasión

Compila con MinGW: -O2 optimiza, -s strippea, -static independiente, -w calla warnings. Nómbralo como update, pasa inadvertido.

Evasión: cifrado mata firmas, syscalls bypass hooks, inyección en legítimo engaña heurísticas. En tests, 0 detecciones en AV de windows defender.

Cómo Mejorarlo para EDRs

Vale, si quieres elevar esto a nivel pro y plantarle cara a EDRs duros como CrowdStrike o SentinelOne, el loader actual con syscalls y AES está bien si nos gusta lo mediocre y si confundimos el concepto de AVs y EDRs, pero contra scans de memoria y telemetría avanzada, necesitas más capas. Herramientas como Freeze intentan automatizarlo, pero hazlo manual para control total. Olvida loaders simples y ve a por técnicas top-tier. Te detallo:

  • Deja CreateThread/CreateRemoteThread: Opta por Pool Party o Timer Queue Injection CreateRemoteThread es un clásico, pero EDRs lo monitorizan como locos (telemetría en kernel). Pool Party usa worker threads del thread pool de Windows (via TpAllocWork y TpPostWork) para ejecutar código sin crear hilos nuevos – parece actividad normal del sistema. Timer Queue Injection encola timers (NtSetTimer) que corren callbacks en hilos existentes, súper stealth. Ambas evitan eventos obvios de creación de threads, reduciendo alertas. Implementa con syscalls como NtCreateTimer o TpAllocTimer para más sigilo.

  • Implementa Stack Spoofing: El Rey de la Evasión Si tu shellcode duerme (como con Ekko, que ofusca sleeps rotando claves), pero sin spoofear la pila, un scan de memoria de CrowdStrike te caza: ven el call stack apuntando a tu código malicioso. Stack spoofing falsifica el stack frame, haciendo que parezca que el hilo duerme en ntdll o kernel32 legítimo. Separa niños de los adultos porque requiere manipular el stack pointer (RSP) y frames manualmente. Integra libs como Unwinder (para unwind el stack real y spoofearlo) o mira AceLdr (un loader open-source que lo hace con syscalls). Básico: guarda stack real, crea uno fake con returns a APIs benignas, duerme, restaura. Contra EDRs con memory hunting, es oro.

  • Reflective Loaders Personalizados (UDRL): Adiós Stub Genérico de Donut El stub de Donut es guay, pero genérico – firmas lo pillan. Escribe tu propio User-Mode Reflective DLL Loader (UDRL): carga DLLs en memoria sin disco, resuelve imports dinámicamente, y después borra headers PE (Header Stomping: sobreescribe la PE header con zeros o ruido). Así, scans de memoria no ven estructuras PE sospechosas. Aprende de sRDI o custom: parsea PE en mem, reloca, resuelve IAT con hashing (ver abajo), ejecuta entrypoint, stomp headers. Más stealth, menos detección estática/dinámica.

Tu setup ya tiene syscalls (bypass user-mode hooks) y AES (ofusca payload). Para batir Freeze (que intenta stack spoof y más), implementa manual

(utilizo freeze de ejemplo porque en su momento fue muy bueno):

A. API Hashing (Ocultar Imports) Ahora, en PEStudio o CFF Explorer, ves imports claras: kernel32 -> VirtualAlloc, etc. – bandera roja. Solución: no uses strings como "NtAllocateVirtualMemory" en GetProcAddress, que dejan textos legibles. Calcula hash (e.g., djb2 o ROT13) del nombre: hash = 0; for char in name: hash = ((hash << 5) + hash) + char. Luego, itera exports de ntdll en mem (parse PEB -> LDR -> modules), hashea cada export y compara. Encuentras la función sin strings – binario limpio, no strings sospechosas en .data/.rdata. Añade sal al hash para uniqueness.

B. Stack Spoofing (Detallado) Como arriba, pero profundizo: en sleeps, EDRs como CrowdStrike usan APIs como StackWalk64 para trazar calls. Spoof: 1) Usa RtlCaptureContext para capturar contexto actual. 2) Modifica RSP a un stack fake allocado, con frames pointing a ntdll!RtlUserThreadStart o similar (parece hilo normal). 3) Duerme (NtDelayExecution). 4) Restaura contexto real. Libs como Unwinder ayudan con unwind/rewind seguro, evitando crashes. Estudia AceLdr: usa syscalls para todo, integra en tu loader para sleeps del implante. Contra ETW/AMSI, combínalo con patches hardware ya en Havoc.

Con esto, tu loader pasa de bueno a élite – prueba en labs con EDRs reales y ajusta. ¡Pero ojo, no abuses!

Video con pruebas

Perdonarme que lo tenga que subir asi, pero gitbook no deja intrustar videos de normal

Conclusiones

Y listo, un loader que evade con estilo, perfecto para aprender o simular amenazas. Si lo mejoras más, te comes a los EDRs.

espero que os guste y QUIERO UN PANDAAAAAA

Última actualización