Vulnerability: FreeRDP FreeRDP 2.0.0-rc3 released version before commit 205c612820dac644d665b5bb1cdf437dc5ca01e3 contains a Other/Unknown vulnerability in channels/drdynvc/client/drdynvc_main.c, drdynvc_process_capability_request that can result in The RDP server can read the client's memory.. This attack appear to be exploitable via RDPClient must connect the rdp server with echo option. This vulnerability appears to have been fixed in after commit 205c612820dac644d665b5bb1cdf437dc5ca01e3.
Vulnerability ID: CVE-2018-1000852
CWE: CWE-125
Severity score: 4.9
Project: https://github.com/FreeRDP/FreeRDP
Published: 2018-12-20
File: channels/drdynvc/client/drdynvc_main.c
Commit message: Fix for #4866: Added additional length checks
Commit author: Armin Novak

Vulnerable version (before fix):
 static UINT dvcman_receive_channel_data(drdynvcPlugin* drdynvc, 	if (channel->dvc_data) 	{ 		/* Fragmented data */ 		if (Stream_GetPosition(channel->dvc_data) + dataSize > (UINT32) Stream_Capacity( 		        channel->dvc_data)) 		{ 			WLog_Print(drdynvc->log, WLOG_ERROR, "data exceeding declared length!"); 			Stream_Release(channel->dvc_data); 			channel->dvc_data = NULL; 			return ERROR_INVALID_DATA; 		}  		Stream_Write(channel->dvc_data, Stream_Pointer(data), dataSize);  		if (Stream_GetPosition(channel->dvc_data) >= channel->dvc_data_length) 		{ static UINT drdynvc_process_capability_request(drdynvcPlugin* drdynvc, int Sp, 	if (!drdynvc) 		return CHANNEL_RC_BAD_INIT_HANDLE;  	WLog_Print(drdynvc->log, WLOG_TRACE, "capability_request Sp=%d cbChId=%d", Sp, cbChId); 	Stream_Seek(s, 1); /* pad */ 	Stream_Read_UINT16(s, drdynvc->version); static UINT drdynvc_process_capability_request(drdynvcPlugin* drdynvc, int Sp, 	 */ 	if ((drdynvc->version == 2) || (drdynvc->version == 3)) 	{ 		Stream_Read_UINT16(s, drdynvc->PriorityCharge0); 		Stream_Read_UINT16(s, drdynvc->PriorityCharge1); 		Stream_Read_UINT16(s, drdynvc->PriorityCharge2); static UINT drdynvc_process_capability_request(drdynvcPlugin* drdynvc, int Sp, 	return status; }  static UINT32 drdynvc_read_variable_uint(wStream* s, int cbLen) { 	UINT32 val; static UINT drdynvc_process_create_request(drdynvcPlugin* drdynvc, int Sp, 	UINT32 ChannelId; 	wStream* data_out; 	UINT channel_status;  	if (!drdynvc) 		return CHANNEL_RC_BAD_CHANNEL_HANDLE; static UINT drdynvc_process_create_request(drdynvcPlugin* drdynvc, int Sp, 		drdynvc->state = DRDYNVC_STATE_READY; 	}  	ChannelId = drdynvc_read_variable_uint(s, cbChId); 	pos = Stream_GetPosition(s); 	WLog_Print(drdynvc->log, WLOG_DEBUG, "process_create_request: ChannelId=%"PRIu32" ChannelName=%s", 	           ChannelId, 	           Stream_Pointer(s)); 	channel_status = dvcman_create_channel(drdynvc, drdynvc->channel_mgr, ChannelId, 	                                       (char*) Stream_Pointer(s)); 	data_out = Stream_New(NULL, pos + 4);  	if (!data_out) static UINT drdynvc_process_data_first(drdynvcPlugin* drdynvc, int Sp, 	UINT status; 	UINT32 Length; 	UINT32 ChannelId; 	ChannelId = drdynvc_read_variable_uint(s, cbChId); 	Length = drdynvc_read_variable_uint(s, Sp); 	WLog_Print(drdynvc->log, WLOG_DEBUG, static UINT drdynvc_process_data(drdynvcPlugin* drdynvc, int Sp, int cbChId,                                  wStream* s) { 	UINT32 ChannelId; 	ChannelId = drdynvc_read_variable_uint(s, cbChId); 	WLog_Print(drdynvc->log, WLOG_TRACE, "process_data: Sp=%d cbChId=%d, ChannelId=%"PRIu32"", Sp, 	           cbChId, static UINT drdynvc_process_close_request(drdynvcPlugin* drdynvc, int Sp, 	UINT error; 	UINT32 ChannelId; 	wStream* data_out; 	ChannelId = drdynvc_read_variable_uint(s, cbChId); 	WLog_Print(drdynvc->log, WLOG_DEBUG, "process_close_request: Sp=%d cbChId=%d, ChannelId=%"PRIu32"", 	           Sp, static UINT drdynvc_order_recv(drdynvcPlugin* drdynvc, wStream* s) 	int Cmd; 	int Sp; 	int cbChId; 	Stream_Read_UINT8(s, value); 	Cmd = (value & 0xf0) >> 4; 	Sp = (value & 0x0c) >> 2; static UINT drdynvc_virtual_channel_event_data_received(drdynvcPlugin* drdynvc, 		return CHANNEL_RC_NO_MEMORY; 	}  	if (!Stream_EnsureRemainingCapacity(data_in, (int) dataLength)) 	{ 		WLog_Print(drdynvc->log, WLOG_ERROR, "Stream_EnsureRemainingCapacity failed!"); 		Stream_Free(drdynvc->data_in, TRUE);

Fixed version (after fix):
 static UINT dvcman_receive_channel_data(drdynvcPlugin* drdynvc, 	if (channel->dvc_data) 	{ 		/* Fragmented data */ 		if (Stream_GetPosition(channel->dvc_data) + dataSize > Stream_Capacity(channel->dvc_data)) 		{ 			WLog_Print(drdynvc->log, WLOG_ERROR, "data exceeding declared length!"); 			Stream_Release(channel->dvc_data); 			channel->dvc_data = NULL; 			return ERROR_INVALID_DATA; 		}  		Stream_Copy(data, channel->dvc_data, dataSize);  		if (Stream_GetPosition(channel->dvc_data) >= channel->dvc_data_length) 		{ static UINT drdynvc_process_capability_request(drdynvcPlugin* drdynvc, int Sp, 	if (!drdynvc) 		return CHANNEL_RC_BAD_INIT_HANDLE;  	if (Stream_GetRemainingLength(s) < 3) 		return ERROR_INVALID_DATA;  	WLog_Print(drdynvc->log, WLOG_TRACE, "capability_request Sp=%d cbChId=%d", Sp, cbChId); 	Stream_Seek(s, 1); /* pad */ 	Stream_Read_UINT16(s, drdynvc->version); static UINT drdynvc_process_capability_request(drdynvcPlugin* drdynvc, int Sp, 	 */ 	if ((drdynvc->version == 2) || (drdynvc->version == 3)) 	{ 		if (Stream_GetRemainingLength(s) < 8) 			return ERROR_INVALID_DATA;  		Stream_Read_UINT16(s, drdynvc->PriorityCharge0); 		Stream_Read_UINT16(s, drdynvc->PriorityCharge1); 		Stream_Read_UINT16(s, drdynvc->PriorityCharge2); static UINT drdynvc_process_capability_request(drdynvcPlugin* drdynvc, int Sp, 	return status; }  static UINT32 drdynvc_cblen_to_bytes(int cbLen) { 	switch (cbLen) 	{ 		case 0: 			return 1;  		case 1: 			return 2;  		default: 			return 4; 	} }  static UINT32 drdynvc_read_variable_uint(wStream* s, int cbLen) { 	UINT32 val; static UINT drdynvc_process_create_request(drdynvcPlugin* drdynvc, int Sp, 	UINT32 ChannelId; 	wStream* data_out; 	UINT channel_status; 	char* name; 	size_t length;  	if (!drdynvc) 		return CHANNEL_RC_BAD_CHANNEL_HANDLE; static UINT drdynvc_process_create_request(drdynvcPlugin* drdynvc, int Sp, 		drdynvc->state = DRDYNVC_STATE_READY; 	}  	if (Stream_GetRemainingLength(s) < drdynvc_cblen_to_bytes(cbChId)) 		return ERROR_INVALID_DATA;  	ChannelId = drdynvc_read_variable_uint(s, cbChId); 	pos = Stream_GetPosition(s); 	name = Stream_Pointer(s); 	length = Stream_GetRemainingLength(s);  	if (strnlen(name, length) >= length) 		return ERROR_INVALID_DATA;  	WLog_Print(drdynvc->log, WLOG_DEBUG, "process_create_request: ChannelId=%"PRIu32" ChannelName=%s", 	           ChannelId, name); 	channel_status = dvcman_create_channel(drdynvc, drdynvc->channel_mgr, ChannelId, name); 	data_out = Stream_New(NULL, pos + 4);  	if (!data_out) static UINT drdynvc_process_data_first(drdynvcPlugin* drdynvc, int Sp, 	UINT status; 	UINT32 Length; 	UINT32 ChannelId;  	if (Stream_GetRemainingLength(s) < drdynvc_cblen_to_bytes(cbChId) + drdynvc_cblen_to_bytes(Sp)) 		return ERROR_INVALID_DATA;  	ChannelId = drdynvc_read_variable_uint(s, cbChId); 	Length = drdynvc_read_variable_uint(s, Sp); 	WLog_Print(drdynvc->log, WLOG_DEBUG, static UINT drdynvc_process_data(drdynvcPlugin* drdynvc, int Sp, int cbChId,                                  wStream* s) { 	UINT32 ChannelId;  	if (Stream_GetRemainingLength(s) < drdynvc_cblen_to_bytes(cbChId)) 		return ERROR_INVALID_DATA;  	ChannelId = drdynvc_read_variable_uint(s, cbChId); 	WLog_Print(drdynvc->log, WLOG_TRACE, "process_data: Sp=%d cbChId=%d, ChannelId=%"PRIu32"", Sp, 	           cbChId, static UINT drdynvc_process_close_request(drdynvcPlugin* drdynvc, int Sp, 	UINT error; 	UINT32 ChannelId; 	wStream* data_out;  	if (Stream_GetRemainingLength(s) < drdynvc_cblen_to_bytes(cbChId)) 		return ERROR_INVALID_DATA;  	ChannelId = drdynvc_read_variable_uint(s, cbChId); 	WLog_Print(drdynvc->log, WLOG_DEBUG, "process_close_request: Sp=%d cbChId=%d, ChannelId=%"PRIu32"", 	           Sp, static UINT drdynvc_order_recv(drdynvcPlugin* drdynvc, wStream* s) 	int Cmd; 	int Sp; 	int cbChId;  	if (Stream_GetRemainingLength(s) < 1) 		return ERROR_INVALID_DATA;  	Stream_Read_UINT8(s, value); 	Cmd = (value & 0xf0) >> 4; 	Sp = (value & 0x0c) >> 2; static UINT drdynvc_virtual_channel_event_data_received(drdynvcPlugin* drdynvc, 		return CHANNEL_RC_NO_MEMORY; 	}  	if (!Stream_EnsureRemainingCapacity(data_in, dataLength)) 	{ 		WLog_Print(drdynvc->log, WLOG_ERROR, "Stream_EnsureRemainingCapacity failed!"); 		Stream_Free(drdynvc->data_in, TRUE);

Patch:
patch: @@ -639,16 +639,15 @@ static UINT dvcman_receive_channel_data(drdynvcPlugin* drdynvc,
 	if (channel->dvc_data)
 	{
 		/* Fragmented data */
-		if (Stream_GetPosition(channel->dvc_data) + dataSize > (UINT32) Stream_Capacity(
-		        channel->dvc_data))
+		if (Stream_GetPosition(channel->dvc_data) + dataSize > Stream_Capacity(channel->dvc_data))
 		{
 			WLog_Print(drdynvc->log, WLOG_ERROR, "data exceeding declared length!");
 			Stream_Release(channel->dvc_data);
 			channel->dvc_data = NULL;
 			return ERROR_INVALID_DATA;
 		}
 
-		Stream_Write(channel->dvc_data, Stream_Pointer(data), dataSize);
+		Stream_Copy(data, channel->dvc_data, dataSize);
 
 		if (Stream_GetPosition(channel->dvc_data) >= channel->dvc_data_length)
 		{
@@ -880,6 +879,9 @@ static UINT drdynvc_process_capability_request(drdynvcPlugin* drdynvc, int Sp,
 	if (!drdynvc)
 		return CHANNEL_RC_BAD_INIT_HANDLE;
 
+	if (Stream_GetRemainingLength(s) < 3)
+		return ERROR_INVALID_DATA;
+
 	WLog_Print(drdynvc->log, WLOG_TRACE, "capability_request Sp=%d cbChId=%d", Sp, cbChId);
 	Stream_Seek(s, 1); /* pad */
 	Stream_Read_UINT16(s, drdynvc->version);
@@ -889,6 +891,9 @@ static UINT drdynvc_process_capability_request(drdynvcPlugin* drdynvc, int Sp,
 	 */
 	if ((drdynvc->version == 2) || (drdynvc->version == 3))
 	{
+		if (Stream_GetRemainingLength(s) < 8)
+			return ERROR_INVALID_DATA;
+
 		Stream_Read_UINT16(s, drdynvc->PriorityCharge0);
 		Stream_Read_UINT16(s, drdynvc->PriorityCharge1);
 		Stream_Read_UINT16(s, drdynvc->PriorityCharge2);
@@ -900,6 +905,21 @@ static UINT drdynvc_process_capability_request(drdynvcPlugin* drdynvc, int Sp,
 	return status;
 }
 
+static UINT32 drdynvc_cblen_to_bytes(int cbLen)
+{
+	switch (cbLen)
+	{
+		case 0:
+			return 1;
+
+		case 1:
+			return 2;
+
+		default:
+			return 4;
+	}
+}
+
 static UINT32 drdynvc_read_variable_uint(wStream* s, int cbLen)
 {
 	UINT32 val;
@@ -935,6 +955,8 @@ static UINT drdynvc_process_create_request(drdynvcPlugin* drdynvc, int Sp,
 	UINT32 ChannelId;
 	wStream* data_out;
 	UINT channel_status;
+	char* name;
+	size_t length;
 
 	if (!drdynvc)
 		return CHANNEL_RC_BAD_CHANNEL_HANDLE;
@@ -957,13 +979,20 @@ static UINT drdynvc_process_create_request(drdynvcPlugin* drdynvc, int Sp,
 		drdynvc->state = DRDYNVC_STATE_READY;
 	}
 
+	if (Stream_GetRemainingLength(s) < drdynvc_cblen_to_bytes(cbChId))
+		return ERROR_INVALID_DATA;
+
 	ChannelId = drdynvc_read_variable_uint(s, cbChId);
 	pos = Stream_GetPosition(s);
+	name = Stream_Pointer(s);
+	length = Stream_GetRemainingLength(s);
+
+	if (strnlen(name, length) >= length)
+		return ERROR_INVALID_DATA;
+
 	WLog_Print(drdynvc->log, WLOG_DEBUG, "process_create_request: ChannelId=%"PRIu32" ChannelName=%s",
-	           ChannelId,
-	           Stream_Pointer(s));
-	channel_status = dvcman_create_channel(drdynvc, drdynvc->channel_mgr, ChannelId,
-	                                       (char*) Stream_Pointer(s));
+	           ChannelId, name);
+	channel_status = dvcman_create_channel(drdynvc, drdynvc->channel_mgr, ChannelId, name);
 	data_out = Stream_New(NULL, pos + 4);
 
 	if (!data_out)
@@ -1024,6 +1053,10 @@ static UINT drdynvc_process_data_first(drdynvcPlugin* drdynvc, int Sp,
 	UINT status;
 	UINT32 Length;
 	UINT32 ChannelId;
+
+	if (Stream_GetRemainingLength(s) < drdynvc_cblen_to_bytes(cbChId) + drdynvc_cblen_to_bytes(Sp))
+		return ERROR_INVALID_DATA;
+
 	ChannelId = drdynvc_read_variable_uint(s, cbChId);
 	Length = drdynvc_read_variable_uint(s, Sp);
 	WLog_Print(drdynvc->log, WLOG_DEBUG,
@@ -1047,6 +1080,10 @@ static UINT drdynvc_process_data(drdynvcPlugin* drdynvc, int Sp, int cbChId,
                                  wStream* s)
 {
 	UINT32 ChannelId;
+
+	if (Stream_GetRemainingLength(s) < drdynvc_cblen_to_bytes(cbChId))
+		return ERROR_INVALID_DATA;
+
 	ChannelId = drdynvc_read_variable_uint(s, cbChId);
 	WLog_Print(drdynvc->log, WLOG_TRACE, "process_data: Sp=%d cbChId=%d, ChannelId=%"PRIu32"", Sp,
 	           cbChId,
@@ -1066,6 +1103,10 @@ static UINT drdynvc_process_close_request(drdynvcPlugin* drdynvc, int Sp,
 	UINT error;
 	UINT32 ChannelId;
 	wStream* data_out;
+
+	if (Stream_GetRemainingLength(s) < drdynvc_cblen_to_bytes(cbChId))
+		return ERROR_INVALID_DATA;
+
 	ChannelId = drdynvc_read_variable_uint(s, cbChId);
 	WLog_Print(drdynvc->log, WLOG_DEBUG, "process_close_request: Sp=%d cbChId=%d, ChannelId=%"PRIu32"",
 	           Sp,
@@ -1108,6 +1149,10 @@ static UINT drdynvc_order_recv(drdynvcPlugin* drdynvc, wStream* s)
 	int Cmd;
 	int Sp;
 	int cbChId;
+
+	if (Stream_GetRemainingLength(s) < 1)
+		return ERROR_INVALID_DATA;
+
 	Stream_Read_UINT8(s, value);
 	Cmd = (value & 0xf0) >> 4;
 	Sp = (value & 0x0c) >> 2;
@@ -1166,7 +1211,7 @@ static UINT drdynvc_virtual_channel_event_data_received(drdynvcPlugin* drdynvc,
 		return CHANNEL_RC_NO_MEMORY;
 	}
 
-	if (!Stream_EnsureRemainingCapacity(data_in, (int) dataLength))
+	if (!Stream_EnsureRemainingCapacity(data_in, dataLength))
 	{
 		WLog_Print(drdynvc->log, WLOG_ERROR, "Stream_EnsureRemainingCapacity failed!");
 		Stream_Free(drdynvc->data_in, TRUE);
path: channels/drdynvc/client/drdynvc_main.c