Correcciones de relocación inseguras en cargadores de assets
Tip
Aprende y practica Hacking en AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP:HackTricks Training GCP Red Team Expert (GRTE)
Aprende y practica Hacking en Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Apoya a HackTricks
- Revisa los planes de suscripción!
- Únete al 💬 grupo de Discord o al grupo de telegram o síguenos en Twitter 🐦 @hacktricks_live.
- Comparte trucos de hacking enviando PRs a los HackTricks y HackTricks Cloud repositorios de github.
Por qué importan las relocaciones de assets
Muchos motores de juego heredados (Granny 3D, Gamebryo, etc.) cargan assets complejos mediante:
- Analizar un header y la tabla de secciones.
- Asignar un heap buffer por sección.
- Construir un
SectionArrayque almacena el puntero base de cada sección. - Aplicar tablas de relocación para que los punteros embebidos dentro de los datos de la sección se parchen al offset + sección objetivo correctos.
Cuando el manejador de relocaciones confía ciegamente en metadata controlada por el atacante, cada relocación se convierte en un potencial primitivo de lectura/escritura arbitraria. En Anno 1404: Venice, granny2.dll incluye la siguiente función auxiliar:
`GrannyGRNFixUp_0` (recortado)
```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 nunca se comprueba en rango y SectionOffset nunca se valida respecto al tamaño actual de la sección. Crear entradas de relocación con offsets negativos o índices sobredimensionados permite salir de la sección que controlas y pisar metadatos del allocator como el propio arreglo de punteros de sección.
Etapa 1 – Escribir hacia atrás en los metadatos del loader
El objetivo es lograr que la tabla de relocación de sección 0 sobrescriba entradas de SectionContentArray (que refleja a SectionArray y se almacena justo antes del primer buffer de sección). Debido a que el custom allocator de Granny antepone 0x1F bytes y la NT heap añade su propio encabezado de 0x10 bytes más alineamiento, un atacante puede precalcular la distancia entre el inicio de la primera sección (destination) y el arreglo de punteros de sección.
En la build probada, forzar al loader a asignar una estructura GrannyFile de exactamente 0x4000 bytes hace que los arreglos de punteros de sección queden justo antes del primer buffer de sección. Resolviendo
0x20 (header) + 0x20 (section descriptors)
+ n * 1 (section types) + n * 1 (flags)
+ n * 4 (pointer table) = 0x4000
genera n = 2720 secciones. Una entrada de relocación con SectionOffset = -0x3FF0 ( 0x4000 - 0x20 - 0x20 + 0x30 ) ahora resuelve a SectionContentArray[1] aunque la sección destino piense que está parchando punteros internos.
Etapa 2 – Distribución determinista del heap en Windows 10
Windows 10 NT Heap dirige las asignaciones ≤ RtlpLargestLfhBlock (0x4000) al LFH aleatorizado y las mayores al asignador backend determinista. Manteniendo los metadatos de GrannyFile ligeramente por encima de ese umbral (usando el truco de 2720 secciones) y precargando varios .gr2 assets maliciosos, puedes hacer que:
- La asignación #1 (metadata + section pointer arrays) quede en un chunk backend >0x4000.
- La asignación #2 (contenido de la sección 0) quede inmediatamente después de la asignación #1.
- La asignación #3 (contenido de la sección 1) quede justo después de la asignación #2, dándote un objetivo predecible para las relocaciones subsecuentes.
Process Monitor confirmó que los assets se transmiten bajo demanda, por lo que solicitar repetidamente unidades/edificios creados a medida es suficiente para “preparar” la disposición del heap sin tocar la imagen ejecutable.
Etapa 3 – Convertir el primitivo en RCE
- Corrompe
SectionContentArray[1]. La tabla de relocaciones de la sección 0 la sobrescribe usando el offset-0x3FF0. Apúntala a cualquier región escribible que controles (p.ej., datos de una sección posterior). - Recicla el puntero corrompido. La tabla de relocaciones de la sección 1 ahora trata
SectionNumber = 1como el puntero que inyectaste. El manejador escribeSectionArray[1] + Offsetendestination + SectionOffset, otorgándote una escritura arbitraria de 4 bytes por cada entrada de relocación. - Golpea dispatchers fiables. En Anno 1404 el objetivo elegido fueron los allocator callbacks de
granny2.dll(sin ASLR, DEP deshabilitado). Sobrescribir el puntero de función quegranny2.dllusa para la siguiente llamada aMalloc/Freedesvía inmediatamente la ejecución a código controlado por el atacante cargado desde el asset troyanizado.
Como tanto granny2.dll como los buffers .gr2 inyectados residen en direcciones estables cuando ASLR/DEP están deshabilitados, el ataque se reduce a construir una pequeña cadena ROP o shellcode bruto y apuntar el callback hacia ella.
Lista de verificación práctica
- Busca asset loaders que mantengan
SectionArray/ relocation tables. - Haz diff de los relocation handlers buscando límites faltantes en índices/offsets.
- Mide los encabezados del allocator añadidos tanto por el wrapper del allocator del juego como por el heap del SO subyacente para calcular con precisión los offsets hacia atrás.
- Forzar la colocación determinista mediante:
- inflar los metadatos (muchas secciones vacías) hasta que el tamaño de la asignación >
RtlpLargestLfhBlock; - cargar repetidamente el asset malicioso para llenar huecos del backend.
- inflar los metadatos (muchas secciones vacías) hasta que el tamaño de la asignación >
- Usa una tabla de relocación en dos etapas (primero para redirigir
SectionArray, segundo para esparcir escrituras) y sobrescribe punteros de función que se ejecuten durante el renderizado normal (allocator callbacks, virtual tables, animation dispatchers, etc.).
Una vez obtengas una escritura arbitraria de archivo (p.ej., vía el path traversal en la transferencia de guardado multijugador), volver a empaquetar los archivos RDA con el .gr2 manipulado te da un vector de entrega limpio que es descomprimido automáticamente por los clientes remotos.
Referencias
Tip
Aprende y practica Hacking en AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP:HackTricks Training GCP Red Team Expert (GRTE)
Aprende y practica Hacking en Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Apoya a HackTricks
- Revisa los planes de suscripción!
- Únete al 💬 grupo de Discord o al grupo de telegram o síguenos en Twitter 🐦 @hacktricks_live.
- Comparte trucos de hacking enviando PRs a los HackTricks y HackTricks Cloud repositorios de github.
HackTricks

