来源:http://bbs.ichunqiu.com/thread-9823-1-1.html?from=ch
社区:i春秋
时间:2016年8月10日
作者:MAX丶
大家肯定用过bugscan老板裤子里面经常出现这个漏洞提示。
【xxx存在http.sys远程漏洞】今天我们来分析分析这漏洞如何存在的,据称,利用HTTP.sys的安全漏洞,攻击者只需要发送恶意的http请求数据包,就可能远程读取IIS服务器的内存数据,或使服务器系统蓝屏崩溃。
根据公告显示,该漏洞对服务器系统造成了不小的影响,主要影响了包括Windows 7、Windows Server 2008 R2、Windows 8、Windows Server 2012、Windows 8.1 和 Windows Server 2012 R2在内的主流服务器操作系统。
1. 首先对补丁文件做了二进制比对
以下是Windows 7 修复前后HTTP.SYS的变化。
值得注意的经验是,每个函数名中都有’range’。 这不禁让我想起了之前Apache HTTPd ‘Range’ 头漏洞, (参见RFC2616章14.35节)。
漏洞数据由 ‘range’ 头带入已经毋庸置疑。 下面要做的是深入研究其产生的原因。如IDA 自动分析出的的HTTP!UlpParseRange 函数的调用关系图所示:
这里我使用Vmware搭建了一个未打补丁的Windows 7 SP1 作为测试目标,并且启用了内核调试器, 对所有关键函数设置断点,收集关键数据和内核信息。
在断点被触发之后, 堆栈信息会被打印出来, 接着程序继续执行. 我们使用了之前的Apache Rangedos 测试样本针对目标服务器做了攻击性测试, 调试器捕获到了下面这些信息:
标准的Apache RangeDos 脚本确实产生了效果,下面让我们更进一步的来看一下 HTTP!UlpParseRange 函数的实现:
在旧代码的调试中发现函数在此处调用了一个很大的整数。
而新版本的代码调用了HTTP!RtlULongLongAdd 来检查是否有整数溢出。 注意,这个HTTP.sys内的函数调用5个参数而不是3个参数。重复之前的测试脚本, 就会发现系统返回的错误代码是0xC000000D(STATUS_INVALID_PARAMETER),而不再是STATUS_INTEGER_OVERFLOW 。
修正之后的一个简单的POC测试脚本如下(此脚本仅用来解析证明模块内部对Range头参数的解析过程)。
接着让我们在未打补丁的模块上下断点。
非常明显了。EAX 是0x7A69 (Poc中设置的range上限 31337), EDI 是0x539(Poc中设置的range下限 1337)。 在老代码中,如果我们的下限是0,则上限不会做如此的改变。在上限非常大的时候(整数临界值), 我们加1, 就会发生整数溢出。 HexRays 的输出更加明了:
*(_QWORD *)v18 = __PAIR__(v22, v23) – __PAIR__(v21, v20) + 1.让我们来试试看。
这时候可以看到上限已经非常的大了(0xFFFFFFFF)。代码接着执行。
We can see that EAX is predictably small now. Now that we have some indication of what the pre-patch block is doing, let’s look at the patch again.
最后EAX在加1之后溢出变成了0. 让我们再看看打过补丁的模块:
使用同样的手法, 我们得到了一个不同的错误信息. 有趣的是很多情况下都会产生这个错误信息(比如节点深度问题等)。但是我们的主要目标是判断是否打补丁, 所以这个没什么大碍. 现在让我们回到系统是否打补丁的预判上.
一种方法让未打补丁的模块返回STATUS_INVALID_PARAMETER错误信息的方法就是使下面代码的关键处校验失败。
使用调试器动态改变跳转条件, 我们可以让服务器返回下面这个有趣的页面(手工内存补丁成功):
打完补丁以后当你提交了包含非法range头字段的http包的时候, 系统会报上面的错误。
如果没打补丁你看到的就是下面这样:
这就是判断是否打了补丁的关键.
打了补丁: HTTP!RtlULongLongAdd在遇到非法range头的时候会返回一个 STATUS_INVALID_PARAMETER 错误, 接着HTTP!UlpParseRang 产生一个”Invalid Header” 错误信息给客户端.
打了补丁: HTTP!UlpParseRange 返回 0, 接着产生一个 “Requested Range Not Satisfiable”.错误信息给客户端.
下面给大家发一个检测py脚本。
[Python]纯文本查看复制代码
01
02
03
04
05
06
07
08
09
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
52importsocket
importrandom[/font][/color][/size]
[size=4][color=Red][font=微软雅黑]
ipAddr="这里填入检测IP地址"
hexAllFfff="18446744073709551615"
req1="GET / HTTP/1.0\r\n\r\n"
req="GET / HTTP/1.1\r\nHost: stuff\r\nRange: bytes=0-"+hexAllFfff+"\r\n\r\n"
print" Audit Started"
client_socket=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client_socket.connect((ipAddr,80))
client_socket.send(req1)
boringResp=client_socket.recv(1024)
if"Microsoft"notinboringResp:
print" Not IIS"
exit(0)
client_socket.close()
client_socket=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client_socket.connect((ipAddr,80))
client_socket.send(req)
goodResp=client_socket.recv(1024)
if"Requested Range Not Satisfiable"ingoodResp:
print"[!!] Looks VULN"
elif" The request has an invalid header name"ingoodResp:
print" Looks Patched"
else:
print"[/font][/color][color=Red][font=微软雅黑] Unexpected response, cannot discern patch status"