Fix 'CLC_CmdKeyValues' exploit

Fix exploitation vector that could be performed on the game client to submit commands to other game clients connected to the same server, specifically the 'OnPlayerAward' command. Base_CmdKeyValues now only works when sv_cheats is enabled. SVC/CLC_CmdKeyValues subclass 'Base_CmdKeyValues', so these messages are 'fixed' as well.
This commit is contained in:
Kawe Mazidjatari 2023-04-15 02:13:29 +02:00
parent 0ffcca14ab
commit 7870f1557a
2 changed files with 93 additions and 6 deletions

View File

@ -9,9 +9,13 @@
//
///////////////////////////////////////////////////////////////////////////////////
#include "core/stdafx.h"
#include "tier1/cvar.h"
#include "engine/net.h"
#include "common/netmessages.h"
///////////////////////////////////////////////////////////////////////////////////
// re-implementation of 'SVC_Print::Process'
///////////////////////////////////////////////////////////////////////////////////
bool SVC_Print::ProcessImpl()
{
if (this->m_szText)
@ -30,6 +34,9 @@ bool SVC_Print::ProcessImpl()
return true; // Original just return true also.
}
///////////////////////////////////////////////////////////////////////////////////
// re-implementation of 'SVC_UserMessage::Process'
///////////////////////////////////////////////////////////////////////////////////
bool SVC_UserMessage::ProcessImpl()
{
bf_read buf = m_DataIn;
@ -53,13 +60,43 @@ bool SVC_UserMessage::ProcessImpl()
return SVC_UserMessage_Process(this); // Need to return original.
}
///////////////////////////////////////////////////////////////////////////////////
// below functions are hooked as 'CmdKeyValues' isn't really used in this game, but
// still exploitable on the server. the 'OnPlayerAward' command calls the function
// 'UTIL_SendClientCommandKVToPlayer' which forwards the keyvalues to all connected clients.
///////////////////////////////////////////////////////////////////////////////////
bool Base_CmdKeyValues::ReadFromBufferImpl(Base_CmdKeyValues* thisptr, bf_read* buffer)
{
// Abusable netmsg; only allow if cheats are enabled.
if (!sv_cheats->GetBool())
{
return false;
}
return Base_CmdKeyValues_ReadFromBuffer(thisptr, buffer);
}
bool Base_CmdKeyValues::WriteToBufferImpl(Base_CmdKeyValues* thisptr, bf_write* buffer)
{
// Abusable netmsg; only allow if cheats are enabled.
if (!sv_cheats->GetBool())
{
return false;
}
return Base_CmdKeyValues_WriteToBuffer(thisptr, buffer);
}
void V_NetMessages::Attach() const
{
#if !defined(DEDICATED)
auto SVCPrint = &SVC_Print::ProcessImpl;
auto SVCUserMessage = &SVC_UserMessage::ProcessImpl;
CMemory::HookVirtualMethod((uintptr_t)g_pSVC_Print_VFTable, (LPVOID&)SVCPrint, 3, (LPVOID*)&SVC_Print_Process);
CMemory::HookVirtualMethod((uintptr_t)g_pSVC_UserMessage_VFTable, (LPVOID&)SVCUserMessage, 3, (LPVOID*)&SVC_UserMessage_Process);
auto hk_SVCPrint_Process = &SVC_Print::ProcessImpl;
auto hk_SVCUserMessage_Process = &SVC_UserMessage::ProcessImpl;
auto hk_Base_CmdKeyValues_ReadFromBuffer = &Base_CmdKeyValues::ReadFromBufferImpl;
auto hk_Base_CmdKeyValues_WriteToBuffer = &Base_CmdKeyValues::WriteToBufferImpl;
CMemory::HookVirtualMethod((uintptr_t)g_pSVC_Print_VFTable, (LPVOID&)hk_SVCPrint_Process, NetMessageVtbl::Process, (LPVOID*)&SVC_Print_Process);
CMemory::HookVirtualMethod((uintptr_t)g_pSVC_UserMessage_VFTable, (LPVOID&)hk_SVCUserMessage_Process, NetMessageVtbl::Process, (LPVOID*)&SVC_UserMessage_Process);
CMemory::HookVirtualMethod((uintptr_t)g_pBase_CmdKeyValues_VFTable, (LPVOID&)hk_Base_CmdKeyValues_ReadFromBuffer, NetMessageVtbl::ReadFromBuffer, (LPVOID*)&Base_CmdKeyValues_ReadFromBuffer);
CMemory::HookVirtualMethod((uintptr_t)g_pBase_CmdKeyValues_VFTable, (LPVOID&)hk_Base_CmdKeyValues_WriteToBuffer, NetMessageVtbl::WriteToBuffer, (LPVOID*)&Base_CmdKeyValues_WriteToBuffer);
#endif // DEDICATED
}
@ -67,7 +104,9 @@ void V_NetMessages::Detach() const
{
#if !defined(DEDICATED)
void* hkRestore = nullptr;
CMemory::HookVirtualMethod((uintptr_t)g_pSVC_Print_VFTable, (LPVOID)SVC_Print_Process, 3, (LPVOID*)&hkRestore);
CMemory::HookVirtualMethod((uintptr_t)g_pSVC_UserMessage_VFTable, (LPVOID)SVC_UserMessage_Process, 3, (LPVOID*)&hkRestore);
CMemory::HookVirtualMethod((uintptr_t)g_pSVC_Print_VFTable, (LPVOID)SVC_Print_Process, NetMessageVtbl::Process, (LPVOID*)&hkRestore);
CMemory::HookVirtualMethod((uintptr_t)g_pSVC_UserMessage_VFTable, (LPVOID)SVC_UserMessage_Process, NetMessageVtbl::Process, (LPVOID*)&hkRestore);
CMemory::HookVirtualMethod((uintptr_t)g_pBase_CmdKeyValues_VFTable, (LPVOID)Base_CmdKeyValues_ReadFromBuffer, NetMessageVtbl::ReadFromBuffer, (LPVOID*)&hkRestore);
CMemory::HookVirtualMethod((uintptr_t)g_pBase_CmdKeyValues_VFTable, (LPVOID)Base_CmdKeyValues_WriteToBuffer, NetMessageVtbl::WriteToBuffer, (LPVOID*)&hkRestore);
#endif // DEDICATED
}

