访问http://your-ip/uploadfiles/nginx.png
和http://your-ip/uploadfiles/nginx.png/.php
即可查看效果。Nginx拿到文件路径(更专业的说法是URI)/nginx.png/.php
后,一看后缀是.php
,便认为该文件是php文件,转交给php去处理。php一看/nginx.png/.php
不存在,便删去最后的/.php
,又看/nginx.png
存在,便把/nginx.png
当成要执行的文件了。
该漏洞与Nginx、php版本无关,属于用户配置不当造成的解析漏洞。
Nginx 1.x 最新版:https://vulhub.org/#/environments/nginx/nginx_parsing_vulnerability/
1、由于nginx.conf的如下配置导致nginx把以.php
结尾的文件交给fastcgi处理,为此可以构造http://ip/uploadfiles/nginx.png/.php
(url结尾不一定是.php
,任何服务器端不存在的php文件均可,比如a.php
),其中nginx.png
是我们上传的包含PHP代码的图片马。
default.conf配置文件如下:
server {
listen 80 default_server;
listen [::]:80 default_server;
root /usr/share/nginx/html;
index index.html index.php;
server_name _;
location / {
try_files $uri $uri/ =404;
}
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;
}
}
2、但是fastcgi在处理.php
文件时发现文件并不存在,这时php.ini
配置文件中cgi.fix_pathinfo=1
发挥作用,这项配置用于修复路径,如果当前路径不存在则采用上层路径。为此这里交由fastcgi处理的文件就变成了nginx.png
。
3、最重要的一点是php-fpm.conf
中的security.limit_extensions
配置项限制了fastcgi解析文件的类型(即指定什么类型的文件当做代码解析),此项设置为空的时候才允许fastcgi将.png
等文件当做代码解析。
注:限制fpm允许解析的脚本扩展名。此设置可以预防web服务器配置的错误。应当限制fpm仅仅解析.php
扩展名,阻止恶意用户使用其他扩展名运行php代码。默认值:.php
4、漏洞防御:
php.ini
文件中的cgi.fix_pathinfo
的值设置为0,这样php再解析1.jpg/1.php
这样的目录时,只要1.php
不存在就会显示404页面php-fpm.conf
中的security.limit_extensions
后面的值设置为.php
参考链接:https://www.cnblogs.com/yuzly/p/11208742.html(nginx解析漏洞复现)
访问靶场地址,正常上传php文件被拦截:
上传jpg文件,得到上传后文件路径:
访问文件路径,正常显示:
文件路径加入/.php,成功解析成php文件,证明上传代码不存在漏洞,但利用解析漏洞即可getshell:
如果将php-fpm.conf
中的security.limit_extensions
后面的值设置为.php
,限制fpm仅仅解析.php
扩展名,阻止恶意用户使用其他扩展名运行php代码。则不会将.jpg
文件当作php代码解析。
这一漏洞的原理是非法字符空格和截止符\0
会导致Nginx解析URI时的有限状态机混乱,危害是允许攻击者通过一个非编码空格绕过后缀名限制。
正常情况下,只有.php
后缀的文件才会被发送给fastcgi解析。
这里却错误地解析了请求的URI
Nginx 0.8.41 ~ 1.4.3 / 1.5.0 ~ 1.5.7
Nginx:https://vulhub.org/#/environments/nginx/CVE-2013-4547/
这个漏洞其实和代码执行没有太大关系,其主要原因是错误地解析了请求的URI,错误地获取到用户请求的文件名,导致出现权限绕过、代码执行的连带影响。
举个例子,比如,Nginx匹配到.php
结尾的请求,就发送给fastcgi进行解析,常见的写法如下:
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;
}
正常情况下(关闭pathinfo的情况下),只有.php
后缀的文件才会被发送给fastcgi解析。
而存在CVE-2013-4547的情况下,我们请求1.gif[0x20][0x00].php
,这个URI可以匹配上正则\.php$
,可以进入这个Location块;但进入后,由于00截断,Nginx却错误地认为请求的文件是1.gif[0x20]
,就设置其为SCRIPT_FILENAME
的值发送给fastcgi。
fastcgi根据SCRIPT_FILENAME
的值进行解析,最后造成了解析漏洞。
所以,我们只需要上传一个空格结尾的文件,即可使PHP解析。
再举个例子,比如很多网站限制了允许访问后台的IP:
location /admin/ {
allow 127.0.0.1;
deny all;
}
我们可以请求如下URI:/test[0x20]/../admin/index.php
,这个URI不会匹配上location后面的/admin/
,也就绕过了其中的IP验证;但最后请求的是/test[0x20]/../admin/index.php
文件,也就是/admin/index.php
,成功访问到后台。(这个前提是需要有一个目录叫“test ”:这是Linux系统的特点,如果有一个不存在的目录,则即使跳转到上一层,也会爆文件不存在的错误,Windows下没有这个限制)
参考:https://www.freesion.com/article/8137557774/(NGINX不一样的逻辑漏洞)
访问http://your-ip:8080/
即可看到一个上传页面
// 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.');
}
这里对上传的文件做了黑名单限制,上传图片马"test.jpg "
,注意后缀后面有空格:
访问http://your-ip:8080/uploadfiles/test.jpg[0x20][0x00].php
,发现PHP已成功解析:
注意:[0x20]
是空格,[0x00]
是\0
,这两个字符都不需要编码。
抓包将.php
前增加[0x20][0x00]
两个字节,nginx识别到.php文件,但由于00截断fastcgi会解析test.jpg[0x20]
文件
如果security.limit_extensions
限制了值为.php
则无法解析,可见security.limit_extensions
威力强大,一招破万法。
如果请求中包含Range头,Nginx会根据指定的start和end位置,返回指定长度的内容。攻击者可以构造两个负的位置来获取响应中的缓存文件头部信息。在某些配置中,缓存文件头可能包含后端服务器的IP地址或其它敏感信息,比如缓存文件中位于HTTP返回包体前的文件头、HTTP返回包头,从而导致信息泄露。
Nginx version 0.5.6 - 1.13.2
Nginx:https://vulhub.org/#/environments/nginx/CVE-2017-7529/
由于对http header中range域处理不当造成。
主要代码是ngx_http_range_parse函数中的循环。
Nginx在反向代理站点的时候,通常会将一些文件进行缓存,特别是静态文件。缓存的部分存储在文件中,每个缓存文件包括“文件头”+“HTTP返回包头”+“HTTP返回包体”。如果二次请求命中了该缓存文件,则Nginx会直接将该文件中的“HTTP返回包体”返回给用户。
如果我们的请求中包含Range头,Nginx将会根据我指定的start和end位置,返回指定长度的内容。而如果我构造了两个负的位置,如(-600, -9223372036854774591),将可能读取到负位置的数据。如果这次请求又命中了缓存文件,则可能就可以读取到缓存文件中位于“HTTP返回包体”前的“文件头”、“HTTP返回包头”等内容。当Nginx服务器使用代理缓存的情况下,攻击者通过利用该漏洞可以拿到服务器的后端真实IP。
正向代理和反向代理
正向代理类似一个跳板机,代理访问外部资源:(正向代理即是客户端代理, 代理客户端, 服务端不知道实际发起请求的客户端)
反向代理实际运行方式是指以代理服务器来接受internet上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给internet上请求连接的客户端,此时代理服务器对外就表现为一个服务器:(反向代理即是服务端代理, 代理服务端, 客户端不知道实际提供服务的服务端)
参考链接:
https://www.cnblogs.com/cute-puli/p/13282355.html
https://www.cnblogs.com/taostaryu/p/10547132.html
访问http://your-ip:8080/index.html
,即可查看到Nginx默认页面,这个页面实际上是反向代理的8081端口的内容。
调用python3 poc.py http://your-ip:8080/
,读取返回结果:
可见,越界读取到了位于“HTTP返回包体”前的“文件头”、“HTTP返回包头”等内容。
如果读取有误,调整poc.py中的偏移地址(605)。
CRLF注入
回车换行(CRLF)注入攻击是一种当用户将CRLF字符插入到应用中而触发漏洞的攻击技巧。CRLF字符(%0d%0a)在许多互联网协议中表示行的结束,包括HTML,该字符解码后即为\ r\ n。这些字符可以被用来表示换行符,并且当该字符与HTTP协议请求和响应的头部一起联用时就有可能会出现各种各样的漏洞,包括http请求走私(HTTP RequestSmuggling)和http响应拆分(HTTP Response Splitting)。
就http请求走私而言,这种漏洞通常出现在当服务器接收http请求并将该请求转发给其他服务器时,比如说代理和防火墙,这种漏洞将造成如下问题:
测试
CRLF注入漏洞的检测也和XSS漏洞的检测差不多。通过修改HTTP参数或URL,注入恶意的CRLF,查看构造的恶意数据是否在响应头中输出。主要是在看到有重定向或者跳转的地方,可以在跳转的地址添加?url=http://baidu.com/xxx%0a%0dSet-Cookie:%20test123=123
测试一下,通过查看响应包的数据查看结果。
如果利用成功的话,响应包会出现一行Set-Cookie: test123=123
数据。
防御方式
服务端收到前端过来的参数,在加入Location之前,需要过滤 \r
、\n
之类的行结束符,避免输入的数据污染其它HTTP首部字段。
参考文章:
https://xz.aliyun.com/t/7501
https://zhuanlan.zhihu.com/p/22953209
https://www.cnblogs.com/mysticbinary/p/12560080.html
Nginx会将$uri
进行解码,导致传入%0a%0d
即可引入换行符,造成CRLF注入漏洞。
错误的配置文件示例(原本的目的是为了让http的请求跳转到https上)
location / {
return 302 https://$host$uri;
}
利用Payload:http://your-ip:8080/%0a%0dSet-Cookie:%20test123=123
,可注入Set-Cookie头:
利用Payload,http://your-ip:8080/%0a%0dSet-Cookie:%20test123=123%0d%0a%0d%0a
,可构造一个xss漏洞:
目录遍历/穿越漏洞是一个WEB安全漏洞,黑客可以利用该漏洞读取运行在服务器上的任意文件。文件可以是应用程序代码,或者数据,后端系统的凭证以及敏感的操作系统文件。在某些情况下,黑客可以在服务器上传任意文件然后允许他们修改应用程序数据行为,从而最终完全控制服务器。
在window上../
和..\
都是有效的目录遍历序列
利用目录穿越漏洞的常见障碍
如果应用程序从用户提供的文件名中剥离或阻止目录穿越,则可以用多种技术绕过这种防御。
filename=/var/www/images/../../../etc/passwd
filename=../../../etc/passwd%00.jpg
防御方式
防止目录文件穿越攻击最有效的方法是避免将用户提供的输入完全传递给文件系统API。可以重写许多执行此操作的应用程序功能,以更安全的方式提供相同的行为。
如果认为将用户输入的信息传递给文件系统API是无法避免的,那么可以使用两层防御来防止攻击:应用程序将在处理之前验证用户输入。理想情况下,验证应与允许值中的白名单进行比较,如果所需功能无法做到这一点,则验证输入内容仅包含允许内容,例如纯字母数字字符。
参考链接:https://bbs.zkaq.cn/t/4315.html
Nginx在配置别名(Alias)的时候,如果忘记加/
,将造成一个目录穿越漏洞。
错误的配置文件示例(原本的目的是为了让用户访问到/home/目录下的文件):
location /files {
alias /home/;
}
Payload:http://your-ip:8081/files../
,成功穿越到根目录:
Nginx配置文件子块(server、location、if)中的add_header
,将会覆盖父块中的add_header
添加的HTTP头,造成一些安全隐患。
如下列代码,整站(父块中)添加了CSP头:
add_header Content-Security-Policy "default-src 'self'";
add_header X-Frame-Options DENY;
location = /test1 {
rewrite ^(.*)$ /xss.html break;
}
location = /test2 {
add_header X-Content-Type-Options nosniff;
rewrite ^(.*)$ /xss.html break;
}
但/test2
的location中又添加了X-Content-Type-Options
头,导致父块中的add_header
全部失效: