CVE-2020-6018 (published 2020-12-02T01:15Z)

Valve's Game Networking Sockets prior to version v1.2.0 improperly handles long encrypted messages in function AES_GCM_DecryptContext::Decrypt() when compiled using libsodium, leading to a Stack-Based Buffer Overflow and resulting in a memory corruption and possibly even a remote code execution.

Severity: HIGH
CVSS v2 base score: 7.5 (AV:N/AC:L/Au:N/C:P/I:P/A:P)
CVSS v3 base score: 9.8, CRITICAL (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H)

Weakness information:
cwe_id: CWE-787
cwe_name: Out-of-bounds Write
description: The product writes data past the end, or before the beginning, of the intended buffer.
extended_description: 
            
            
             
			 
         
         

Fix information:
repo_url: https://github.com/ValveSoftware/GameNetworkingSockets
commit_details: repo_url: https://github.com/ValveSoftware/GameNetworkingSockets
author: Fletcher Dunn
author_date: 2020-09-03 15:05:55-07:00
msg: Check if output buffer is too small.

It really seems like libsodium (whose entire purpose is to make crypto
idiot-proof) making me mess with these details is a flaw in the API design.

Also, correct Hungarian.
repository: repo_url: https://github.com/ValveSoftware/GameNetworkingSockets
repo_name: ValveSoftware/GameNetworkingSockets
description: Reliable & unreliable messages over UDP.  Robust message fragmentation & reassembly.  P2P networking / NAT traversal.  Encryption.
repo_language: C++
owner: ValveSoftware
file_changes: filename: crypto_libsodium.cpp
change_type: ModificationType.MODIFY
diff: @@ -50,10 +50,17 @@ bool AES_GCM_EncryptContext::Encrypt(
 	void *pEncryptedDataAndTag, uint32 *pcbEncryptedDataAndTag,
 	const void *pAdditionalAuthenticationData, size_t cbAuthenticationData
 ) {
-    unsigned long long pcbEncryptedDataAndTag_longlong = *pcbEncryptedDataAndTag;
 
+	// Make sure caller's buffer is big enough to hold the result.
+	if ( cbPlaintextData + crypto_aead_aes256gcm_ABYTES > *pcbEncryptedDataAndTag )
+	{
+		*pcbEncryptedDataAndTag = 0;
+		return false;
+	}
+
+    unsigned long long cbEncryptedDataAndTag_longlong;
     crypto_aead_aes256gcm_encrypt_afternm(
-		static_cast<unsigned char*>( pEncryptedDataAndTag ), &pcbEncryptedDataAndTag_longlong,
+		static_cast<unsigned char*>( pEncryptedDataAndTag ), &cbEncryptedDataAndTag_longlong,
 		static_cast<const unsigned char*>( pPlaintextData ), cbPlaintextData,
 		static_cast<const unsigned char*>(pAdditionalAuthenticationData), cbAuthenticationData,
 		nullptr,
@@ -61,7 +68,7 @@ bool AES_GCM_EncryptContext::Encrypt(
 		static_cast<const crypto_aead_aes256gcm_state*>( m_ctx )
 	);
 
-    *pcbEncryptedDataAndTag = pcbEncryptedDataAndTag_longlong;
+    *pcbEncryptedDataAndTag = cbEncryptedDataAndTag_longlong;
 
     return true;
 }
@@ -72,17 +79,23 @@ bool AES_GCM_DecryptContext::Decrypt(
 	void *pPlaintextData, uint32 *pcbPlaintextData,
 	const void *pAdditionalAuthenticationData, size_t cbAuthenticationData
 ) {
-    unsigned long long pcbPlaintextData_longlong;
-
+	// Make sure caller's buffer is big enough to hold the result
+	if ( cbEncryptedDataAndTag > *pcbPlaintextData + crypto_aead_aes256gcm_ABYTES )
+	{
+		*pcbPlaintextData = 0;
+		return false;
+	}
+
+    unsigned long long cbPlaintextData_longlong;
     const int nDecryptResult = crypto_aead_aes256gcm_decrypt_afternm(
-		static_cast<unsigned char*>( pPlaintextData ), &pcbPlaintextData_longlong,
+		static_cast<unsigned char*>( pPlaintextData ), &cbPlaintextData_longlong,
 		nullptr,
 		static_cast<const unsigned char*>( pEncryptedDataAndTag ), cbEncryptedDataAndTag,
 		static_cast<const unsigned char*>( pAdditionalAuthenticationData ), cbAuthenticationData,
 		static_cast<const unsigned char*>( pIV ), static_cast<const crypto_aead_aes256gcm_state*>( m_ctx )
 	);
 
-    *pcbPlaintextData = pcbPlaintextData_longlong;
+    *pcbPlaintextData = cbPlaintextData_longlong;
 
     return nDecryptResult == 0;
 }

programming_language: C++