Remote Code Execution In Source Games

Published: July 19, 2017
Author: Justin Taft

Valve's Source SDK contained a buffer overflow vulnerability which allowed remote code execution on clients and servers. The vulnerability was exploited by fragging a player, which casued 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 vulnerabilites 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 delimieter character sep is not found. No bounds checking is performed.

View source on GitHub.

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.

View source on GitHub.

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.

Using Mona to find ASLR disabled binaries


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.

ROP Gadgets


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. 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.

Map Download Progress Bar


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.

Wifi symbol signaling airgapped network