首页
社区
课程
招聘
[旧帖] [原创]IIS8 HTTP.sys漏洞 深入分析 0.00雪花
2015-4-29 11:02 7979

[旧帖] [原创]IIS8 HTTP.sys漏洞 深入分析 0.00雪花

2015-4-29 11:02
7979
0x0 简述
4月的微软补丁日,发布了标记为高危的MS15-034补丁。ms15-034漏洞出在http.sys中,这个文件是iis的一个重要组件,功能和特性对windows来说意义重大。微软公告说这个漏洞是一个可以执行远程代码的漏洞,但是截至目前为止已发布的poc仅仅只能dos掉系统。笔者通过补丁对比,深入分析了http.sys漏洞的工作原理,这几日把分析记录整理了一手,以此成文。

0x1 漏洞原理
首先利用补丁对比,发现修复了下面5个函数。



以第一个UlpParseRange函数为例,看看修复了哪些部分。



运气不错,一下就发现了漏洞出现的原因,原来是64位整数相加,导致了STATUS_INTEGER_OVERFLOW。如果在EAX=0xFFFFFFFF,ECX=0xFFFFFFFF的情况下,
经过ADD EAX,1运算后,EAX上溢为0,CF标志=1。ADC ECX, 0执行时,相当于ECX + 0 + CF,导致ECX上溢为0。补丁通过RtlULongLongAdd函数实现了64位无符号数相加,该函数带有安全检查机制,一旦发现相加后的数溢出就会拦截到该错误。尽管这里有一处溢出,但其实这只是整个漏洞利用过程其中的一处BUG,从函数名字来看,补丁修复的是HTTP协议的Range域,下面尝试构造一个简单的POC,来看看程序是如何执行到UlpParseRange函数的。Range域的格式为Range:bytes=begin-end,该域的作用是可以在客户端指定获取服务器文件的某一范围数据,常用于多线程下载。





js执行后,断在了补丁修复处,可以观察寄存器的值。

UlContentRangeHeaderHandler函数是range域的处理函数,我把它逆向了一手,好弄清楚断点触发前range域都经过了哪些处理,由于代码篇幅太长,这里只贴上伪代码。

void UlContentRangeHeaderHandler(REQUEST_INFO *request_info, 
									char *request_value, 
									int len)
{
Char *szBytes = “bytes”;
Int begin64[2];				//低地址存放低32位,高地址存放高32位
Int length64[2];			//这两个参数用于保存UlpParseRange解析range域的值

If(len < sizeof(“bytes”))
{
Return;
}

For(int i = 0;i < sizeof(“bytes”);++i)
{
If(request_value[i] != szBytes[i])
{
Return;
}
}

//调用UlpParseRange处理bytes后面的begin和end,也就是“0-18446744073709551615”
UlpParseRange(request_value, len, &begin64, &length64);

//将begin和length保存到HTTP Request对象的range属性,2个64位数,共16字节
Memcpy(request_info->range, &begin, 0x10);
}

