Vectored Overloading PE Injection

Tip

Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE) Apprenez et pratiquez le hacking Azure : HackTricks Training Azure Red Team Expert (AzRTE)

Soutenir HackTricks

Tip

Vous cherchez des techniques d’évasion Windows 11 LFH heap shaping et VMware Workstation PVSCSI (vmware-vmx) ?

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

Aperçu de la technique

Vectored Overloading est une primitive d’injection Windows PE qui fusionne le classique Module Overloading avec les Vectored Exception Handlers (VEHs) et les hardware breakpoints. Au lieu de patcher LoadLibrary ou d’écrire son propre loader, l’adversaire :

  1. Crée une section SEC_IMAGE adossée à une DLL légitime (par ex., wmp.dll).
  2. Écrase la vue mappée avec un PE malveillant entièrement relocalisé mais laisse l’objet section pointant vers l’image bénigne sur disque.
  3. Enregistre un VEH et programme les registres de debug de sorte que chaque appel à NtOpenSection, NtMapViewOfSection, et optionnellement NtClose déclenche un breakpoint en mode utilisateur.
  4. Appelle LoadLibrary("amsi.dll") (ou toute autre cible bénigne). Lorsque le loader Windows invoque ces syscalls, le VEH évite la transition noyau et retourne les handles et adresses de base de l’image malveillante préparée.

Parce que le loader croit toujours avoir mappé la DLL demandée, les outils qui ne regardent que les fichiers de backing de section voient wmp.dll même si la mémoire contient maintenant la charge utile de l’attaquant. Pendant ce temps, les imports/callbacks TLS sont toujours résolus par le loader authentique, réduisant significativement la quantité de logique personnalisée d’analyse PE que l’adversaire doit maintenir.

Étape 1 – Construire la section déguisée

  1. Créer et mapper une section pour la DLL leurre
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. Copier le PE malveillant dans cette vue section par section, en respectant SizeOfRawData/VirtualSize et en mettant à jour les protections ensuite (PAGE_EXECUTE_READ, PAGE_READWRITE, etc.).
  2. Appliquer les relocations et résoudre les imports exactement comme le ferait un reflective loader. Parce que la vue est déjà mappée comme SEC_IMAGE, les alignements de section et les guard pages correspondent à ce que le loader Windows attend ensuite.
  3. Normaliser l’en-tête PE :
  • Si le payload est un EXE, définir IMAGE_FILE_HEADER.Characteristics |= IMAGE_FILE_DLL et mettre à zéro l’entry point pour empêcher LdrpCallTlsInitializers de sauter dans des stubs spécifiques aux EXE.
  • Les payloads DLL peuvent laisser leurs en-têtes inchangés.

À ce stade le processus possède une vue RWX dont l’objet backing est toujours wmp.dll, pourtant les octets en mémoire sont contrôlés par l’attaquant.

Étape 2 – Détourner le loader avec des VEHs

  1. Enregistrer un VEH et armer des hardware breakpoints : programmer Dr0 (ou un autre registre de debug) avec l’adresse de ntdll!NtOpenSection et définir DR7 pour que chaque exécution soulève STATUS_SINGLE_STEP. Répéter ensuite pour NtMapViewOfSection et éventuellement NtClose.
  2. Déclencher le chargement de la DLL avec LoadLibrary("amsi.dll"). LdrLoadDll appellera finalement NtOpenSection pour obtenir le handle de section réel.
  3. Hook VEH pour NtOpenSection :
  • Localiser l’emplacement sur la pile pour l’argument [out] PHANDLE SectionHandle.
  • Écrire le handle DecoySection précédemment créé dans cet emplacement.
  • Avancer RIP/EIP jusqu’à l’instruction ret pour que le kernel ne soit jamais appelé.
  • Réarmer le hardware breakpoint pour surveiller NtMapViewOfSection ensuite.
  1. Hook VEH pour NtMapViewOfSection :
  • Écraser le [out] PVOID *BaseAddress (et les sorties taille/protection) avec l’adresse de la vue malveillante déjà mappée.
  • Sauter le corps du syscall comme précédemment.
  1. (Optionnel) Hook VEH pour NtClose vérifie que le faux handle de section est nettoyé, évitant les fuites de ressources et fournissant une dernière vérification de cohérence.

Parce que les syscalls ne sont jamais exécutés, les callbacks noyau (ETWti, minifilter, etc.) n’observent pas les événements suspects NtOpenSection/NtMapViewOfSection, réduisant drastiquement la télémétrie. Du point de vue du loader tout a réussi et amsi.dll est en mémoire, il procède donc à la résolution des imports/TLS contre les octets de l’attaquant.

Notes d’implémentation du PoC (2025)

Le PoC public montre quelques détails pratiques faciles à manquer lors de la ré-implémentation de la technique :

  • HWBPs are per-thread. Le PoC définit CONTEXT_DEBUG_REGISTERS sur le thread courant avant d’appeler LoadLibrary, donc le VEH doit s’exécuter sur le même thread qui déclenche le loader.
  • Syscall emulation: le VEH met RAX = 0 et avance RIP jusqu’au ret à l’intérieur du stub ntdll (il scanne pour 0xC3) de sorte que la transition noyau n’ait jamais lieu, puis reprend avec NtContinue.
  • Output parameters: pour NtMapViewOfSection, le VEH écrase les sorties retournées BaseAddress, ViewSize, et Win32Protect pour que le loader croie que le mapping a réussi et continue la résolution des imports/TLS en utilisant la vue de l’attaquant.

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);

Variation furtive

Des recherches récentes sur VEH montrent que des handlers peuvent être enregistrés en manipulant manuellement la liste VEH au lieu d’appeler AddVectoredExceptionHandler, ce qui réduit la dépendance aux user-mode APIs qui peuvent être surveillées ou hookées. Cela n’est pas requis pour Vectored Overloading mais peut être combiné avec celui-ci pour réduire l’activité API observable.

Étape 3 – Exécuter le payload

  • EXE payload : l’injecteur saute simplement vers le point d’entrée original une fois les relocations terminées. Lorsque le loader pense qu’il va appeler DllMain, le code personnalisé exécute à la place l’entrée de type EXE.
  • DLL payload / Node.js addon : résoudre et appeler l’export ciblé (Kidkadi expose une fonction nommée à JavaScript). Parce que le module est déjà enregistré dans LdrpModuleBaseAddressIndex, les recherches ultérieures le voient comme la DLL bénigne.

Lorsqu’il est combiné avec un Node.js native addon (.node file), toute la partie lourde des Windows-internals reste en dehors de la couche JavaScript, ce qui permet à l’acteur de menace de déployer le même loader avec de nombreux wrappers Node obfusqués différents.

References

Tip

Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE) Apprenez et pratiquez le hacking Azure : HackTricks Training Azure Red Team Expert (AzRTE)

Soutenir HackTricks