Valve · Report #876719
Signedness issue in ClassInfo message handler leads to RCE on CS:GO client
Summary
Title: Signedness issue in ClassInfo message handler leads to RCE on CS:GO client Scope: csgo.exe Weakness: Array Index Underflow Severity: Critical (9.6) Link: https://hackerone.com/reports/876719 Date: 2020-05-17 20:31:35 +0000 By: @chaynik
Details: Vulnerability
CSVCMsg_ClassInfo message is used by Source Engine to pass information about entity classes. It is described by the following Protobuf:
message CSVCMsg_ClassInfo {
message class_t {
optional int32 class_id = 1;
optional string data_table_name = 2;
optional string class_name = 3;
}
optional bool create_on_client = 1;
repeated .CSVCMsg_ClassInfo.class_t classes = 2;
}
The bug is present in CSVCMsg_ClassInfo message handler on client. The pseudocode of function that handles this message:
bool ProcessClassInfo(CSVCMsg_ClassInfo *msg) {
...
int nClasses = msg->classes_size;
ClassInfo *pClasses = new ClassInfo[nClasses];
...
for (int i = 0; i < nClasses; i++) {
class_t *src = msg->classes[i];
if (src->class_id >= nClasses) { // class_id can be negative!
...
return false;
}
ClassInfo *dst = &pClasses[src->class_id];
...
}
...
}
An array of appropriate size is allocated to hold the received information. The array is indexed by class_id, which is improperly sanitized: it can be an arbitrary negative integer. This allows an out of bounds write, which can be exploited to perform remote code execution.
Any Source Engine (and Source 2) game that uses Protobuf network messages should be affected by this vulnerability, including CS:GO and Dota 2. Only CS:GO has been tested.
Exploit details
The Proof-of-Concept exploit consists of two main phases:
- ASLR bypass - the bug is used to get base address of
client_panorama.dll - Remote code execution - the bug is used to divert control flow to ROP chain
In order to craft a ROP chain, attacker needs to know the absolute address of some application module. The ASLR attempts to prevent it by randomizing memory layout.
Bypassing the ASLR
Source Engine has mechanism that allows server to set and query "cvars" - variables that control various game-related settings. The exploit leverages cvars to steal a pointer to predictable memory location in game process:
- Spray the heap with entities
- we want to make heap allocations more predictable
- if we allocate 500+ entities there is high chance that last of them are side-by-side
- Delete last 20 entities
- deallocated data, including pointers to vtables, remains on heap
- Set some cvar to a string of appropriate length
- we want it to be allocated in place of old entities
- we want the vtable pointer of some old entity to be right after the end of the string
- Use the vulnerability to overwrite null terminator of cvar string
- we want the class-infos to get allocated next to cvar
- if we succeed, the cvar string will be extended to contain the vtable pointer
- Query the cvar string
- the leaked vtable pointer allows us to calculate
client_panorama.dllbase address
- the leaked vtable pointer allows us to calculate
There is one issue with this idea: the client breaks connection after the vulnerability is used, due to some late sanity check. There's an easy way around it though. Server can queue a retry command to be executed on client, so the client automatically reconnects.
Executing code
After succesful pointer leakage, a ROP chain that runs calc.exe is crafted using gadgets from client_panorama.dll. The RCE is performed as follows:
- Deliver the ROP chain in
CCSUsrMsg_ShowMenuuser message- client stores it in global buffer in
client_panorama.dll - we can easily calculate address to it
- client stores it in global buffer in
- Spray the heap with entities
- we want to make heap allocations more predictable
- Use the vulnerability to overwrite vtable pointer of some entity
- we want the class-infos to get allocated after last entity
- The client breaks connection and deallocates entities
- the fake vtable will divert control flow to our ROP chain
- the ROP chain will launch Calculator app
Reproduction
The PoC script simulates a malicious CS:GO server. It demonstrates RCE capability on CS:GO client for Windows (version 13752, 2020-05-14 stable release).
- Download the attached Python 3 script: F831986
- Run the script (possibly on another host)
- Start CS:GO client
- Connect to the malicious server
- Wait for
calc.exeto pop up
Similarly as in #470520, Steam browser protocol can be used to launch an attack from web browser:
- Download the attached Python 3 script: F831986
- Run the script (possibly on another host)
- Download attached HTML file - F831987
- Set address in iframe URL to the malicious server
- Open downloaded HTML file and confirm
Open steam - Wait for
calc.exeto pop up
Impact
An attacker can execute arbitrary code on the computer of anyone who attempts to connect to the server. After successful exploitation an attacker can gain control over victim's computer.
The connection to the server can be initiated manually by the victim or automatically by visiting malicious web site via Steam browser protocol.
The likelihood of victim joining the server via in-game server browser can be greatly improved by faking high player count and further social engineering. Many players sort server list by player amount.
In case of an attack from web browser many users don't need to click Open steam and this method requires no further interaction from user - connection will be initiated without confirmation (even game client will be started if it's not running).
How it was found
The write-up for this report has not been downloaded yet, or the program disclosed it without one. Read it on HackerOne.