-
-
[原创]HTTP.sys UAF 漏洞 CVE-2021-31166 简要分析
-
2023-1-3 18:38 8922
-
最近在看http.sys,必然需要分析旧的漏洞的,使用网上的poc触发了漏洞后,有了个具体概念,就写一篇简要分析,帮助大家理解
poc
通过 POC(链接地址) 可知,漏洞是一个UAF漏洞,栈回溯也很清晰:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | # 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 |
从栈回溯可以知道, 它是一个独立线程, 崩溃在解析Header里. 而poc也很简单"Accept-Encoding: doar-e, ftw, imo, ,", 发送一个带"Accept-Encoding"字段的请求, 包含至少一个正常值, 最后带个空值.
原理分析
以下是函数UlpParseAcceptEncoding的伪代码. 分析参见注释和序号.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | 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); |
以下是UlFreeUnknownCodingList函数:
1 2 3 4 5 6 7 8 9 10 11 12 | while ( 1 ) { Flink = a1 - >Flink; if ( a1 - >Flink = = a1 ) / / 7. 当链表第一个项被释放后, 此条件还是成立 break ; v3 = Flink - >Flink; if ( Flink - >Flink - >Blink ! = Flink || (v4 = Flink - >Blink, v4 - >Flink ! = Flink) ) / / 8. 当链表第一个项被释放后, 链表已经错乱, 这个条件就成立了, 最后触发崩溃 __fastfail( 3u ); v4 - >Flink = v3; v3 - >Blink = v4; / / 6. 此处也没有重置Flink的前项和后项字段. ExFreePoolWithTag(&Flink[ - 1 ], 0 ); } |
通过上述注释, 可以明显看到问题出在v29的链表管理上.
下面是释放前后的关系:
释放前的链表关系:
步骤7时的链表关系:
补丁
知道了根本原因, 修补也很简单, 在步骤3和步骤4之间, 把v29的指针重置一下即可. 而官方补丁也是这样做的.
CVE-2022-21907
根据测试来看, 在本来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, 如果及时清理了, 其实也就不会造成安全问题. 所以我们分析软件安全问题时, 也要多注意这种类似点, 提高敏感度
[培训]《安卓高级研修班(网课)》月薪三万计划,掌握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法