nginx整数溢出小结-cve-2017-7529

起因

这两天在做nginx整数溢出时出了点小岔子,经过学习对这个问题有了深入的了解,特此记录。

漏洞补丁以及平安银河实验室已经把这事说的很清楚了,在此记录原文地址。

//https://www.freebuf.com/articles/terminal/140402.html

简单的说就是这么一回事:

HTTP的Range允许客户端分批次请求资源的一部分,如果服务端资源较大,可以通过Range来并发下载;
正常情况下组件代码中计算了Range的长度,防止溢出,可以构造恶意长度来绕过,从而读取缓存的头部数据,造成泄露。

细节要留意的就是range的相关定义,示例如下
Range:bytes=0-1024 表示访问第0到第1024字节;

Range:bytes=500-600,601-999,-300 表示分三块访问,分别是500到600字节,601到600字节,最后的300字节;

在Response头中设置:

Accept-Ranges:bytes 表示接受部分资源的请求;

Content-Range: bytes START-END/SIZE 表示返回的资源位置;其中SIZE等于Content-Length;如:Content-Range: bytes 500-600/1000

经过

测试站点:www.(我肯定是不能明说).com.cn
nginx整数溢出小结-cve-2017-7529_第1张图片
以站点的一张图片为例子,路径:/images/sq.jpg
nginx整数溢出小结-cve-2017-7529_第2张图片
上图可以看到正常情况下content-length的大小是8935bytes,也就是size的大小。
size大小就是我一开始搞混了的地方,清楚了size的大小那么根据平安银河实验室的文章就有如下过程:
nginx整数溢出小结-cve-2017-7529_第3张图片
上图可以看到正常的返回数据以及完整的ranges长度返回。
nginx整数溢出小结-cve-2017-7529_第4张图片
上图可以看到,选择返回后十个bytes得到的结果(8935-10=8925,所以是从8925开始返回。因为写定的end是总长减一,所以结尾的值是8934,所以此处statr=8925,end=8934),以及返回的range的位置。
nginx整数溢出小结-cve-2017-7529_第5张图片
上图是对上面的补充,可以看到在读取部分bytes时,完整的参数是从“多少”到“多少”,上面是从1到10

nginx整数溢出小结-cve-2017-7529_第6张图片
上图可以看到,在使用完整的输入模式,对-1开始读取的时候,触发了

ngx_http_range_parse()

对statr的负值检查,所以报错,无法读取。
那么此时就如原文所说

因此,如果需要将start解析为负数,只能通过-end这类后缀型range参数实现

nginx整数溢出小结-cve-2017-7529_第7张图片
如上图,使用 -end 只设置一个大于8935的值,此时statr = 8935-9000 = -65,满足了负值。但按上文所说,statr是负值则会报错。
注意到此时并未报错,而是正常返回,原因是此处设置的range不是完整模式,虽然是负值,但是因为end的默认值是总长-1(8935-1=8934),那么此时这段要读取的range的范围是从 -658934 其实际总长超过了 8935 所以nginx丢弃了range,所以正常返回。

重点

上面满足了传值为负值的条件,但是因为总长度检测的原因,导致丢弃range,那么如果绕过总长度检测?为什么这个漏洞要叫 整数溢出 漏洞呢?
这里注意到 ***start, end, size均为64位有符号整形,值的范围是-9223372036854775808 … 9223372036854775807 ,那么根据补丁中的代码 size += end - start
nginx整数溢出小结-cve-2017-7529_第8张图片只要最终size的值是负值,即可溢出,从而绕过检测,所以叫 整数溢出
0x8000 0000 0000 0000既是64位有符号型的最小负值,所以只需要最终相加得到的size为0×8000000000000000即可。

注意 size += end -start;这等于如果存在多个range,那么就会有 size = size + (end - start) 
也就是说在有多个range时,最终的size = size1 + size2 + ... +sizeN
如上所说,只要size是负值即可溢出,那么就能得到第二个range的值,若第一个range是9500
那么0x8000000000000000-9500 既是第二个值

那么如下图:

成功!

结果

你可能感兴趣的:(漏洞复现)