利用docker复现Nginx配置漏洞
一、$uri
导致的CRLF注入漏洞
下面两种情景十分常见:
-
用户访问
http://example.com/aabbcc
,自动跳转到https://example.com/aabbcc
-
用户访问
http://example.com/aabbcc
,自动跳转到http://www.example.com/aabbcc
第二个场景主要是为了统一用户访问的域名,更加有益于SEO优化。
在跳转的过程中,我们需要保证用户访问的页面不变,所以需要从Nginx获取用户请求的文件路径。查看Nginx文档,可以发现有三个表示uri的变量:
-
$uri
-
$document_uri
-
$request_uri
解释一下,1和2表示的是解码以后的请求路径,不带参数;3表示的是完整的URI(没有解码)。那么,如果运维配置了下列的代码:
location / { return 302 https://$host$request_uri; }
因为$uri
是解码以后的请求路径,所以可能就会包含换行符,也就造成了一个CRLF注入漏洞。
这个漏洞产生的原因是请求重定向的错误配置,导致在url中输入回车换行符可以控制http响应头部。
原本的目的是为了让http的请求跳转到https上,但是$uri参数是不包含查询参数的,于是当我们在url中输入%0d%0a时,$uri参数不会将回车换行符传入,这就导致用户可以控制http响应头部
访问http://ip:8080,使用bp抓包,构造反射性xss。
%0d%0a%0d%0a<img src=1 onerror=alert(1)>
<?php header("Content-Security-Policy: frame-src http://localhost:8081/"); ?> <iframe src="http://localhost:8081/?path=http://www.baidu.com/%0a%0dX-XSS-Protection:0%0a%0d%0a%0d<script>alert(location.href)</script>"></iframe>
修复方法:把$url改为$request-uri,这个参数会传入完整的原始url请求,也就是说用户输入的所有内容都会被当做参数传入Location字段
location / { return 302 https://$host$request_uri; }
另外,由$uri
导致的CRLF注入漏洞不仅可能出现在上述两个场景中,理论上,只要是可以设置HTTP头的场景都会出现这个问题。
二、目录穿越漏洞
这个常见于Nginx做反向代理的情况,动态的部分被proxy_pass传递给后端端口,而静态文件需要Nginx来处理。
假设静态文件存储在/home/目录下,而该目录在url中名字为files,那么就需要用alias设置目录的别名:
location /files/{ alias /home/; } www.xianoupeng.com/files../ location /files/ { alias /home/ }
此时,访问http://example.com/files/readme.txt
,就可以获取/home/readme.txt
文件。
但我们注意到,url上/files
没有加后缀/
,而alias设置的/home/
是有后缀/
的,这个/
就导致我们可以从/home/
目录穿越到他的上层目录:
进而我们获得了一个任意文件下载漏洞。
/user/share/nginx/html/config.php 有mysql配置 mysql 用户名和密码
如何解决这个漏洞?只需要保证location和alias的值都有后缀/
或都没有这个后缀。
三、Http Header被覆盖的问题
众所周知,Nginx的配置文件分为Server、Location、If等一些配置块,并且存在包含关系,和编程语言比较类似。如果在外层配置的一些选项,是可以被继承到内层的。
但这里的继承也有一些特性,比如add_header,子块中配置后将会覆盖父块中的add_header添加的所有HTTP头,造成一些安全隐患。
如下列代码,Server块添加了CSP头:
server { ... add_header Content-Security-Policy "default-src 'self'"; #这个头部用于配置浏览器加载和执行内容的安全策略。在这里,设置为 "default-src 'self'" 表示只允许从同源(即当前域名)加载内容,其他来源的内容被禁止加载。 add_header X-Frame-Options DENY; #防止页面被嵌入到 <frame>、<iframe> 或其他框架中。设置为 DENY 表示不允许任何形式的嵌入。 location = /test1 { rewrite ^(.*)$ /xss.html break; } location = /test2 { add_header X-Content-Type-Options nosniff; #该头部防止浏览器尝试嗅探未知 MIME 类型的响应内容 rewrite ^(.*)$ /xss.html break; } }
这个配置本意是想在子块中再添加一个参数,结果覆盖父块,导致父块参数全部失效
bp抓包发现父块添加的参数消失,只有子块里的参数
修复方法:删除子块中的add_header参数,或者添加到父块中