HTTP协议漏洞分析
HTTP协议漏洞
1. CVE-2021-31166
跟CVE-2022-211907一样道理。
2. CVE-2022-21907
前言
“Accept-Encoding” HTTP 标头字段值具有以下格式:
Accept-Encoding = #( codings [ weight ] ) |
示例“Accept-Encoding”标头如下:
Accept-Encoding: gzip |
其中字段值字符串是gzip、identity、*、deflate、 gzip;q=1.0, ;q=0.5在HTTP.sys中的例程HTTP!UlAcceptEncodingHeaderHandler、HTTP!UlpParseAcceptEncoding和HTTP!UlpParseContentCoding负责解析“Accept-Encoding”HTTP 请求标头。
漏洞描述:
HTTP 协议栈远程代码执行漏洞,Internet Information Services (IIS) 是一种灵活、可扩展、安全且可管理的 Web 服务,用于托管 Web 上的静态和动态内容。IIS 支持各种 Web 技术,包括 HTML、ASP、ASP.NET、JSP、PHP 和 CGI。此功能通过称为 http 协议栈 ( http.sys ) 的内核模式设备驱动实现。此驱动程序负责解析 http 请求并对客户端响应。
漏洞出现在http.sys内,未授权的攻击者可以构造恶意请求包攻击目标服务器,成功利用该漏洞的攻击者可导致目标服务器蓝屏,或执行任意代码。
超文本传输协议(HTTP)是一个用于传输超媒体文档(例如HTML)的应用层协议。它是为Web浏览器与Web服务器之间的通信而设计的,Windows上的HTTP协议栈用于Windows上的Web服务器,如IIS,若该协议栈相关的组件存在漏洞,则可能导致远程恶意代码执行。经分析,CVE-2021-31166漏洞影响可稳定触发BSoD(Blue Screen of Death,缩写BSoD,指微软Windows操作系统在无法从一个系统错误中恢复过来时显示的蓝屏死机图像)。该漏洞被微软官方标记为Wormable(蠕虫级)和Exploitation More Likely(更可能被利用),这意味着漏洞利用可能性很大,恶意攻击者有可能通过利用该漏洞制造蠕虫病毒攻击。
漏洞复现:
攻击者 | 受害者 |
---|---|
Windows 11 | Windows 10 2004版(20H1) |
poc代码如下:
import requests |
然后在Win10中开启IIS:
打开控制面板-启用关闭Windows功能-开启Internet Information Services&Internet Information Services可承载的Web核心
这里我的Virtualbox中Win10使用的是桥接模式,要保证能和主机互相ping通:
在cmd中输入ipconfig/all
查看Win10本机的ip地址,然后在浏览器中打开本机的ip地址就可以看到IIS服务:
在攻击者机器中输入如下,便可造成Windows 10中的BSoD蓝屏。
漏洞原因分析:
将dump文件在Windgb中打开进行分析:
使用kd>k
进行栈回溯:
发现是调用了HTTP!UIFreeUnknownCodingList
函数之后才引发的KiRaiseSecurityCheckFailure
报错。
调用如下:
───> nt!KiStartSystemThread+0x28
│ ├──> nt!PspSystemThreadStartup+0x55
│ │ ├──> HTTP!UlpThreadPoolWorker+0x112
│ │ │ ├──> HTTP!UlpHandleRequest+0x1aa
│ │ │ │ ├──> HTTP!UlpParseNextRequest+0x1ff
│ │ │ │ │ ├──> HTTP!UlParseHttp+0xac7
│ │ │ │ │ │ ├──> HTTP!UlParseHeader+0x218
│ │ │ │ │ │ │ ├──> HTTP!UlAcceptEncodingHeaderHandler+0x51
│ │ │ │ │ │ │ │ ├──> HTTP!UlpParseAcceptEncoding+0x298f5
│ │ │ │ │ │ │ │ │ ├──> HTTP!UlFreeUnknownCodingList+0x63
│ │ │ │ │ │ │ │ │ │ ├──> nt!KiRaiseSecurityCheckFailure+0x323
│ │ │ │ │ │ │ │ │ │ │ ├──> nt!KiFastFailDispatch+0xd0
│ │ │ │ │ │ │ │ │ │ │ │ ├──> nt!KiBugCheckDispatch+0x69
│ │ │ │ │ │ │ │ │ │ │ │ │ └──> nt!KeBugCheckEx
这里整理一下调用关系:
UlParseHttp -> UlParseHeader -> UlAcceptEncodingHeaderHandler -> UlpParseAcceptEncoding |
使用kd>!analyze -v
自动分析bug信息报告:
可以看到是由于LIST_ENTRY
的双重释放而造成的蓝屏,并且可以看到造成问题的程序是HTTP.sys。然后我们可以在C:\Windows\System32\drivers
中找到http.sys
文件。
分析DUMP,调用栈可见异常出现在UlFreeUnknownCodingList函数内,该函数由UlpParseAcceptEncoding
调用,接下来对这两个函数进行分析。先再IDA中打开对UlpParseAcceptEncoding
函数分析。
1. 构建链表UlpParseAcceptEncoding
它会循环解析 Accept-Encoding 中的编码方式,首先通过 UlpParseContentCoding
函数依次获取每个编码信息,如果是支持的编码方式,则设置相应位;如果是 没有被识别出的其他编码方式,则调用 ExAllocatePoolWithTagPriority
函数申请 0x20 字节大小的堆空间来存放这些信息,然后将它们链到 UnknownCodingList
中,其中UnknownCodingList
是一个双向循环链表:
2. 更换为Request链表头
上述构建链表过程通过循环调用,直到获取完所有编码后,会进入一个判断分支,除非UnknownCodingList
不为空链表且链头前后链接无异常,就会将 UnknownCodingListHead
取下,然后把 Request
对象的UnknownCodingListHead
链入:
对关系进行整理如下:
//*************如果满足以下任意条件,则会调用 __fastfail(3u)************* |
在上一步更换链头的过程中,虽然已经将链表头由UnknownCodingListHead
更换为Request->UnknownCodingListHead
,但是并没有 UnknownCodingListHead
将清空,通过UnknownCodingListHead
还是可以访问 UnknownCodingListHead->Flink
和 UnknownCodingListHead->Blink
,即,UnknownCodingListHead
中依然保留可以操控原来链表的指针,如下图:
图中左右侧箭头都表示节点的前后指针,在更换链表头后,没有将UnknownCodingListHead
置为空指针。
3. 参数判定
回到前面,在进行判断时:
在此之后,程序进入判断如果v5<0
,则跳转到LABEL_33:
LABEL_33处会判断UnknownCodingList
是否为空,如果不为空就调用 UlFreeUnknownCodingList
函数,且参数为&UnknownCodingListHead
。由于 UnknownCodingListHead
已经不在循环双链表中,按照这个流程对UnknownCodingList
进行释放就会出现双重释放的问题。
因此,要触发此漏洞,要使得v5的值小于0,通过对于v5进行溯源,如下图所示:
v5作为函数UlpParseContentCoding
的返回值,若 v5 小于 0 且不等于 0xC0000225,则逻辑转移至 LABEL_46,这会进一步导致程序逻辑跳过将RequestUnknownCodingListHead
链进链表的过程,在 UlFreeUnknownCodingList
函数中释放 UnknownCodingList
时,不会出现双重释放的问题。
综上,只有当 v5 为 0xC0000225,且UnknownCodingList
不为空时,才会进行前文中分析的链表操作,且在最后判断 v5 < 0
后,跳到 LABEL_33 去执行UlFreeUnknownCodingList
函数,于是就引起了双重释放。
为了让UlpParseContentCoding
函数的返回值等于0xC0000225,才能使得程序进入漏洞触发逻辑,观察UlpParseContentCoding
函数:
函数最终的返回值是v12。v9通过参数a2进行传递,而a2是当前解析剩余长度:
最终,当v9的值为0时,将返回值v12赋值为0xC0000225,并跳转至返回逻辑。综上,在函数UlpParseContentCoding
中, 因此要使得v9的值为0,可将最后一个参数设定为空格,在进入UlpParseContentCoding
后,a2的值为1,v9会被赋值为1,并且第一个编码字符A是未知编码字符,通过循环自减一次后会退出,使得v9的值为0。使得上图判断条件成立后,作为函数返回值的参数v12会被进一步赋值为0xc0000225,最终使得程序进入双重释放的漏洞分支逻辑。