Skip to content

Commit beeb25e

Browse files
authored
Merge pull request #1679 from HackTricks-wiki/update_Exploiting_Anno_1404_20251216_183640
Exploiting Anno 1404
2 parents 5e5093c + 97531bc commit beeb25e

File tree

5 files changed

+211
-0
lines changed

5 files changed

+211
-0
lines changed

src/SUMMARY.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,7 @@
442442
- [Buckets](network-services-pentesting/pentesting-web/buckets/README.md)
443443
- [Firebase Database](network-services-pentesting/pentesting-web/buckets/firebase-database.md)
444444
- [CGI](network-services-pentesting/pentesting-web/cgi.md)
445+
- [Custom Protocols](network-services-pentesting/pentesting-web/custom-protocols.md)
445446
- [Django](network-services-pentesting/pentesting-web/django.md)
446447
- [Dotnet Soap Wsdl Client Exploitation](network-services-pentesting/pentesting-web/dotnet-soap-wsdl-client-exploitation.md)
447448
- [DotNetNuke (DNN)](network-services-pentesting/pentesting-web/dotnetnuke-dnn.md)
@@ -823,6 +824,7 @@
823824
- [Windows Seh Overflow](binary-exploitation/stack-overflow/windows-seh-overflow.md)
824825
- [Array Indexing](binary-exploitation/array-indexing.md)
825826
- [Chrome Exploiting](binary-exploitation/chrome-exploiting.md)
827+
- [Common Exploiting Problems Unsafe Relocation Fixups](binary-exploitation/common-exploiting-problems-unsafe-relocation-fixups.md)
826828
- [Integer Overflow](binary-exploitation/integer-overflow-and-underflow.md)
827829
- [Format Strings](binary-exploitation/format-strings/README.md)
828830
- [Format Strings - Arbitrary Read Example](binary-exploitation/format-strings/format-strings-arbitrary-read-example.md)
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
# Unsafe Relocation Fixups in Asset Loaders
2+
3+
{{#include ../banners/hacktricks-training.md}}
4+
5+
## Why asset relocations matter
6+
7+
Many legacy game engines (Granny 3D, Gamebryo, etc.) load complex assets by:
8+
9+
1. Parsing a header and section table.
10+
2. Allocating one heap buffer per section.
11+
3. Building a `SectionArray` that stores the base pointer of every section.
12+
4. Applying relocation tables so that pointers embedded inside the section data get patched to the right target section + offset.
13+
14+
When the relocation handler blindly trusts attacker-controlled metadata, every relocation becomes a potential arbitrary read/write primitive. In *Anno 1404: Venice*, `granny2.dll` ships the following helper:
15+
16+
<details>
17+
<summary>`GrannyGRNFixUp_0` (trimmed)</summary>
18+
19+
```c
20+
int *__cdecl GrannyGRNFixUp_0(DWORD RelocationCount,
21+
Relocation *PointerFixupArray,
22+
int *SectionArray,
23+
char *destination)
24+
{
25+
while (RelocationCount--) {
26+
int target_base = SectionArray[PointerFixupArray->SectionNumber]; // unchecked index
27+
int *patch_site = (int *)(destination + PointerFixupArray->SectionOffset); // unchecked offset
28+
*patch_site = target_base ;
29+
if (target_base)
30+
*patch_site = target_base + PointerFixupArray->Offset;
31+
++PointerFixupArray;
32+
}
33+
return SectionArray;
34+
}
35+
```
36+
37+
</details>
38+
39+
`SectionNumber` is never range-checked and `SectionOffset` is never validated against the current section size. Crafting relocation entries with negative offsets or oversized indices lets you walk outside the section you control and stomp allocator metadata such as the section pointer array itself.
40+
41+
## Stage 1 – Writing backwards into loader metadata
42+
43+
The goal is to make the relocation table of **section 0** overwrite entries of `SectionContentArray` (which mirrors `SectionArray` and is stored right before the first section buffer). Because Granny’s custom allocator prepends **0x1F** bytes and the NT heap adds its own **0x10**-byte header plus alignment, an attacker can precalculate the distance between the start of the first section (`destination`) and the section-pointer array.
44+
45+
In the tested build, forcing the loader to allocate a `GrannyFile` structure that is exactly **0x4000 bytes** makes the section-pointer arrays land right before the first section buffer. Solving
46+
47+
```
48+
0x20 (header) + 0x20 (section descriptors)
49+
+ n * 1 (section types) + n * 1 (flags)
50+
+ n * 4 (pointer table) = 0x4000
51+
```
52+
53+
gives **n = 2720** sections. A relocation entry with `SectionOffset = -0x3FF0` ( `0x4000 - 0x20 - 0x20 + 0x30` ) now resolves to `SectionContentArray[1]` even though the destination section thinks it is patching internal pointers.
54+
55+
## Stage 2 – Deterministic heap layout on Windows 10
56+
57+
Windows 10 NT Heap routes allocations **≤ RtlpLargestLfhBlock (0x4000)** to the randomized LFH and larger ones to the deterministic backend allocator. By keeping the `GrannyFile` metadata slightly above that threshold (using the 2720 sections trick) and preloading several malicious `.gr2` assets, you can make:
58+
59+
- Allocation #1 (metadata + section pointer arrays) land in a >0x4000 backend chunk.
60+
- Allocation #2 (section 0 contents) land immediately after allocation #1.
61+
- Allocation #3 (section 1 contents) land right after allocation #2, giving you a predictable target for subsequent relocations.
62+
63+
Process Monitor confirmed that assets are streamed on demand, so repeatedly requesting crafted units/buildings is enough to “prime” the heap layout without touching the executable image.
64+
65+
## Stage 3 – Converting the primitive into RCE
66+
67+
1. **Corrupt `SectionContentArray[1]`.** Section 0’s relocation table overwrites it by using the `-0x3FF0` offset. Point it at any writable region you control (e.g., later section data).
68+
2. **Recycle the corrupted pointer.** Section 1’s relocation table now treats `SectionNumber = 1` as whatever pointer you injected. The handler writes `SectionArray[1] + Offset` to `destination + SectionOffset`, giving you an arbitrary 4-byte write for every relocation entry.
69+
3. **Hit reliable dispatchers.** In Anno 1404 the target of choice was the `granny2.dll` allocator callbacks (no ASLR, DEP disabled). Overwriting the function pointer that `granny2.dll` uses for the next `Malloc`/`Free` call immediately diverts execution to attacker-controlled code loaded from the trojanized asset.
70+
71+
Because both `granny2.dll` and the injected `.gr2` buffers reside at stable addresses when ASLR/DEP are disabled, the attack reduces to building a small ROP chain or raw shellcode and pointing the callback at it.
72+
73+
## Practical checklist
74+
75+
- Look for asset loaders that maintain `SectionArray` / relocation tables.
76+
- Diff relocation handlers for missing bounds on indices/offsets.
77+
- Measure the allocator headers added by both the game’s allocator wrapper and the underlying OS heap to compute backwards offsets precisely.
78+
- Force deterministic placement by:
79+
- inflating metadata (many empty sections) until allocation size > `RtlpLargestLfhBlock`;
80+
- repeatedly loading the malicious asset to fill backend holes.
81+
- 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.).
82+
83+
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.
84+
85+
## References
86+
87+
- [Synacktiv – Exploiting Anno 1404](https://www.synacktiv.com/publications/exploiting-anno-1404.html)
88+
- [W. Yason – Windows 10 Segment Heap Internals (BlackHat USA 2016)](https://blackhat.com/docs/us-16/materials/us-16-Yason-Windows-10-Segment-Heap-Internals-wp.pdf)
89+
90+
{{#include ../banners/hacktricks-training.md}}

src/binary-exploitation/common-exploiting-problems.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,10 @@ No memory safety bug is required—simply observing serialization order of point
210210

211211
## Related pages
212212

213+
{{#ref}}
214+
common-exploiting-problems-unsafe-relocation-fixups.md
215+
{{#endref}}
216+
213217
{{#ref}}
214218
../mobile-pentesting/android-app-pentesting/reversing-native-libraries.md
215219
{{#endref}}

src/network-services-pentesting/pentesting-web/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ Some **tricks** for **finding vulnerabilities** in different well known **techno
7676
- [**Artifactory**](artifactory-hacking-guide.md)
7777
- [**Buckets**](buckets/index.html)
7878
- [**CGI**](cgi.md)
79+
- [**Custom UDP RPC Protocols**](custom-protocols.md)
7980
- [**Dotnet SOAP WSDL client exploitation**](dotnet-soap-wsdl-client-exploitation.md)
8081
- [**Drupal**](drupal/index.html)
8182
- [**Flask**](flask.md)
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
# Custom UDP RPC Enumeration & File-Transfer Abuse
2+
3+
{{#include ../../banners/hacktricks-training.md}}
4+
5+
## Mapping proprietary RPC objects with Frida
6+
7+
Older multiplayer titles often embed home-grown RPC stacks on top of UDP. In *Anno 1404: Venice* this is implemented inside `NetComEngine3.dll` via the `RMC_CallMessage` dispatcher, which parses 5 fields from every datagram:
8+
9+
| Field | Purpose |
10+
| --- | --- |
11+
| `ID` | RPC verb (16-bit) |
12+
| `Flags` | Transport modifiers (reliability, ordering) |
13+
| `Source` | Object ID of the caller |
14+
| `TargetObject` | Remote object instance |
15+
| `Method` | Method index inside the target class |
16+
17+
Two helper functions – `ClassToMethodName()` and `TargetName()` – translate raw IDs into human-readable strings for logging. By brute-forcing 24‑bit object IDs and 16‑bit method IDs and calling those helpers we can enumerate the entire remotely reachable surface without traffic captures or symbol leaks.
18+
19+
<details>
20+
<summary>Frida surface enumerator (trimmed)</summary>
21+
22+
```javascript
23+
'use strict';
24+
25+
const classToMethod = Module.getExportByName('NetComEngine3.dll', 'ClassToMethodName');
26+
const targetName = Module.getExportByName('NetComEngine3.dll', 'TargetName');
27+
28+
function tryID(objID, methodID) {
29+
const method = new NativeFunction(classToMethod, 'pointer', ['pointer', 'uint']);
30+
const target = new NativeFunction(targetName, 'pointer', ['pointer']);
31+
const buf = Memory.alloc(Process.pointerSize);
32+
buf.writeU32(objID);
33+
const m = method(buf, methodID);
34+
if (!m.isNull()) {
35+
const t = target(buf);
36+
console.log(objID.toString(16), '=', t.readUtf16String());
37+
console.log(' -', methodID, '=', m.readUtf16String());
38+
}
39+
}
40+
41+
for (let obj = 0; obj < 0x9000000; obj += 0x400000) {
42+
for (let meth = 0; meth < 0x40; meth++) {
43+
tryID(obj, meth);
44+
}
45+
}
46+
```
47+
48+
</details>
49+
50+
Running `frida -l explore-surface.js Addon.exe` emitted the complete RPC map, including the `Player` object (`0x7400000`) and its file-transfer verbs `OnSendFileInit`, `OnSendFileData`, `OnReceivedFileData`, and `OnCancelSendFile`. The same workflow applies to any binary protocol that exposes internal reflection helpers: intercept the dispatcher, brute-force IDs, and log what the engine already knows about each callable method.
51+
52+
### Tips
53+
54+
- Use the engine’s own logging buffers (`WString::Format` in this case) to avoid reimplementing undocumented string encodings.
55+
- Dump `Flags` to identify reliability features (ACK, resend requests) before attempting fuzzing; custom UDP stacks frequently drop malformed packets silently.
56+
- Store the enumerated map – it serves as a fuzzing corpus and makes it obvious which objects manipulate the filesystem, world state, or in-game scripting.
57+
58+
## Subverting file-transfer RPCs
59+
60+
Multiplayer save synchronization used a two-packet handshake:
61+
62+
1. `OnSendFileInit` — carries the UTF‑16 filename the client should use when saving the incoming payload.
63+
2. `OnSendFileData` — streams raw file contents in fixed-size chunks.
64+
65+
Because the server serializes the filename through `ByteStreamWriteString()` right before sending, a Frida hook can swap the pointer to a traversal payload while keeping packet sizes intact.
66+
67+
<details>
68+
<summary>Filename swapper</summary>
69+
70+
```javascript
71+
const writeStr = ptr('0x1003A250');
72+
const ByteStreamWriteString = new NativeFunction(writeStr, 'pointer', ['pointer', 'pointer']);
73+
const evil = Memory.allocUtf16String('..\\..\\..\\..\\Sauvegarde.sww');
74+
75+
Interceptor.attach(writeStr, {
76+
onEnter(args) {
77+
const src = args[1].readPointer();
78+
const value = src.readUtf16String();
79+
if (value && value.indexOf('Sauvegarde.sww') !== -1) {
80+
args[1].writePointer(evil);
81+
}
82+
}
83+
});
84+
```
85+
86+
</details>
87+
88+
Victim clients performed zero sanitisation and wrote the received save to whatever path the hostile host supplied, e.g. dropping into `C:\User\user` instead of the intended `...\Savegames\MPShare` tree. On Windows installations of Anno 1404 the game directory is world-writable, so the traversal instantly becomes an arbitrary file write primitive:
89+
90+
- **Drop DLLs** for classic search-order hijacking on next launch, or
91+
- **Overwrite asset archives** (RDA files) so that weaponized models, textures, or scripts are loaded live during the same session.
92+
93+
### Defending / attacking other targets
94+
95+
- Look for RPC verbs named `SendFile`, `Upload`, `ShareSave`, etc., then intercept the serialization helper responsible for filenames or target directories.
96+
- Even if filenames are length-checked, many stacks forget to canonicalize `..\` or mixed `/` vs `\` sequences; brute-force all separators.
97+
- When the receiver stores files under the game install path, check ACLs via `icacls` to confirm whether an unprivileged user can drop code there.
98+
99+
## Turning path traversal into live asset execution
100+
101+
Once you can upload arbitrary bytes, replace any frequently loaded asset:
102+
103+
1. **Unpack the archive.** RDA archives are DEFLATE-based containers whose metadata is optionally XOR-obfuscated with `srand(0xA2C2A)` seeded streams. Tools like [RDAExplorer](https://github.com/lysanntranvouez/RDAExplorer) re-pack archives after edits.
104+
2. **Inject a malicious `.gr2`.** The trojanized Granny 3D file carries the relocation exploit that overwrites `SectionContentArray` and, through a two-stage relocation sequence, gains an arbitrary 4-byte write inside `granny2.dll`.
105+
3. **Hijack allocator callbacks.** With ASLR disabled and DEP off, replacing the `malloc/free` function pointers in `granny2.dll` redirects the next allocation to your shellcode, giving immediate RCE without waiting for the victim to restart the game.
106+
107+
This pattern generalises to any title that streams structured assets from binary archives: combine RPC-level traversal for delivery and unsafe relocation processing for code execution.
108+
109+
## References
110+
111+
- [Synacktiv – Exploiting Anno 1404](https://www.synacktiv.com/publications/exploiting-anno-1404.html)
112+
- [RDA File Format notes](https://github.com/lysanntranvouez/RDAExplorer/wiki/RDA-File-Format)
113+
114+
{{#include ../../banners/hacktricks-training.md}}

0 commit comments

Comments
 (0)