ValveSoftware / ValveSoftware/source-sdk-2013

[All] Explain client crash reason when malicious server sends CUtlVector length not in allowed range

Open
#1,511 4 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
C++
Stars
10k
Forks
3k
Avg merge
8d 11h
Merged PRs (30d)
2

Description

Due to off by one error above if server sends m_Int == pExtra->m_nMaxElements as length it will be accepted by the client.
The problem is CUtlVector indexes lie in the range [0...length) so it is classic off by one error.

Corrected segment (notice added Error statement so client has a chance to understand why his game crashed):

We have a silent crash when malicious server has send CUtlVector length not in allowed range:

void RecvProxy_UtlVectorLength( const CRecvProxyData *pData, void *pStruct, void *pOut )
{
	CRecvPropExtra_UtlVector *pExtra = (CRecvPropExtra_UtlVector*)pData->m_pRecvProp->GetExtraData();
	if ( pData->m_Value.m_Int < 0 || pData->m_Value.m_Int > pExtra->m_nMaxElements )
	{
		// If this happens we're most likely talking to a malicious server.
		// Protect against remote code execution by crashing ourselves.
		// A malicious server can send an invalid lengthprop attribute and cause the below code
		// to "successfully" resize the vector to -1, which eventually translates into a call to realloc(0)
		// due to integer math overflow.
		// Then the remaining payload ( the actual elements of the vector ) can be used
		// to write arbitrary data to out of bounds memory.
		// There isn't much we can do at this point - we're deep in the networking stack, it's hard to recover
		// gracefully and we shouldn't be talking to this server anymore.
		// So we crash.
		*(int *) 1 = 2;
	}

What I suggest is to add Error statement just before crash so player can understand why his client crashed.

void RecvProxy_UtlVectorLength( const CRecvProxyData *pData, void *pStruct, void *pOut )
{
	CRecvPropExtra_UtlVector *pExtra = (CRecvPropExtra_UtlVector*)pData->m_pRecvProp->GetExtraData();
	if ( pData->m_Value.m_Int < 0 || pData->m_Value.m_Int > pExtra->m_nMaxElements )
	{
		// If this happens we're most likely talking to a malicious server.
		// Protect against remote code execution by crashing ourselves.
		// A malicious server can send an invalid lengthprop attribute and cause the below code
		// to "successfully" resize the vector to -1, which eventually translates into a call to realloc(0)
		// due to integer math overflow.
		// Then the remaining payload ( the actual elements of the vector ) can be used
		// to write arbitrary data to out of bounds memory.
		// There isn't much we can do at this point - we're deep in the networking stack, it's hard to recover
		// gracefully and we shouldn't be talking to this server anymore.
		//
		// So we notify client.
		Error("Server send utlvector length value %d which is not in range [%d...%d]. Crashing client to prevent RCE...\n",
		    pData->m_Value.m_Int, 0, pExtra->m_nMaxElements);
		// And crash.
		*(int *) 1 = 2;
	}

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reading RecvProxy_UtlVectorLength and the existing invalid-length branch shown in the issue. Add the requested Error diagnostic immediately before the intentional crash, including the received length and allowed range; done means the client reports why it crashed when the server sends an invalid CUtlVector length.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp
Domain
networking, security
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.