July 1, 2020

GHSL-2020-103: OOB read vulnerability in FreeRDP license_read_new_or_upgrade_license_packet - CVE-2020-11099

Antonio Morales

Summary

An out-of-bounds (OOB) read vulnerability has been detected in FreeRDP's license_read_new_or_upgrade_license_packet function due to an incorrect handling of data blob sizes.

Product

FreeRDP

Tested Version

Development version - master branch (May 21, 2020)

Details: Out-of-bound read in license_read_new_or_upgrade_license_packet

The license_read_new_or_upgrade_license_packet function in license.c performs a call to Stream_Read_UINT16(licenseStream, os_minor) (line 1255), where licenseStream is a wStream* whose size can be controlled indirectly by a potential attacker.

Stream_Read_ are a family of macros that read the given amount of bits from the specified wStream and then move the stream's current position that many bits forward:

View on GitHub!

/* winpr/include/winpr/stream.h */

#define _stream_read_n16_le(_t, _s, _v, _p)                                      \
	do                                                                           \
	{                                                                            \
		(_v) = (_t)((*(_s)->pointer) + (((UINT16)(*((_s)->pointer + 1))) << 8)); \
		if (_p)                                                                  \
			Stream_Seek(_s, sizeof(_t));                                         \
	} while (0)

...

#define Stream_Read_UINT16(_s, _v) _stream_read_n16_le(UINT16, _s, _v, TRUE)

As we mentioned before, the licenseStream size can be controlled by an attacker. This is because in the RDP protocol, a data blob's length is read as a separate field:

View on GitHub!

/* libfreerdp/core/license.c */

static BOOL license_read_encrypted_blob(const rdpLicense* license, wStream* s, LICENSE_BLOB* target)
{
...

  Stream_Read_UINT16(s, wBlobLen);

...
}

Since there is no logic that range checks wBlobLen, a malicious server could send a small value (even zero). This would result in an abnormally small "Stream_New" allocation (calBlob->length = wBlobLen):

View on GitHub!

/* libfreerdp/core/license.c */

licenseStream = Stream_New(calBlob->data, calBlob->length);

As seen above licenseStream points to calBlob->data, and given that Stream_Read_ functions seek the read pointer, the subsequent calls to Stream_Read_ could move the pointer far beyond the array's limits:

View on GitHub!

/* libfreerdp/core/license.c */

...

 Stream_Read_UINT16(licenseStream, os_minor);
 Stream_Read_UINT16(licenseStream, os_major);

 /* Scope */
 Stream_Read_UINT32(licenseStream, cbScope);
...

As a result, OOB reads can occur resulting in accessing a memory location that is outside of the boundaries of the calBlob->data memory region.

Impact

This issue may lead to Out-of-Bounds read.

CVE

  • CVE-2020-11099

Coordinated Disclosure Timeline

This report was subject to the GHSL coordinated disclosure policy.

  • 05/21/2020: Vendor contacted
  • 05/26/2020: Vendor acknowledges report
  • 06/22/2020: Bug fixed and patch released by the vendor

Supporting Resources

Credit

This issue was discovered and reported by GHSL team member @antonio-morales (Antonio Morales).

Contact

You can contact the GHSL team at securitylab@github.com, please include the GHSL-2020-103 in any communication regarding this issue.