Nginx在反向代理站点的时候,通常会将一些文件进行缓存,特别是静态文件。缓存的部分存储在文件中,每个缓存文件包括"文件头"+"HTTP返回包头"+"HTTP返回包体"。如果二次请求命中了该缓存文件,则Nginx会直接将该文件中的"HTTP返回包体"返回给用户。

脚本代码
这个headers['range']相当于在请求包中加了如下内容
Range: bytes=-1605,-9223372036854775808
headers本身就是一个请求包

如果我的请求中包含Range头,Nginx将会根据我指定的start和end位置,返回指定长度的内容。而如果我构造了两个负的位置,如(-600, -9223372036854774591),将可能读取到负位置的数据。如果这次请求又命中了缓存文件,则可能就可以读取到缓存文件中位于"HTTP返回包体"前的"文件头"、"HTTP返回包头"等内容。
bytes=-%d1,-%d1代表的是从文章末尾第d1个字节开始读,一直读到从文章末尾第d2个字节的位置,由于d2数字赋值较大,会越界读取服务器本不会加载的内容(这是我之前的理解,但是是错误的)
CVE-2017-7529 只能通过 bytes=-%d,-%d 这种"负数 + 多 Range"方式触发。
bytes=-%d1,-%d2
这个意思是两个范围从文章末往回数d1个字节读取后边的内容
第二个范围从文章末往回数d2个字节读取后边的内容
注意bytes=%d,%d并不会触发越界
只有"负数 + 多 Range"方式触发。
那这时候会想,多range为什么不返回多个内容,而只是返回了一个内容
二、那为什么 CVE-2017-7529 里你只看到"一坨数据"?❗
这是 这个漏洞最"反直觉"的地方。
核心一句话:
漏洞发生在「缓存读取阶段」,而不是「HTTP Range 组包阶段」
三、Nginx 正常流程 vs 漏洞流程(非常关键)
✅ 正常情况(无漏洞)
流程是:
解析 Range 头 → 校验 Range 合法性 → 计算每个 range 的起止位置 → 生成 multipart/byteranges 响应
❌ CVE-2017-7529 的真实流程
流程变成了:
解析 Range 头 → 负数 Range 触发溢出 → 计算出一个错误的缓存偏移 → 直接从 cache 文件连续读取数据 → 当成"一个连续 buffer"返回
⚠️ 注意这里:
Nginx 根本没走到"multipart 组包"那一步
四、为什么它"退化成一个整体返回"?(重点)
因为:
- 漏洞触发在:
ngx_http_file_cache_read() - 这里是 底层缓存文件读取
- 不是 HTTP 协议层
结果是:
它读的是"缓存文件中的连续内存/磁盘内容"
而不是
"两个逻辑上的 Range"
五、换成一句人话解释(超重要)
你可以这样理解:
本来你点了两样菜
厨师应该分盘给你
但因为后厨算错了位置
直接从冰箱里
把一整块乱七八糟的东西
倒你盘子里了
什么是组包
HTTP/1.1 206 Partial Content
Content-Type: multipart/byteranges; boundary=XYZ
--XYZ
Content-Range: bytes 2-4/20
234
--XYZ
Content-Range: bytes 7-8/20
78
--XYZ--
类似于这种
- --XYZ 是分隔符
- 每段都有 Content-Range 头
- 每段的内容只包含客户端请求的那部分
三、CVE-2017-7529 里组包阶段为什么没生效?
在 CVE-2017-7529 里:
- 你发的 Range 是负数 + 多 Range:bytes=-1605,-9223372036854774203
- Nginx 在计算偏移量 的时候触发 整数溢出
- 结果:
- 返回的数据不是逻辑上的两个区段
- 而是 连续的缓存内容
- 服务器根本没走"multipart 组包"的逻辑
所以你看到的响应: - 没有 Content-Type: multipart/byteranges
- 没有分段标记
- 只是一坨连续数据

最上边的content-type和content-range一直到key(包括key)之前的都是越界内容