UlpParseRange函数作用就是计算begin和end的差值length,同样的,贴上伪代码:
int UlpParseRange(char *request_value, 
					int request_len, 
					int *begin64, 
					int *length64)
{
//UlpParseRangeNumber函数作用是将字符串解析为64位整数,就不贴代码了
//解析符号“-”前的begin,将结果保存到begin64
UlpParseRangeNumber(request_value, &request_len, &request_value, begin64);

...
...
//request_value修正,request_len修正操作
...
...

//解析符号”-”后面的end,将结果保存到
UlpParseRangeNumber(request_value, &request_len, &request_value, length64);

//下面开始进行合法性检查
If((begin64低32位) & (begin64高32位) == 0xFFFFFFFF)
{
Return error;
}

If(begin64高32位 >= length64高32位)
{
If(begin64高32位 > length64高32位)
{
Return error;
}
Else
{
If(begin64低32位 > length64低32位)
{
Return error;
}
}

mov eax, leng64低32位		//0xFFFFFFFF
mov edi, begin64低32位		//0x0
mov ecx, length64高32位		//0xFFFFFFFF
mov edx, begin64高32位		//0x0

sbb eax, edi					//补丁修复前
sbb ecx, edx
add eax, 1					//eax = 0x0,保存请求长度的低32位
adc ecx, 0					//ecx = 0x0,保存请求长度的高32位

*length_64 = eax;				//length64是一个int [2]数组,高地址保存高32位
*(length_64 + 4) = ecx;			
}


Add eax, 1是因为比如range:bytes=50-100,那么请求的文件长度为100 - 50 + 1 = 51。

UlpParseRange函数处理完毕后,返回到UlContentRangeHeaderHandler,该函数随后将begin64和length64复制到http request对象,然后返回。很明显,后面肯定有地方要处理begin64和length64,给这两个地址下读写断点,断在了UlAdjustRangesToContentSize



该函数我只反汇编了一小段,仅这一小段已经足够解释漏洞的整个工作流程。这一小段的代码作用是,将UlpParseRange函数得到的length64和请求的文件实际大小进行修正。

void UlAdjustRangesToContentSize(REQUEST_INFO *request,int file_low, int file_high)
{
Int begin_low = request->range[0];
Int begin_high = request->range[4];
Int length_low = request->range[8];
Int length_high = request->range[12];

//file_low为HTTP请求文件长度的低32位
//file_high为高32位
If(begin_high >= file_high)
{
If(begin_high > file_high)
{
Return error;
}
Else
{
If(begin_low >= file_low)
{
Return error;
}
}

Do
{
//如果length_low 和 length_high 都等于0xFFFFFFFF,则必修正
If((length_low & length_high) != 0xFFFFFFFF)
{
mov edx, begin_low
add edx, length_low				//此时edx上溢为0
mov ecx, begin_high
adc ecx, length_high				//此时ecx上溢为0

mov end_low, edx
mov end_high, ecx

if(end_high < file_high)
{
//不需要修正
Break;
}

if(end_high == file_high)
{
if(end_low < file_low)			
{
//不需要修正
break;
}
}
}

//下面代码进行length修正
int adjust_length_low = file_low - begin_low;
int adjust_length_high = file_high - begin_high;

Request->range[8] = adjust_length_low;
Request->range[12] = adjust_length_high;
}while(false);
...
}

从伪代码可以看出,如果想绕过修正length部分的代码,构造的Range域必须满足以下几个条件:
假设构造的range域格式为:
Range:bytes=begin-0xFFFFFFFFFFFFFFFF

1.begin_high < file_high && begin_low < file_low        //File是请求的文件长度
2.0xFFFFFFFFFFFFFFFF-begin + 1 不能等于0xFFFFFFFFFFFFFFFF,故begin 不能等于1,如果begin等于0的话,那么经过UlpParseRange函数后计算的长度也为0,就不会触发后面的内存破坏,所以begin > 1



虚拟机跑的win8 IIS8.0,poc请求的是iisstart.htm,长度为1307,后面发了一个1306过去,服务器直接挂掉了。后面测试只要2 <= begin < 1307,就可以稳定触发BSOD

0x3 BSOD触发点
通过dump,可以确定BSOD的代码处。



此刻看函数调用参数



打印出8570e2f2地址上的数据,本次触发BSOD的begin设置为2



IoBuildPartialMdl函数在传递length时传递了一个超大的整数,导致读取数据时CPU产生PAGE_FAULT_IN_NONPAGED_AREA异常,继而BSOD。

0x4 总结
微软官方解释该漏洞为可能允许远程代码执行,理论上通过破坏内存确实有点可行性,但需要设法请求更大的文件,而且数据覆盖的目标地址也很难控制,再加上各种保护,实际操作上,利用漏洞来进行任意代码执行是非常困难的。

[培训]《安卓高级研修班(网课)》月薪三万计划

上传的附件:
收藏
点赞2
打赏
分享
最新回复 (11)
雪    币: 208
活跃值: (60)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
kiwiHacker 2 2015-5-5 13:20
2
0
是否有信息泄露可能?
雪    币: 37
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
suntinker 2015-5-5 16:49
3
0
学习力。。。
雪    币: 5
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
牧民cracker 2015-5-5 17:09
4
0
好厉害呀
雪    币: 41
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
julianfish 2015-5-6 00:10
5
0
学习了,很有用,谢谢!
雪    币: 214
活跃值: (90)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
HelloCrack 2015-5-6 09:03
6
0
http://www.securitysift.com/an-analysis-of-ms15-034/

http://blog.trendmicro.com/trendlabs-security-intelligence/iis-at-risk-an-in-depth-look-into-cve-2015-1635/?utm_source=feedburner&utm_medium=feed&utm_campaign=Feed%3A+Anti-MalwareBlog+%28Trendlabs+Security+Intelligence+Blog%29
雪    币: 42
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
zzxxaa 2015-5-10 23:44
7
0
学习了确实不错
雪    币: 11
活跃值: (40)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
tyrandeOOO 2015-5-13 16:00
8
0
国外的大牛确实厉害,分析的很透彻
雪    币: 7604
活跃值: (1950)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
friendanx 2015-5-15 21:37
9
0
分析的不错,学习了
雪    币: 79
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
wbcmlb 2015-5-15 23:39
10
0
有点高深 学习中
雪    币: 1626
活跃值: (17)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
红颜世家、 2015-5-17 11:40
11
0
已经跪了...
雪    币: 0
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
meiqiu 2015-5-22 21:52
12
0
看看。。。
游客
登录 | 注册 方可回帖
返回