nginx 整数溢出小结 cve-2017-7529
本文转自gaogaogaopipi
起因
这两天在做nginx整数溢出时出了点小岔子,经过学习对这个问题有了深入的了解,特此记录。
漏洞补丁以及平安银河实验室已经把这事说的很清楚了,在此记录原文地址
简单的说就是这么一回事:
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
以站点的一张图片为例子,路径:/images/sq.jpg
上图可以看到正常情况下content-length的大小是8935bytes,也就是size的大小。
size大小就是我一开始搞混了的地方,清楚了size的大小那么根据平安银河实验室的文章就有如下过程:
上图可以看到正常的返回数据以及完整的ranges长度返回。
上图可以看到,选择返回后十个bytes得到的结果(8935-10=8925,所以是从8925开始返回。因为写定的end是总长减一,所以结尾的值是8934,所以此处statr=8925,end=8934),以及返回的range的位置。
上图是对上面的补充,可以看到在读取部分bytes时,完整的参数是从“多少”到“多少”,上面是从1到10
上图可以看到,在使用完整的输入模式,对-1开始读取的时候,触发了ngx_http_range_parse()
对statr的负值检查,所以报错,无法读取。
那么此时就如原文所说
因此,如果需要将start解析为负数,只能通过-end这类后缀型range参数实现
如上图,使用 -end 只设置一个大于8935的值,此时statr = 8935-9000 = -65,满足了负值。但按上文所说,statr是负值则会报错。
注意到此时并未报错,而是正常返回,原因是此处设置的range不是完整模式,虽然是负值,但是因为end的默认值是总长-1(8935-1=8934),那么此时这段要读取的range的范围是从 -65到 8934 其实际总长超过了 8935 所以nginx丢弃了range,所以正常返回。
重点
上面满足了传值为负值的条件,但是因为总长度检测的原因,导致丢弃range,那么如果绕过总长度检测?为什么这个漏洞要叫 整数溢出 漏洞呢?
这里注意到 ***start, end, size均为64位有符号整形,值的范围是-9223372036854775808 … 9223372036854775807 ,那么根据补丁中的代码 size += end - start
只要最终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 既是第二个值
那么如下图:
成功!
结果