Vectored Overloading PE Injection

Tip

Ucz się i ćwicz Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Ucz się i ćwicz Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Wsparcie dla HackTricks

Tip

Szukasz technik kształtowania heap LFH w Windows 11 oraz ucieczki z VMware Workstation PVSCSI (vmware-vmx)?

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

Technique overview

Vectored Overloading to podstawowy prymityw wstrzykiwania PE dla Windows, który łączy klasyczny Module Overloading z Vectored Exception Handlers (VEHs) oraz hardware breakpoints. Zamiast patchować LoadLibrary lub pisać własny loader, przeciwnik:

  1. Tworzy sekcję SEC_IMAGE powiązaną z legalnym DLL (np. wmp.dll).
  2. Nadpisuje mapped view kompletnie zrelokowanym złośliwym PE, ale pozostawia obiekt sekcji wskazujący na benign obraz na dysku.
  3. Rejestruje VEH i programuje rejestry debugowania tak, żeby każde wywołanie NtOpenSection, NtMapViewOfSection, i opcjonalnie NtClose generowało user-mode breakpoint.
  4. Wywołuje LoadLibrary("amsi.dll") (lub inny benign cel). Kiedy loader Windows wywołuje te syscallsy, VEH pomija przejście do kernela i zwraca uchwyty oraz base address przygotowanego złośliwego obrazu.

Ponieważ loader nadal wierzy, że zmapował żądany DLL, narzędzia które patrzą tylko na pliki backing section widzą wmp.dll, mimo że pamięć zawiera teraz payload atakującego. Tymczasem importy/TLS callbacks są nadal rozwiązywane przez prawdziwy loader, co znacząco zmniejsza ilość własnej logiki parsowania PE, którą musi utrzymywać przeciwnik.

Stage 1 – Build the disguised section

  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. Kopiuj złośliwy PE do tego view sekcja po sekcji, respektując SizeOfRawData/VirtualSize i aktualizując ochrony po tym (PAGE_EXECUTE_READ, PAGE_READWRITE, itd.).
  2. Zastosuj relocacje i rozwiąż importy dokładnie tak jak zrobiłby to reflective loader. Ponieważ view jest już zmapowane jako SEC_IMAGE, alignmenty sekcji i guard pages pasują do tego, czego później spodziewa się Windows loader.
  3. Znormalizuj nagłówek PE:
  • Jeśli payload jest EXE, ustaw IMAGE_FILE_HEADER.Characteristics |= IMAGE_FILE_DLL i wyzeruj entry point, aby zapobiec skokowi LdrpCallTlsInitializers do stubów specyficznych dla EXE.
  • DLL payloady mogą pozostawić nagłówki bez zmian.

W tym momencie proces posiada view z możliwością RWX, którego backing object nadal wskazuje na wmp.dll, podczas gdy bajty w pamięci są kontrolowane przez atakującego.

Stage 2 – Hijack the loader with VEHs

  1. Zarejestruj VEH i ustaw hardware breakpoints: zaprogramuj Dr0 (lub inny rejestr debugowania) na adres ntdll!NtOpenSection i ustaw DR7 tak, żeby każde wykonanie generowało STATUS_SINGLE_STEP. Powtórz później dla NtMapViewOfSection i opcjonalnie NtClose.
  2. Wywołaj ładowanie DLL z LoadLibrary("amsi.dll"). LdrLoadDll ostatecznie wywoła NtOpenSection, aby uzyskać prawdziwy handle sekcji.
  3. VEH hook dla NtOpenSection:
  • Znajdź slot na stosie dla [out] PHANDLE SectionHandle argumentu.
  • Zapisz wcześniej utworzony uchwyt DecoySection do tego slotu.
  • Przesuń RIP/EIP do instrukcji ret, tak żeby kernel nigdy nie został wywołany.
  • Ponownie ustaw hardware breakpoint, aby obserwować NtMapViewOfSection następnie.
  1. VEH hook dla NtMapViewOfSection:
  • Nadpisz [out] PVOID *BaseAddress (i wyjścia dotyczące rozmiaru/ochrony) adresem już zmapowanego złośliwego view.
  • Pomiń ciało syscalla tak jak wcześniej.
  1. (Opcjonalne) VEH hook dla NtClose weryfikuje, że fałszywy handle sekcji jest posprzątany, zapobiegając wyciekom zasobów i dostarczając końcową kontrolę sanityzacji.

Ponieważ syscallsy nigdy nie są wykonane, callbacky jądra (ETWti, minifilter, itd.) nie obserwują podejrzanych zdarzeń NtOpenSection/NtMapViewOfSection, co drastycznie obniża telemetrię. Z punktu widzenia loadera wszystko się powiodło i amsi.dll jest w pamięci, więc kontynuuje rozwiązywanie importów/TLS używając view atakującego.

PoC implementation notes (2025)

Publiczny PoC pokazuje kilka praktycznych detali, które łatwo przeoczyć podczas ponownej implementacji techniki:

  • HWBPs są per-thread. PoC ustawia CONTEXT_DEBUG_REGISTERS na aktualnym wątku przed wywołaniem LoadLibrary, więc VEH musi uruchomić się na tym samym wątku, który wyzwala loader.
  • Emulacja syscalli: VEH ustawia RAX = 0 i przesuwa RIP do ret wewnątrz stubu ntdll (skanuje w poszukiwaniu 0xC3), tak że przejście do kernela nigdy nie następuje, a następnie wznawia wykonanie z NtContinue.
  • Parametry wyjściowe: dla NtMapViewOfSection, VEH nadpisuje zwracane BaseAddress, ViewSize i Win32Protect, tak aby loader uwierzył, że mapowanie powiodło się i kontynuował z importami/TLS używając view atakującego.

Minimalna konfiguracja HWBP użyta w 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);

Wariant stealth

Najnowsze badania związane z VEH pokazują, że handlery można zarejestrować poprzez ręczną manipulację listą VEH zamiast wywoływać AddVectoredExceptionHandler, co ogranicza poleganie na user-mode APIs, które mogą być monitorowane lub hooked. Nie jest to wymagane dla Vectored Overloading, ale można to połączyć, aby zmniejszyć obserwowalną aktywność API.

Etap 3 – Uruchom payload

  • EXE payload: Injector po prostu skacze do oryginalnego entry point, gdy relocations zostaną wykonane. Gdy loader zakłada, że wywoła DllMain, niestandardowy kod zamiast tego wykonuje EXE-style entry.
  • DLL payload / Node.js addon: Rozwiąż i wywołaj docelowy eksport (Kidkadi udostępnia nazwaną funkcję dla JavaScript). Ponieważ moduł jest już zarejestrowany w LdrpModuleBaseAddressIndex, kolejne wyszukiwania traktują go jako nieszkodliwy DLL.

W połączeniu z natywnym addonem Node.js (.node file), cała praca związana z Windows-internals pozostaje poza warstwą JavaScript, co pomaga threat actorowi dostarczać ten sam loader z wieloma różnymi obfuskowanymi wrapperami Node.

References

Tip

Ucz się i ćwicz Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Ucz się i ćwicz Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Wsparcie dla HackTricks