利用docker复现Nginx配置漏洞

胧月北宸 / 2023-08-12 / 原文

三个案例看Nginx配置安全

一、$uri导致的CRLF注入漏洞

下面两种情景十分常见:

  1. 用户访问http://example.com/aabbcc,自动跳转到https://example.com/aabbcc

  2. 用户访问http://example.com/aabbcc,自动跳转到http://www.example.com/aabbcc

第二个场景主要是为了统一用户访问的域名,更加有益于SEO优化。

在跳转的过程中,我们需要保证用户访问的页面不变,所以需要从Nginx获取用户请求的文件路径。查看Nginx文档,可以发现有三个表示uri的变量:

  1. $uri

  2. $document_uri

  3. $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)>

 

 

 

同理加两个换行符可以将数据写入响应体,通过这种注入可实现xss攻击。

<?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参数,或者添加到父块中