Niebezpieczne poprawki relokacji w ładowarkach zasobów

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

Dlaczego relokacje zasobów są ważne

Wiele przestarzałych silników gier (Granny 3D, Gamebryo, itd.) wczytuje złożone zasoby przez:

  1. Parsowanie nagłówka i tabeli sekcji.
  2. Alokowanie jednego bufora na stercie na każdą sekcję.
  3. Tworzenie SectionArray, który przechowuje wskaźnik bazowy każdej sekcji.
  4. Stosowanie tabel relokacji, aby wskaźniki osadzone w danych sekcji zostały poprawione na właściwą sekcję docelową + przesunięcie.

Kiedy obsługa relokacji bezkrytycznie ufa metadanym kontrolowanym przez atakującego, każda relokacja staje się potencjalnym prymitywem dowolnego odczytu/zapisu. W Anno 1404: Venice, granny2.dll dostarcza następującą funkcję pomocniczą:

`GrannyGRNFixUp_0` (skrócone) ```c int *__cdecl GrannyGRNFixUp_0(DWORD RelocationCount, Relocation *PointerFixupArray, int *SectionArray, char *destination) { while (RelocationCount--) { int target_base = SectionArray[PointerFixupArray->SectionNumber]; // unchecked index int *patch_site = (int *)(destination + PointerFixupArray->SectionOffset); // unchecked offset *patch_site = target_base ; if (target_base) *patch_site = target_base + PointerFixupArray->Offset; ++PointerFixupArray; } return SectionArray; } ```

SectionNumber nie jest sprawdzany zakresowo, a SectionOffset nie jest weryfikowany względem rozmiaru bieżącej sekcji. Sporządzanie wpisów relokacji z ujemnymi przesunięciami lub nadmiernie dużymi indeksami pozwala wyjść poza kontrolowaną sekcję i nadpisać metadane alokatora, takie jak sama tablica wskaźników sekcji.

Stage 1 – Zapis wstecz w metadanych loadera

Celem jest sprawienie, by tabela relokacji section 0 nadpisała wpisy SectionContentArray (która odzwierciedla SectionArray i jest przechowywana tuż przed pierwszym buforem sekcji). Ponieważ niestandardowy alokator Granny dokłada z przodu 0x1F bajtów, a NT heap dodaje własny nagłówek o rozmiarze 0x10 bajtów oraz wyrównanie, atakujący może wstępnie obliczyć odległość między początkiem pierwszej sekcji (destination) a tablicą wskaźników sekcji.

W testowanej kompilacji zmuszenie loadera do zaalokowania struktury GrannyFile o dokładnie 0x4000 bajtów powoduje, że tablice wskaźników sekcji lądują tuż przed pierwszym buforem sekcji. Rozwiązanie

0x20 (header) + 0x20 (section descriptors)
+ n * 1 (section types) + n * 1 (flags)
+ n * 4 (pointer table) = 0x4000

daje n = 2720 sekcji. Wpis relocacji z SectionOffset = -0x3FF0 ( 0x4000 - 0x20 - 0x20 + 0x30 ) teraz rozwiązuje się do SectionContentArray[1], mimo że docelowa sekcja myśli, że poprawia wewnętrzne wskaźniki.

Etap 2 – Deterministyczny heap layout na Windows 10

Windows 10 NT Heap kieruje alokacje ≤ RtlpLargestLfhBlock (0x4000) do zrandomizowanego LFH, a większe do deterministycznego backend allocator. Zachowując metadane GrannyFile nieco powyżej tego progu (używając sztuczki z 2720 sekcjami) i wstępnie ładując kilka złośliwych assetów .gr2, możesz spowodować:

  • Alokacja #1 (metadata + tablice wskaźników sekcji) trafi do backend chunku >0x4000.
  • Alokacja #2 (zawartość sekcji 0) trafi bezpośrednio po alokacji #1.
  • Alokacja #3 (zawartość sekcji 1) trafi tuż po alokacji #2, dając przewidywalny cel dla kolejnych relocacji.

Process Monitor potwierdził, że assety są streamowane na żądanie, więc wielokrotne żądanie spreparowanych jednostek/budynków wystarczy, by „przygotować” heap layout bez dotykania pliku wykonywalnego.

Etap 3 – Konwersja prymitywu na RCE

  1. Uszkodź SectionContentArray[1]. Tabela relocacji sekcji 0 nadpisuje ją używając offsetu -0x3FF0. Wskaż ją na dowolny zapisywalny region, który kontrolujesz (np. dane późniejszej sekcji).
  2. Ponownie użyj skażonego wskaźnika. Tabela relocacji sekcji 1 teraz traktuje SectionNumber = 1 jako wskaźnik, który wstrzyknąłeś. Handler zapisuje SectionArray[1] + Offset do destination + SectionOffset, dając Ci arbitralny zapis 4 bajtów dla każdego wpisu relocacji.
  3. Traf w niezawodne dispatchery. W Anno 1404 celem z wyboru były allocator callbacks w granny2.dll (brak ASLR, DEP wyłączone). Nadpisanie wskaźnika funkcji, którego granny2.dll używa dla następnego wywołania Malloc/Free, natychmiast przekierowuje wykonanie do kodu kontrolowanego przez atakującego załadowanego z trojanizowanego assetu.

Ponieważ zarówno granny2.dll, jak i wstrzyknięte bufory .gr2 znajdują się pod stabilnymi adresami, gdy ASLR/DEP są wyłączone, atak sprowadza się do zbudowania małego łańcucha ROP lub surowego shellcode’u i skierowania callbacka na niego.

Praktyczna lista kontrolna

  • Szukaj loaderów assetów, które utrzymują SectionArray / tabele relocacji.
  • Porównaj (diff) handlery obsługi relocacji pod kątem brakujących sprawdzeń granic dla indeksów/offsetów.
  • Zmierz nagłówki alokatorów dodawane zarówno przez wrapper alokatora gry, jak i przez systemowy heap, aby precyzyjnie obliczyć odwrotne offsety.
  • Wymuś deterministyczne rozmieszczenie przez:
    • zwiększanie metadata (wiele pustych sekcji) aż rozmiar alokacji > RtlpLargestLfhBlock;
    • wielokrotne ładowanie złośliwego assetu, aby wypełnić luki w backendzie.
  • Użyj dwustopniowej tabeli relocacji (najpierw do przelokowania SectionArray, potem do rozpylenia zapisów) i nadpisz wskaźniki funkcji, które zostaną wywołane podczas normalnego renderowania (allocator callbacks, virtual tables, animation dispatchers, itd.).

Gdy uzyskasz arbitralny zapis pliku (np. przez path traversal w transferze zapisu multiplayer), przepakowanie archiwów RDA ze spreparowanym .gr2 daje czysty wektor dostarczenia, który jest automatycznie dekompresowany przez zdalnych klientów.

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