-
-
[原创]HTTP.sys UAF 漏洞 CVE-2021-31166 简要分析
-
发表于: 2023-1-3 18:38 10512
-
最近在看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, 如果及时清理了, 其实也就不会造成安全问题. 所以我们分析软件安全问题时, 也要多注意这种类似点, 提高敏感度
# Child-SP RetAddr Call Site
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
# Child-SP RetAddr Call Site
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
;
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!