Valve’s Source SDK contained a buffer overflow vulnerability, allowing remote code execution on clients and servers. The vulnerability was exploited by fragging a player, which caused a specially crafted ragdoll model to be loaded. Multiple Source games were updated during the month of June 2017 to fix the vulnerability. Titles included CS:GO, TF2, Hl2:DM, Portal 2, and L4D2. We thank Valve for being very responsive and taking care of vulnerabilities swiftly. Valve patched and released updates for their more popular titles within a day.
Missing Bounds Check
The function nexttoken
is used to tokenize a string. Note how the buffer str
is copied into the buffer token
, as long as a NULL
character or the delimiter character sep
is not found. No bounds checking is performed.
const char *nexttoken(char *token, const char *str, char sep)
{
...
while ((*str != sep) && (*str != '\0'))
{
*token++ = *str++;
}
...
}
The Vulnerability
The method ParseKeyValue
of class CRagdollCollisionRulesParse
is called when processing ragdoll model data, such as when a player is fragged. This method calls nexttoken
to tokenize the rule for further processing.
By supplying a collisionpair
rule longer then 256 characters, the buffer szToken
can be overflowed. Since szToken
is stored on the stack, the return address of the ParseKeyValue
method can be overwritten.
class CRagdollCollisionRulesParse : public IVPhysicsKeyHandler
{
virtual void ParseKeyValue( void *pData, const char *pKey, const char *pValue )
{
...
else if ( !strcmpi( pKey, "collisionpair" ) )
...
char szToken[256];
const char *pStr = nexttoken(szToken, pValue, ',');
...
}
}
Mitigation Bypass
Address Space Layout Randomization (ASLR) is a powerful mitigation against exploiting memory corruption vulnerabilities. The mitigation randomizes the addresses where executables are loaded into memory. This feature is opt-in, and all executables loaded into memory of a process must have it enabled in order for it to be effective.
The DLL steamclient.dll
did not have ASLR enabled. This meant the address of executable pages of steamclient.dll
loaded into memory at predictable addresses. This allowed existing instructions within the executable memory pages to be located and used trivially.
Collecting ROP Gadgets
Return Oriented Programming is a technique that allows shellcode to be created by re-using existing instructions in a program. Simply put, you find a chain of instructions that end with a RETN
instruction. You insert the address of the first instruction of the chain to the stack, so when a function returns the address is popped into the Instruction Pointer register, and then the instructions execute.
Since x86 and x64 instructions do not need to be memory aligned, any address can be interpreted as an instruction. By setting the instruction pointer to a middle of an instruction, a wider range of instructions become available.
The Immunity Debugger plugin Mona provides a utility to discover gadgets. Be aware though, the plugin doesn’t find all useful gadgets, such as REP MOVS
.
Launching cmd.exe
Due to the way the payload is processed, NULL
characters can not be used, and upper case characters are converted to lower case characters. This means our ROP gadget addresses becomes limited, as well as any other data used in our payload.
To get around this, the shellcode is bootstrapped with with a gadget chain which locates the original un-modified buffer in memory. The un-modified payload is then copied back onto the stack via a REP MOVS
gadget.
The steamclient.dll
executable imports LoadLibraryA
and GetProcaddressA
. This allows us to load other DLLs into memory, and obtain references to additional exported functions. We can import Shell32.dll
to obtain a reference to the function ShellExecuteA
, which can be used to launch other programs.
Proof Of Concept
In order to give third-party mod creators time to update their games, the proof of concept will be released in 30 days. Numerous third party game modifications are not compiled with the new security patch. To protect users, the proof of concept will not be released. Source mod developers should apply the patch below.
Delivering The Payload
The Source engine allows custom content to be packed into map files. Commonly this is used for adding extra content to maps, such as sounds or textures. By packing a ragdoll model file into a map file, with the same resource path as an original ragdoll model file, our version will be used instead.
Recommended Fix
To prevent buffer overflows from occurring, do not store more data in a buffer than it can hold. The nexttoken
function should accept a token length argument which would be used to perform bounds checking. Developers who have created a Source modification game should apply the following patch.
To mitigate exploitation of memory corruption vulnerabilities, enable ASLR for all executables. Perform automated checks during the build process to ensure all executables support ASLR. This can be achieved by using the checkbins.py utility developed by the chromium team.
Additionally, Source games should be sandboxed to restrict access to resources and to prevent new processes from being started. As an example of how effective proper sandboxing can be, kernel exploits are often used when exploiting web browser memory corruption vulnerabilities, since the userland browser process is so restricted. For additional information, refer to Chromium’s sandbox implementation.
Final Thoughts
Video games are interesting targets for exploitation, not only technically but also logistically. As video games are common inside employee break rooms and homes of employees, exploitation of a vulnerability could be used in a targeted attack to jump the air gap to a private network. Additionally, discovering a remote code execution vulnerability in a popular video game can be used to quickly create a bot net or spread ransomware.
As a mitigation, games should not be installed on work devices. Gaming machines should be moved to an untrusted network, and business devicess should not connect to the untrusted network.
For those who play Source games, the attack surface can be shrunk by disabling third-party content from downloading. This can be achieved with the console commands cl_allowdownload 0
and cl_downloadfilter all
.
Additionally, since the vulnerability was discovered in the Source SDK, additional third-party mods are most likely vulnerable. However, by enabling ASLR for all executable modules, a memory disclosure vulnerability is required to develop a reliable exploit.
About the author: Justin Taft is a software engineer who is adamant about secure coding practices. He has worked with many Fortune 500 companies to improve their security posture of their products. From reversing firmware of biometric fingerprint readers, to performing security reviews of cloud based Java application deployments, he is experienced in tackling a wide range of security assessments.