近日关注到 MSRC 发布了一个RDC组件的CVE,花了点时间看了看。
Remote Desktop Protocol Client Information Disclosure Vulnerability
CWE-125: Out-of-bounds Read
漏洞在 CCO::OnSaveSessionInfoPDU 上,
CCO::OnSaveSessionInfoPDU(CCO *this, tagTS_SAVE_SESSION_INFO_PDU_DATA *pData, ULONG cbData)
pData 实际对应于消息结构
tagTS_SAVE_SESSION_INFO_PDU_DATA
参考 spec
得到以下
struct __unaligned tagTS_SAVE_SESSION_INFO_PDU_DATA
{
UINT32 infoType;
union {
struct __unaligned tagTS_LOGON_INFO_VERSION_2
{
UINT16 Version;
UINT32 Size;
UINT32 SessionId;
UINT32 cbDomain;
UINT32 cbUserName;
CHAR Pad[558];
// WCHAR Domain[cbDomain/sizeof(WCHAR)];
// WCHAR UserName[cbUserName/sizeof(WCHAR)];
} LogonLongData;
struct __unaligned tagTS_LOGON_INFO_VERSION_1
{
UINT32 cbDomain;
WCHAR Domain[26];
UINT32 cbUserName;
WCHAR UserName[256];
UINT32 SessionId;
} LogonData;
};
};
当 infoType == 1 时,有以下处理逻辑:
CCO::OnSaveSessionInfoPDU(CCO *this, tagTS_SAVE_SESSION_INFO_PDU_DATA *pData, ULONG cbData)
{
// ...
WCHAR wszDomain[256];
size_t cbDomain;
WCHAR wszUserName[256];
size_t cbUserName;
cbDomain = pData->LogonLongData.cbDomain;
cbUserName = pData->LogonLongData.cbUserName;
if ( cbUserName + cbDomain + 0x16 > cbData || (uint32_t)cbDomain > 0x200 || (uint32_t)cbUserName > 0x200 )
{
// ...
goto FAILED;
}
memset(wszDomain, 0, sizeof(wszDomain));
memset(wszUserName, 0, sizeof(wszUserName));
memcpy(wszDomain, (char*)pData + 0x224, cbDomain);
memcpy(wszUserName, (char*)pData + 0x224 + cbDomain, cbUserName);
}
这里的 0x224 对应于 tagTS_LOGON_INFO_VERSION_2 中的 Domain 字段的偏移位置。
乍一看似乎很正常,对 cbDomain cbUserName 进行了约束,同时也校验了数据包的大小。
但是问题恰恰出在校验上:cbUserName + cbDomain + 0x16 > cbData
这里的 0x16 实际并不是 tagTS_LOGON_INFO_VERSION_2 除 variables 外所有字段的固定大小,而是 tagTS_LOGON_INFO_VERSION_1 的大小。
这导致校验存在一个内存空域,
指定:
infoType = 1;
cbDomain = 0x200;
cbUserName = 0x200;
// ...
Domain[0x200/sizeof(WCHAR)];
UserName[0x8/sizeof(WCHAR)];
此时
cbData
为 0x244 + 0x200 + 0x8 = 0x44c带入校验
cbUserName + cbDomain + 0x16 > cbData
=> 0x200 + 0x200 + 0x16 > 0x44c
=> true
(uint32_t)cbDomain > 0x200
=> 0x200 > 0x200
=> true
(uint32_t)cbUserName > 0x200
=> 0x200 > 0x200
=> true
满足条件,接下来进入复制阶段
memcpy(wszUserName, (char*)pData + 0x224 + cbDomain, cbUserName);
此时有以下事实
cbDomain = 0x200;
cbUserName = 0x200;
(char*)pData + 0x224 + 0x200
=> (char*)pData + 0x424
看到复制
pData
范围在 [0x424, 0x424+0x200]
但 pData 实际大小为
0x44c
,发生越界读取。修补
修正约束条件。
核心条件:
cbUserName + cbDomain + 0x244 > cbData