CVE-2020-11097 (C, published 2020-06-22T22:15Z)
Description: value: In FreeRDP before version 2.1.2, an out of bounds read occurs resulting in accessing a memory location that is outside of the boundaries of the static array PRIMARY_DRAWING_ORDER_FIELD_BYTES. This is fixed in version 2.1.2.
Severity: MEDIUM (CVSS v3 base score: 5.4)
Weakness: CWE-125 \u2014 Out-of-bounds Read
The product reads data past the end, or before the beginning, of the intended buffer.
Repository: https://github.com/FreeRDP/FreeRDP
Files changed:
winpr/libwinpr/sspi/NTLM/ntlm_av_pairs.c
Commit message: Fixed OOB read in ntlm_av_pair_get

CVE-2020-11097 thanks to @antonio-morales for finding this.


Vulnerable code:
static const char* const AV_PAIR_STRINGS[] = {
	"MsvAvEOL",           "MsvAvNbComputerName", "MsvAvNbDomainName", "MsvAvDnsComputerName",
	"MsvAvDnsDomainName", "MsvAvDnsTreeName",    "MsvAvFlags",        "MsvAvTimestamp",
	"MsvAvRestrictions",  "MsvAvTargetName",     "MsvChannelBindings"
};
static BOOL ntlm_av_pair_check(NTLM_AV_PAIR* pAvPair, size_t cbAvPair);
static INLINE UINT16 ntlm_av_pair_get_id(const NTLM_AV_PAIR* pAvPair)
	return AvId;
static INLINE SIZE_T ntlm_av_pair_get_len(const NTLM_AV_PAIR* pAvPair)
	return AvLen;
	while (pAvPair && ntlm_av_pair_get_id(pAvPair) != MsvAvEOL)
		WLog_INFO(TAG, "\t%s AvId: %" PRIu16 " AvLen: %" PRIu16 "",
		          AV_PAIR_STRINGS[ntlm_av_pair_get_id(pAvPair)], ntlm_av_pair_get_id(pAvPair),
		          ntlm_av_pair_get_len(pAvPair));
		winpr_HexDump(TAG, WLOG_INFO, ntlm_av_pair_get_value_pointer(pAvPair),
		              ntlm_av_pair_get_len(pAvPair));
static size_t ntlm_av_pair_get_next_offset(NTLM_AV_PAIR* pAvPair)
	return ntlm_av_pair_get_len(pAvPair) + sizeof(NTLM_AV_PAIR);
static BOOL ntlm_av_pair_check(NTLM_AV_PAIR* pAvPair, size_t cbAvPair)
	if (!pAvPair || cbAvPair < sizeof(NTLM_AV_PAIR))
		return FALSE;
	return cbAvPair >= ntlm_av_pair_get_next_offset(pAvPair);
	offset = ntlm_av_pair_get_next_offset(pAvPair);
	while (pAvPair)
		UINT16 id = ntlm_av_pair_get_id(pAvPair);

	return ntlm_av_pair_add(pAvPairList, cbAvPairList, ntlm_av_pair_get_id(pAvPair),
	                        ntlm_av_pair_get_value_pointer(pAvPair), ntlm_av_pair_get_len(pAvPair));
		AvPairsValueLength += ntlm_av_pair_get_len(AvNbDomainName);
		AvPairsValueLength += ntlm_av_pair_get_len(AvNbComputerName);
		AvPairsValueLength += ntlm_av_pair_get_len(AvDnsDomainName);
		AvPairsValueLength += ntlm_av_pair_get_len(AvDnsComputerName);
		AvPairsValueLength += ntlm_av_pair_get_len(AvDnsTreeName);

Fixed code:
static BOOL ntlm_av_pair_get_next_offset(const NTLM_AV_PAIR* pAvPair, size_t size, size_t* pOffset);
static BOOL ntlm_av_pair_check_data(const NTLM_AV_PAIR* pAvPair, size_t cbAvPair, size_t size)
{
	size_t offset;
	if (!pAvPair || cbAvPair < sizeof(NTLM_AV_PAIR) + size)
		return FALSE;
	if (!ntlm_av_pair_get_next_offset(pAvPair, cbAvPair, &offset))
		return FALSE;
	return cbAvPair >= offset;
}

