Vectored Overloading PE Injection

Tip

Impara e pratica il hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica il hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Impara e pratica il hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Supporta HackTricks

Tip

Cerchi tecniche di Windows 11 LFH heap shaping e escape per VMware Workstation PVSCSI (vmware-vmx)?

{{#ref}} vmware-workstation-pvscsi-lfh-escape.md {{#endref}}

Panoramica della tecnica

Vectored Overloading è una Windows PE injection primitive che fonde il classico Module Overloading con Vectored Exception Handlers (VEHs) e hardware breakpoints. Invece di patchare LoadLibrary o scrivere un proprio loader, l’avversario:

  1. Crea una sezione SEC_IMAGE appoggiata da una DLL legittima (es. wmp.dll).
  2. Sovrascrive la view mappata con un PE maligno completamente relocato ma mantiene l’oggetto section puntato all’immagine benevola su disco.
  3. Registra un VEH e programma i debug registers in modo che ogni chiamata a NtOpenSection, NtMapViewOfSection e opzionalmente NtClose sollevi un breakpoint in user-mode.
  4. Chiama LoadLibrary("amsi.dll") (o qualsiasi altro target benigno). Quando il loader di Windows invoca quei syscall, il VEH salta la transizione al kernel e ritorna gli handle e gli indirizzi base dell’immagine malevola preparata.

Poiché il loader crede di aver mappato la DLL richiesta, gli strumenti che guardano solo al file di backing della section vedono wmp.dll anche se la memoria contiene il payload dell’attaccante. Nel frattempo, gli imports/TLS callbacks sono ancora risolti dal loader genuino, riducendo significativamente la quantità di logica di parsing PE custom che l’avversario deve mantenere.

Fase 1 – Costruire la sezione camuffata

  1. Create and map a section for the decoy DLL
NtCreateSection(&DecoySection, SECTION_ALL_ACCESS, NULL,
0, PAGE_READWRITE, SEC_IMAGE, L"\??\C:\\Windows\\System32\\wmp.dll");
NtMapViewOfSection(DecoySection, GetCurrentProcess(), &DecoyView, 0, 0,
NULL, &DecoySize, ViewShare, 0, PAGE_READWRITE);
  1. Copiare il PE maligno in quella view sezione per sezione, rispettando SizeOfRawData/VirtualSize e aggiornando le protezioni in seguito (PAGE_EXECUTE_READ, PAGE_READWRITE, ecc.).
  2. Applicare le relocations e risolvere gli imports esattamente come farebbe un reflective loader. Poiché la view è già mappata come SEC_IMAGE, gli allineamenti di section e le guard pages corrispondono a ciò che il loader di Windows si aspetta in seguito.
  3. Normalizzare l’header PE:
  • Se il payload è un EXE, impostare IMAGE_FILE_HEADER.Characteristics |= IMAGE_FILE_DLL e azzerare l’entry point per impedire a LdrpCallTlsInitializers di saltare in stub specifici per EXE.
  • I payload DLL possono mantenere gli header invariati.

A questo punto il processo possiede una view con permessi RWX il cui oggetto di backing è ancora wmp.dll, ma i byte in memoria sono controllati dall’attaccante.

Fase 2 – Dirottare il loader con i VEH

  1. Registrare un VEH e armare gli hardware breakpoints: programmare Dr0 (o un altro debug register) con l’indirizzo di ntdll!NtOpenSection e impostare DR7 in modo che ogni esecuzione sollevi STATUS_SINGLE_STEP. Ripetere poi per NtMapViewOfSection e opzionalmente NtClose.
  2. Triggerare il caricamento della DLL con LoadLibrary("amsi.dll"). LdrLoadDll chiamerà infine NtOpenSection per ottenere il reale section handle.
  3. VEH hook per NtOpenSection:
  • Localizzare lo slot nello stack per l’argomento [out] PHANDLE SectionHandle.
  • Scrivere in quello slot l’handle DecoySection creato precedentemente.
  • Avanzare RIP/EIP fino all’istruzione ret così il kernel non viene mai chiamato.
  • Ri-armare l’hardware breakpoint per monitorare poi NtMapViewOfSection.
  1. VEH hook per NtMapViewOfSection:
  • Sovrascrivere l’[out] PVOID *BaseAddress (e gli output di size/protection) con l’indirizzo della view malevola già mappata.
  • Saltare il corpo del syscall come prima.
  1. (Opzionale) VEH hook per NtClose verifica che l’handle fittizio della section venga pulito, prevenendo resource leaks e fornendo un controllo di sanity finale.

Poiché i syscall non vengono eseguiti, i callback del kernel (ETWti, minifilter, ecc.) non osservano gli eventi sospetti di NtOpenSection/NtMapViewOfSection, abbassando drasticamente la telemetry. Dal punto di vista del loader tutto è andato a buon fine e amsi.dll è in memoria, quindi procede con la risoluzione di imports/TLS contro la view dell’attaccante.

Note sull’implementazione PoC (2025)

Il PoC pubblico mostra alcuni dettagli pratici facili da perdere quando si re-implementa la tecnica:

  • HWBPs are per-thread. Il PoC imposta CONTEXT_DEBUG_REGISTERS sul thread corrente prima di chiamare LoadLibrary, quindi il VEH deve girare sullo stesso thread che innesca il loader.
  • Syscall emulation: il VEH imposta RAX = 0 e avanza RIP fino al ret dentro lo stub di ntdll (scansiona per 0xC3) così la transizione al kernel non avviene mai, poi riprende con NtContinue.
  • Output parameters: per NtMapViewOfSection, il VEH sovrascrive gli output BaseAddress, ViewSize e Win32Protect così il loader crede che la mappatura sia riuscita e continua con imports/TLS usando la view dell’attaccante.

Minimal HWBP setup used by the PoC (x64):

CONTEXT ctx = {0};
ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
GetThreadContext(GetCurrentThread(), &ctx);
ctx.Dr0 = (DWORD64)NtOpenSection;
ctx.Dr7 = 1;
SetThreadContext(GetCurrentThread(), &ctx);
AddVectoredExceptionHandler(1, VehHandler);

Variante stealth

Ricerche recenti su VEH evidenziano che i handler possono essere registrati manualmente manipolando la VEH list invece di chiamare AddVectoredExceptionHandler, riducendo così la dipendenza dalle user-mode APIs che potrebbero essere monitorate o hooked. Questo non è richiesto per Vectored Overloading ma può essere combinato con esso per ridurre l’attività API osservabile.

Fase 3 – Eseguire il payload

  • EXE payload: L’injector salta semplicemente all’original entry point una volta completate le relocations. Quando il loader pensa che chiamerà DllMain, il codice custom esegue invece l’entry in stile EXE.
  • DLL payload / Node.js addon: Risolve e chiama l’export previsto (Kidkadi espone una funzione nominata a JavaScript). Poiché il modulo è già registrato in LdrpModuleBaseAddressIndex, le ricerche successive lo vedono come la DLL benigna.

Quando combinato con un Node.js native addon (.node file), tutta la parte pesante degli internals di Windows rimane al di fuori dello strato JavaScript, aiutando il threat actor a distribuire lo stesso loader con molti diversi wrapper Node offuscati.

References

Tip

Impara e pratica il hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica il hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Impara e pratica il hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Supporta HackTricks