最近在看http.sys,必然需要分析旧的漏洞的,使用网上的poc触发了漏洞后,有了个具体概念,就写一篇简要分析,帮助大家理解
通过 POC(链接地址) 可知,漏洞是一个UAF漏洞,栈回溯也很清晰:
从栈回溯可以知道, 它是一个独立线程, 崩溃在解析Header里. 而poc也很简单"Accept-Encoding: doar-e, ftw, imo, ,", 发送一个带"Accept-Encoding"字段的请求, 包含至少一个正常值, 最后带个空值.
以下是函数UlpParseAcceptEncoding的伪代码. 分析参见注释和序号.
以下是UlFreeUnknownCodingList函数:
通过上述注释, 可以明显看到问题出在v29的链表管理上.
下面是释放前后的关系:
释放前的链表关系:

步骤7时的链表关系:

知道了根本原因, 修补也很简单, 在步骤3和步骤4之间, 把v29的指针重置一下即可. 而官方补丁也是这样做的.
根据测试来看, 在本来win10 2004已经补了CVE-2021-31166, 它的继任者 win 10 21H1, 又重新引入了这个bug, 看起来在6月(或者5月?)的补丁里又补了它, 导致有的人测试还以为CVE-2022-21907就是CVE-2021-31166, 因此github上也有很多拿CVE-2021-31166的poc当作CVE-2022-21907的poc的情况.
事实上CVE-2022-21907属于另一个未初始化的问题. 参考此处链接
问题出在UlFastSendHttpResponse函数内, 某种条件下, 让第一次UlpAllocateFastTracker申请就被释放, 然后走第二次申请,让申请的内存没有初始化重要的+80字段的内容, 然后在后续的UlGenerateFixedHeaders中失败, 直接跳转到MmUnmapLockedPages去调用未初始化的数据, 导致崩溃. 具体成因比较复杂, 就不深入分析了.
可以看到, 问题主要还是在于v29在链接了其它链表时, 没有清理自身的链表, 导致有可能UAF, 如果及时清理了, 其实也就不会造成安全问题. 所以我们分析软件安全问题时, 也要多注意这种类似点, 提高敏感度
00 fffff90e`a867e368 fffff804`19525382 nt!DbgBreakPointWithStatus
01 fffff90e`a867e370 fffff804`19524966 nt!KiBugCheckDebugBreak+0x12
02 fffff90e`a867e3d0 fffff804`19408eb7 nt!KeBugCheck2+0x946
03 fffff90e`a867eae0 fffff804`1941ad69 nt!KeBugCheckEx+0x107
04 fffff90e`a867eb20 fffff804`1941b190 nt!KiBugCheckDispatch+0x69
05 fffff90e`a867ec60 fffff804`19419523 nt!KiFastFailDispatch+0xd0
06 fffff90e`a867ee40 fffff804`1db3f677 nt!KiRaiseSecurityCheckFailure+0x323
07 fffff90e`a867efd0 fffff804`1daf6c05 HTTP!UlFreeUnknownCodingList+0x63
08 fffff90e`a867f000 fffff804`1dacd201 HTTP!UlpParseAcceptEncoding+0x299c5
09 fffff90e`a867f0f0 fffff804`1daa93d8 HTTP!UlAcceptEncodingHeaderHandler+0x51
0a fffff90e`a867f140 fffff804`1daa8ab7 HTTP!UlParseHeader+0x218
0b fffff90e`a867f240 fffff804`1da04c5f HTTP!UlParseHttp+0xac7
0c fffff90e`a867f3a0 fffff804`1da0490a HTTP!UlpParseNextRequest+0x1ff
0d fffff90e`a867f4a0 fffff804`1daa48c2 HTTP!UlpHandleRequest+0x1aa
0e fffff90e`a867f540 fffff804`1932ae85 HTTP!UlpThreadPoolWorker+0x112
0f fffff90e`a867f5d0 fffff804`19410408 nt!PspSystemThreadStartup+0x55
10 fffff90e`a867f620 00000000`00000000 nt!KiStartSystemThread+0x28
00 fffff90e`a867e368 fffff804`19525382 nt!DbgBreakPointWithStatus
01 fffff90e`a867e370 fffff804`19524966 nt!KiBugCheckDebugBreak+0x12
02 fffff90e`a867e3d0 fffff804`19408eb7 nt!KeBugCheck2+0x946
03 fffff90e`a867eae0 fffff804`1941ad69 nt!KeBugCheckEx+0x107
04 fffff90e`a867eb20 fffff804`1941b190 nt!KiBugCheckDispatch+0x69
05 fffff90e`a867ec60 fffff804`19419523 nt!KiFastFailDispatch+0xd0
06 fffff90e`a867ee40 fffff804`1db3f677 nt!KiRaiseSecurityCheckFailure+0x323
07 fffff90e`a867efd0 fffff804`1daf6c05 HTTP!UlFreeUnknownCodingList+0x63
08 fffff90e`a867f000 fffff804`1dacd201 HTTP!UlpParseAcceptEncoding+0x299c5
09 fffff90e`a867f0f0 fffff804`1daa93d8 HTTP!UlAcceptEncodingHeaderHandler+0x51
0a fffff90e`a867f140 fffff804`1daa8ab7 HTTP!UlParseHeader+0x218
0b fffff90e`a867f240 fffff804`1da04c5f HTTP!UlParseHttp+0xac7
0c fffff90e`a867f3a0 fffff804`1da0490a HTTP!UlpParseNextRequest+0x1ff
0d fffff90e`a867f4a0 fffff804`1daa48c2 HTTP!UlpHandleRequest+0x1aa
0e fffff90e`a867f540 fffff804`1932ae85 HTTP!UlpThreadPoolWorker+0x112
0f fffff90e`a867f5d0 fffff804`19410408 nt!PspSystemThreadStartup+0x55
10 fffff90e`a867f620 00000000`00000000 nt!KiStartSystemThread+0x28
while ( 1 )
{
v26[0] = 1000;
ret = UlpParseContentCoding(// 以","为分割, 解析Accept-Encoding字段的值.
v7,
v4,
(unsigned int)&v27,
(unsigned int)&v32,
(__int64)&v30,
(__int64)v26,
(__int64)&v31);// v31指向解析的字符末尾, 比如 传入"a,b,c\r\n", 一轮下来, v31指向"b,c\r\n".
if ( ret < 0 )
{
if ( ret != 0xC0000225 ) // 1. 当最后一次的长度为0时, UlpParseContentCoding会返回 0xC0000225
goto LABEL_46;
if ( v31 == end && !v9 )
{
ret = 0;
goto LABEL_25;
}
}
....
if ( v31 >= end ) // 2. 当刚好是末尾的时候, v31就是消息的末尾. 所以条件成立, 跳出while循环
break;
v7 = v31;
LODWORD(v4) = end - v31;
}
Flink = v29.Flink;
if ( v29.Flink != &v29 )// 3. 当本地链表v29里有链接的时候, 链接到a3变量的结构体里.
{
v20 = v29.Blink;
if ( v29.Flink->Blink != &v29
|| v29.Blink->Flink != &v29
|| (v29.Blink->Flink = v29.Flink,
p_encoding_link_off10h_990h = &a3->encoding_link_off10h_990h,
Flink->Blink = v20,
v22 = a3->encoding_link_off10h_990h.Blink,
a3->encoding_link_off10h_990h.Flink->Blink != &a3->encoding_link_off10h_990h)
|| v22->Flink != p_encoding_link_off10h_990h
|| Flink->Flink->Blink != Flink
|| v20->Flink != Flink )
{
LABEL_47:
__fastfail(3u);
}
v22->Flink = Flink;
a3->encoding_link_off10h_990h.Blink = Flink->Blink;
Flink->Blink->Flink = p_encoding_link_off10h_990h;
v23 = v28;
Flink->Blink = v22;
*(_WORD *)&a3->gap88C[254] = v23; // 4. 链接完成后, 没有清理v29链表的前项和后项;
LABEL_46:
Flink = v29.Flink;
}
if ( ret < 0 ) // 5. 因为ret=0xC0000225, 条件成立, 跳转LABEL_33
goto LABEL_33;
LABEL_33:
if ( Flink != &v29 )
UlFreeUnknownCodingList(&v29);
while ( 1 )
{
v26[0] = 1000;
ret = UlpParseContentCoding(// 以","为分割, 解析Accept-Encoding字段的值.
v7,
v4,
(unsigned int)&v27,
(unsigned int)&v32,
(__int64)&v30,
(__int64)v26,
(__int64)&v31);// v31指向解析的字符末尾, 比如 传入"a,b,c\r\n", 一轮下来, v31指向"b,c\r\n".
if ( ret < 0 )
{
if ( ret != 0xC0000225 ) // 1. 当最后一次的长度为0时, UlpParseContentCoding会返回 0xC0000225
goto LABEL_46;
if ( v31 == end && !v9 )
{
ret = 0;
goto LABEL_25;
}
}
....
if ( v31 >= end ) // 2. 当刚好是末尾的时候, v31就是消息的末尾. 所以条件成立, 跳出while循环
break;
传播安全知识、拓宽行业人脉——看雪讲师团队等你加入!
最后于 2023-1-4 17:35
被音货得福编辑
,原因: 更新内容