static const char* get_av_pair_string(UINT16 pair)
{
	switch (pair)
	{
		case MsvAvEOL:
			return "MsvAvEOL";
		case MsvAvNbComputerName:
			return "MsvAvNbComputerName";
		case MsvAvNbDomainName:
			return "MsvAvNbDomainName";
		case MsvAvDnsComputerName:
			return "MsvAvDnsComputerName";
		case MsvAvDnsDomainName:
			return "MsvAvDnsDomainName";
		case MsvAvDnsTreeName:
			return "MsvAvDnsTreeName";
		case MsvAvFlags:
			return "MsvAvFlags";
		case MsvAvTimestamp:
			return "MsvAvTimestamp";
		case MsvAvSingleHost:
			return "MsvAvSingleHost";
		case MsvAvTargetName:
			return "MsvAvTargetName";
		case MsvChannelBindings:
			return "MsvChannelBindings";
		default:
			return "UNKNOWN";
	}
}

static BOOL ntlm_av_pair_check(const NTLM_AV_PAIR* pAvPair, size_t cbAvPair);
static INLINE BOOL ntlm_av_pair_get_id(const NTLM_AV_PAIR* pAvPair, size_t size, UINT16* pair)
	if (!pAvPair || !pair)
		return FALSE;

	if (size < sizeof(NTLM_AV_PAIR))
		return FALSE;
	*pair = AvId;
	return TRUE;
static INLINE BOOL ntlm_av_pair_get_len(const NTLM_AV_PAIR* pAvPair, size_t size, size_t* pAvLen)
	if (!pAvPair)
		return FALSE;

	if (size < sizeof(NTLM_AV_PAIR))
		return FALSE;
	*pAvLen = AvLen;
	return TRUE;
	UINT16 pair;
	while (pAvPair && ntlm_av_pair_get_id(pAvPair, cbAvPair, &pair) && (pair != MsvAvEOL))
		size_t cbLen = 0;
		ntlm_av_pair_get_len(pAvPair, cbAvPair, &cbLen);

		WLog_INFO(TAG, "\t%s AvId: %" PRIu16 " AvLen: %" PRIu16 "", get_av_pair_string(pair), pair);
		winpr_HexDump(TAG, WLOG_INFO, ntlm_av_pair_get_value_pointer(pAvPair), cbLen);
static BOOL ntlm_av_pair_get_next_offset(const NTLM_AV_PAIR* pAvPair, size_t size, size_t* pOffset)
	size_t avLen;
	if (!pOffset)
		return FALSE;

	if (!ntlm_av_pair_get_len(pAvPair, size, &avLen))
		return FALSE;
	*pOffset = avLen + sizeof(NTLM_AV_PAIR);
	return TRUE;
static BOOL ntlm_av_pair_check(const NTLM_AV_PAIR* pAvPair, size_t cbAvPair)
	return ntlm_av_pair_check_data(pAvPair, cbAvPair, 0);
	if (!ntlm_av_pair_get_next_offset(pAvPair, *pcbAvPair, &offset))
		return NULL;

	UINT16 id;
	while (pAvPair && ntlm_av_pair_get_id(pAvPair, cbAvPair, &id))
	UINT16 pair;
	size_t avLen;

	if (!ntlm_av_pair_get_id(pAvPair, cbAvPair, &pair))
		return FALSE;

	if (!ntlm_av_pair_get_len(pAvPair, cbAvPair, &avLen))
		return FALSE;

	return ntlm_av_pair_add(pAvPairList, cbAvPairList, pair,
	                        ntlm_av_pair_get_value_pointer(pAvPair), avLen);
		size_t avLen;
		if (!ntlm_av_pair_get_len(AvNbDomainName, cbAvNbDomainName, &avLen))
			goto fail;
		AvPairsValueLength += avLen;
		size_t avLen;
		if (!ntlm_av_pair_get_len(AvNbComputerName, cbAvNbComputerName, &avLen))
			goto fail;
		AvPairsValueLength += avLen;
		size_t avLen;
		if (!ntlm_av_pair_get_len(AvDnsDomainName, cbAvDnsDomainName, &avLen))
			goto fail;
		AvPairsValueLength += avLen;
		size_t avLen;
		if (!ntlm_av_pair_get_len(AvDnsComputerName, cbAvDnsComputerName, &avLen))
			goto fail;
		AvPairsValueLength += avLen;
		size_t avLen;
		if (!ntlm_av_pair_get_len(AvDnsTreeName, cbAvDnsTreeName, &avLen))
			goto fail;
		AvPairsValueLength += avLen;