Fixups de relocation non sécurisés dans les chargeurs d’assets
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
- Vérifiez les plans d’abonnement !
- Rejoignez le 💬 groupe Discord ou le groupe telegram ou suivez-nous sur Twitter 🐦 @hacktricks_live.
- Partagez des astuces de hacking en soumettant des PR au HackTricks et HackTricks Cloud dépôts github.
Pourquoi les relocations d’assets sont importantes
Beaucoup de moteurs de jeu legacy (Granny 3D, Gamebryo, etc.) chargent des assets complexes en :
- Analyse d’un en-tête et d’une table de sections.
- Allocation d’un buffer heap par section.
- Construction d’un
SectionArrayqui stocke le pointeur de base de chaque section. - Application des tables de relocation afin que les pointeurs intégrés dans les données de section soient patchés vers la section cible et l’offset approprié.
Quand le gestionnaire de relocations fait aveuglément confiance à des métadonnées contrôlées par l’attaquant, chaque relocation devient une primitive potentielle de lecture/écriture arbitraire. Dans Anno 1404: Venice, granny2.dll embarque la fonction utilitaire suivante :
`GrannyGRNFixUp_0` (trimmed)
```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 n’est jamais contrôlé pour la plage et SectionOffset n’est jamais validé par rapport à la taille de la section actuelle. La création d’entrées de relocation avec des offsets négatifs ou des indices surdimensionnés permet de sortir de la section contrôlée et d’écraser des métadonnées de l’allocator, comme le tableau de pointeurs de section lui-même.
Étape 1 – Écriture en arrière dans les métadonnées du loader
L’objectif est de faire en sorte que la relocation table de section 0 écrase des entrées de SectionContentArray (qui reflète SectionArray et est stocké juste avant le premier buffer de section). Parce que l’allocator personnalisé de Granny préfixe 0x1F octets et que le NT heap ajoute son propre en-tête de 0x10 octets plus l’alignement, un attaquant peut précalculer la distance entre le début de la première section (destination) et le tableau de pointeurs de section.
Dans la build testée, forcer le loader à allouer une structure GrannyFile d’exactement 0x4000 octets fait positionner les tableaux de pointeurs de section juste avant le premier buffer de section. En résolvant
0x20 (header) + 0x20 (section descriptors)
+ n * 1 (section types) + n * 1 (flags)
+ n * 4 (pointer table) = 0x4000
donne n = 2720 sections. Une entrée de relocation avec SectionOffset = -0x3FF0 (0x4000 - 0x20 - 0x20 + 0x30) résout maintenant vers SectionContentArray[1] même si la section de destination pense qu’elle modifie des pointeurs internes.
Stage 2 – Deterministic heap layout on Windows 10
Le heap NT de Windows 10 envoie les allocations ≤ RtlpLargestLfhBlock (0x4000) vers le LFH randomisé, et les allocations plus grandes vers l’allocateur backend déterministe. En gardant les métadonnées GrannyFile légèrement au‑dessus de ce seuil (en utilisant l’astuce des 2720 sections) et en préchargeant plusieurs assets .gr2 malveillants, vous pouvez faire :
- Allocation #1 (metadata + section pointer arrays) se place dans un chunk backend >0x4000.
- Allocation #2 (section 0 contents) se place immédiatement après l’allocation #1.
- Allocation #3 (section 1 contents) se place juste après l’allocation #2, vous donnant une cible prévisible pour les relocations suivantes.
Process Monitor a confirmé que les assets sont streamés à la demande, donc le fait de demander à plusieurs reprises des unités/bâtiments spécialement conçus suffit à « amorcer » la disposition du heap sans toucher à l’image exécutable.
Stage 3 – Converting the primitive into RCE
- Corrupt
SectionContentArray[1]. La table de relocation de la section 0 l’écrase en utilisant l’offset-0x3FF0. Pointez‑la vers n’importe quelle région modifiable que vous contrôlez (par ex., des données d’une section ultérieure). - Recycle the corrupted pointer. La table de relocation de la section 1 traite maintenant
SectionNumber = 1comme le pointeur que vous avez injecté. Le gestionnaire écritSectionArray[1] + Offsetàdestination + SectionOffset, vous donnant une écriture arbitraire de 4 octets pour chaque entrée de relocation. - Hit reliable dispatchers. Dans Anno 1404 la cible de choix était les allocator callbacks de
granny2.dll(pas d’ASLR, DEP désactivé). Écraser le pointeur de fonction quegranny2.dllutilise pour le prochain appelMalloc/Freedétourne immédiatement l’exécution vers du code contrôlé par l’attaquant, chargé depuis l’asset trojanisé.
Parce que granny2.dll et les buffers .gr2 injectés résident à des adresses stables lorsque ASLR/DEP sont désactivés, l’attaque se réduit à construire une petite chaîne ROP ou du shellcode brut et à pointer le callback dessus.
Practical checklist
- Look for asset loaders that maintain
SectionArray/ relocation tables. - Diff relocation handlers for missing bounds on indices/offsets.
- Measure the allocator headers added by both the game’s allocator wrapper and the underlying OS heap to compute backwards offsets precisely.
- Force deterministic placement by:
- inflating metadata (many empty sections) until allocation size >
RtlpLargestLfhBlock; - repeatedly loading the malicious asset to fill backend holes.
- inflating metadata (many empty sections) until allocation size >
- Use a two-stage relocation table (first to retarget
SectionArray, second to spray writes) and overwrite function pointers that will fire during normal rendering (allocator callbacks, virtual tables, animation dispatchers, etc.).
Once you gain an arbitrary file write (e.g., via the path traversal in the multiplayer save transfer), repackaging RDA archives with the crafted .gr2 gives you a clean delivery vector that is automatically decompressed by remote clients.
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
- Vérifiez les plans d’abonnement !
- Rejoignez le 💬 groupe Discord ou le groupe telegram ou suivez-nous sur Twitter 🐦 @hacktricks_live.
- Partagez des astuces de hacking en soumettant des PR au HackTricks et HackTricks Cloud dépôts github.
HackTricks