View File

@ -22,6 +22,7 @@
class CNetChan;
class SVC_Print;
class SVC_UserMessage;
class Base_CmdKeyValues;
//-------------------------------------------------------------------------
// MM_HEARTBEAT
@ -45,6 +46,13 @@ inline void* g_pSVC_UserMessage_VFTable = nullptr;
//-------------------------------------------------------------------------
inline void* g_pSVC_ServerTick_VFTable = nullptr;
//-------------------------------------------------------------------------
// Base_CmdKeyValues
//-------------------------------------------------------------------------
inline auto Base_CmdKeyValues_ReadFromBuffer = CMemory().RCast<bool(*)(Base_CmdKeyValues* thisptr, bf_read* buffer)>();
inline auto Base_CmdKeyValues_WriteToBuffer = CMemory().RCast<bool(*)(Base_CmdKeyValues* thisptr, bf_write* buffer)>();
inline void* g_pBase_CmdKeyValues_VFTable = nullptr;
//-------------------------------------------------------------------------
// Enumeration of the INetMessage class
//-------------------------------------------------------------------------
@ -189,12 +197,50 @@ public:
nettick_t m_NetTick;
};
///////////////////////////////////////////////////////////////////////////////////////
// Client messages:
///////////////////////////////////////////////////////////////////////////////////////
struct NET_StringCmd : CNetMessage
{
const char* cmd;
char buffer[1024];
};
///////////////////////////////////////////////////////////////////////////////////////
// This message is subclassed by 'SVC_CmdKeyValues' and 'CLC_CmdKeyValues'
class Base_CmdKeyValues : public CNetMessage
{
public:
virtual ~Base_CmdKeyValues() {};
virtual void SetNetChannel(CNetChan* netchan) = 0;
virtual void SetReliable(bool state) = 0;
virtual bool Process(void) = 0;
virtual bool ReadFromBuffer(bf_read* buffer) = 0;
static bool ReadFromBufferImpl(Base_CmdKeyValues* thisptr, bf_read* buffer);
virtual bool WriteToBuffer(bf_write* buffer) = 0;
static bool WriteToBufferImpl(Base_CmdKeyValues* thisptr, bf_write* buffer);
virtual bool IsReliable(void) const = 0;
virtual int GetGroup(void) const = 0;
virtual int GetType(void) const = 0;
virtual const char* GetName(void) const = 0;
virtual CNetChan* GetNetChannel(void) const = 0;
virtual const char* ToString(void) const = 0;
virtual size_t GetSize(void) const = 0;
int m_nMsgType;
int m_nLength; // data length in bits
bf_read m_DataIn;
bf_write m_DataOut;
};
///////////////////////////////////////////////////////////////////////////////
class V_NetMessages : public IDetour
{
@ -203,6 +249,7 @@ class V_NetMessages : public IDetour
LogConAdr("SVC_Print::`vftable'", reinterpret_cast<uintptr_t>(g_pSVC_Print_VFTable));
LogConAdr("SVC_UserMessage::`vftable'", reinterpret_cast<uintptr_t>(g_pSVC_UserMessage_VFTable));
LogConAdr("SVC_ServerTick::`vftable'", reinterpret_cast<uintptr_t>(g_pSVC_ServerTick_VFTable));
LogConAdr("Base_CmdKeyValues::`vftable'", reinterpret_cast<uintptr_t>(g_pBase_CmdKeyValues_VFTable));
LogFunAdr("MM_Heartbeat::ToString", MM_Heartbeat__ToString.GetPtr());
}
virtual void GetFun(void) const
@ -217,6 +264,7 @@ class V_NetMessages : public IDetour
g_pSVC_Print_VFTable = g_GameDll.GetVirtualMethodTable(".?AVSVC_Print@@");
g_pSVC_UserMessage_VFTable = g_GameDll.GetVirtualMethodTable(".?AVSVC_UserMessage@@");
g_pSVC_ServerTick_VFTable = g_GameDll.GetVirtualMethodTable(".?AVSVC_ServerTick@@");
g_pBase_CmdKeyValues_VFTable = g_GameDll.GetVirtualMethodTable(".?AVBase_CmdKeyValues@@");
}
virtual void Attach(void) const;
virtual void Detach(void) const;