CVE-2023-39354 (published 2023-08-31T20:15Z)

FreeRDP is a free implementation of the Remote Desktop Protocol (RDP), released under the Apache license. Affected versions are subject to an Out-Of-Bounds Read in the `nsc_rle_decompress_data` function. The Out-Of-Bounds Read occurs because it processes `context->Planes` without  checking if it contains data of sufficient length. Should an attacker be able to leverage this vulnerability they may be able to cause a crash. This issue has been addressed in versions 2.11.0 and 3.0.0-beta3. Users are advised to upgrade. There are no known workarounds for this vulnerability.

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

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

Fix information:
repo_url: https://github.com/FreeRDP/FreeRDP
commit_details: repo_url: https://github.com/FreeRDP/FreeRDP
author: akallabeth
author_date: 2023-08-22 10:48:57+02:00
msg: [codec,nsc] fix input length validation
repository: repo_url: https://github.com/FreeRDP/FreeRDP
repo_name: FreeRDP/FreeRDP
description: FreeRDP is a free remote desktop protocol library and clients
repo_language: C
owner: FreeRDP
file_changes: filename: nsc.c
change_type: ModificationType.MODIFY
diff: @@ -112,12 +112,17 @@ static BOOL nsc_decode(NSC_CONTEXT* context)
 	return TRUE;
 }
 
-static BOOL nsc_rle_decode(BYTE* in, BYTE* out, UINT32 outSize, UINT32 originalSize)
+static BOOL nsc_rle_decode(const BYTE* in, size_t inSize, BYTE* out, UINT32 outSize,
+                           UINT32 originalSize)
 {
 	UINT32 left = originalSize;
 
 	while (left > 4)
 	{
+		if (inSize < 1)
+			return FALSE;
+		inSize--;
+
 		const BYTE value = *in++;
 		UINT32 len = 0;
 
@@ -130,17 +135,26 @@ static BOOL nsc_rle_decode(BYTE* in, BYTE* out, UINT32 outSize, UINT32 originalS
 			*out++ = value;
 			left--;
 		}
+		else if (inSize < 1)
+			return FALSE;
 		else if (value == *in)
 		{
+			inSize--;
 			in++;
 
-			if (*in < 0xFF)
+			if (inSize < 1)
+				return FALSE;
+			else if (*in < 0xFF)
 			{
+				inSize--;
 				len = (UINT32)*in++;
 				len += 2;
 			}
 			else
 			{
+				if (inSize < 5)
+					return FALSE;
+				inSize -= 5;
 				in++;
 				len = ((UINT32)(*in++));
 				len |= ((UINT32)(*in++)) << 8U;
@@ -170,6 +184,8 @@ static BOOL nsc_rle_decode(BYTE* in, BYTE* out, UINT32 outSize, UINT32 originalS
 	if ((outSize < 4) || (left < 4))
 		return FALSE;
 
+	if (inSize < 4)
+		return FALSE;
 	memcpy(out, in, 4);
 	return TRUE;
 }
@@ -179,7 +195,8 @@ static BOOL nsc_rle_decompress_data(NSC_CONTEXT* context)
 	if (!context)
 		return FALSE;
 
-	BYTE* rle = context->Planes;
+	const BYTE* rle = context->Planes;
+	size_t rleSize = context->PlanesSize;
 	WINPR_ASSERT(rle);
 
 	for (size_t i = 0; i < 4; i++)
@@ -187,6 +204,9 @@ static BOOL nsc_rle_decompress_data(NSC_CONTEXT* context)
 		const UINT32 originalSize = context->OrgByteCount[i];
 		const UINT32 planeSize = context->PlaneByteCount[i];
 
+		if (rleSize < planeSize)
+			return FALSE;
+
 		if (planeSize == 0)
 		{
 			if (context->priv->PlaneBuffersLength < originalSize)
@@ -196,7 +216,7 @@ static BOOL nsc_rle_decompress_data(NSC_CONTEXT* context)
 		}
 		else if (planeSize < originalSize)
 		{
-			if (!nsc_rle_decode(rle, context->priv->PlaneBuffers[i],
+			if (!nsc_rle_decode(rle, rleSize, context->priv->PlaneBuffers[i],
 			                    context->priv->PlaneBuffersLength, originalSize))
 				return FALSE;
 		}
@@ -205,6 +225,9 @@ static BOOL nsc_rle_decompress_data(NSC_CONTEXT* context)
 			if (context->priv->PlaneBuffersLength < originalSize)
 				return FALSE;
 
+			if (rleSize < originalSize)
+				return FALSE;
+
 			CopyMemory(context->priv->PlaneBuffers[i], rle, originalSize);
 		}
 
@@ -232,6 +255,7 @@ static BOOL nsc_stream_initialize(NSC_CONTEXT* context, wStream* s)
 	Stream_Read_UINT8(s, context->ChromaSubsamplingLevel); /* ChromaSubsamplingLevel (1 byte) */
 	Stream_Seek(s, 2);                                     /* Reserved (2 bytes) */
 	context->Planes = Stream_Pointer(s);
+	context->PlanesSize = total;
 	return Stream_CheckAndLogRequiredLengthWLog(context->priv->log, s, total);
 }
 

programming_language: C
filename: nsc_types.h
change_type: ModificationType.MODIFY
diff: @@ -57,6 +57,7 @@ struct S_NSC_CONTEXT
 	UINT32 BitmapDataLength;
 
 	BYTE* Planes;
+	size_t PlanesSize;
 	UINT32 PlaneByteCount[4];
 	UINT32 ColorLossLevel;
 	UINT32 ChromaSubsamplingLevel;

programming_language: C