# Exploring Counter-strike: Global Offensive Attack Surface

Unchecked studiohdr2index in CS:GO MDL parsing gives a relative heap OOB write, remotely triggerable by a malicious server via the downloadables string table.

- Payout: $750
- Severity: high
- Program: Valve
- Credited: Victor Cutillas, Louis Jacotot
- Disclosed: 2024-01-08
- Write-up quality: 8/10
- Original: https://www.synacktiv.com/publications/exploring-counter-strike-global-offensive-attack-surface
- Library page: https://bugbountylibrary.com/r/blog%3Asynacktiv-com-publications-exploring-counter-strike-global-offensive-attack-surface
- Tags: domain:desktop domain:network impact:rce kind:bounty platform:blog sev:high tech:windows technique:fuzzing technique:reverse-engineering technique:source-review vuln:dos vuln:memory-corruption

**Target:** Source Engine MDL model parser (datacache.so) on Windows client
**Root cause:** CMDLCache::ReadMDLFile reads studiohdr2index directly from the MDL file and SetVirtualModel() writes pStudioHdr2()->virtualModel at that unchecked offset, a relative heap OOB write.
**Steps:**
1. Craft MDL with studiohdr2index set to a large offset, e.g. `0x1ffff00d` at offset 0x190
2. Place file at `csgo\models\payload.mdl`
3. In offline game console: `sv_cheats 1` then `prop_dynamic_create payload`
4. Crash occurs in studiohdr_t::SetVirtualModel(); ECX holds the file offset, EAX the virtual model id
5. Remotely: hook CDownloadListGenerator::SetStringTable() on server to add malicious model tagged CONSISTENCY_BOUNDS to downloadables string table
6. Client with `sv_allowupload 1` connects, downloads MDL, loads it for bounds check, triggers write
**How found:** AFL++ fuzzing of MDL/BSP parsers using a harness from niklasb's RealWorldCTF 2018 writeup; after two nights ~100 crashes, most OOB reads, but a few write crashes in datacache.so traced to one root cause at MDL parse entrypoint.
**Exploitation:** Attacker-controlled server adds a malicious MDL to the downloadables string table tagged CONSISTENCY_BOUNDS; client with sv_allowupload 1 downloads and loads it during sign-on consistency check, triggering the OOB write before loading screen ends. Full RCE requires heap massaging, partial pointer overwrite for ASLR leak, arbitrary read, then write-what-where.
**Escalation:** Chained with server-controlled primitives: CNETMsg_PlayerAvatarData for arbitrary-size persistent heap spray, and CCSUsrMsg_ShowMenu writing attacker data into g_szPrelocalisedMenuString in engine.dll .data for fixed-address writes.
**Insight:** When auditing game/asset parsers, grep for struct fields read from the file and used directly as offsets into the same buffer (e.g. `this + index`); fuzz harnesses from prior CTF writeups transfer well to proprietary formats, and triage write crashes separately from the noisy OOB-read majority.
