目录
1. CRLF注入漏洞
Bottle HTTP头注入漏洞
2.目录穿越漏洞
3. http add_header被覆盖
本篇要复现的漏洞实验有一个网站直接为我们提供了Docker的环境,我们只需要下载下来就可以使用:
Docker环境的安装可以参考:Docker安装
漏洞环境的搭建具体步骤如下:
(1)首先我们需要下载环境的源码:
https://github.com/vulhub/vulhub/archive/master.zip
(2)将下载的源码上传到centos环境中
(3)然后进入/root/vulhub-master/nginx目录中,这就是该文件为我们提供的有关nginx的一些漏洞环境
(4)然后我们就可以移动到对应的环境中使用一下命令来拉取环境
docker compose up -d
然后就可以正常使用了
1. CRLF注入漏洞
CRLF注入漏洞,也叫做HTTP头部注入漏洞
在HTTP头部每一行的结尾都会有一个 \r \n
下面两种情景十分常见:
用户访问http://example.com/aabbcc,自动跳转到https://example.com/aabbcc (自动将http->https)
用户访问http://example.com/aabbcc,自动跳转到http://www.example.com/aabbcc(自动增加一级域名)
第二个场景主要是为了统一用户访问的域名,更加有益于SEO优化。
在跳转的过程中,我们需要保证用户访问的页面不变,所以需要从Nginx获取用户请求的文件路径。
查看Nginx文档,可以发现有三个表示uri的变量:
$uri
$document_uri
$request_uri
1和2表示的是解码以后的请求路径,不带参数;3表示的是完整的URI(没有解码)。
错误的配置文件示例(原本的目的是为了让http的请求跳转到https上):
location / {
return 302 https://$host$uri;
}
#因为$uri是解码以后的请求路径,所以就会包含换行符,就有可能造成CRLF漏洞 这个CRLF注入漏洞,导致固定漏洞、设置cookie引发的csrf漏洞或者xss漏洞,其中,我们可以注入两个\r \n即可控制HTTP体进行xss,但是因为浏览器会认为这是一个300跳转,所以并不会显示我们注入的内容
Payload: http://your-ip:8080/%0d%0aSet-Cookie:%20a=1,可注入Set-Cookie头。
这种情况下,我们可以使用一些技巧:比如使用CSP头来ifame的地址,这样浏览器并不会跳转,进而执行我们插入的HTML
下面我简单的使用这个漏洞环境中的/root/vulhub-master/nginx/insecure-configuration环境来演示一下CRLF漏洞
(1)首先我们移动到对应的环境下
(2)使用docker compose up -d来拉取环境
[root@centos111 insecure-configuration]# docker compose up -d
[+] Running 1/1
✔ Container insecure-configuration-nginx-1 Started
注:拉取环境的前提是我们安装了Docker
(3)可以使用dokcer ps 来查看一下容器是否运行:
可以看到正常的运行了
(4)现在可以做一个简单的测试,在shell中使用curl来尝试在头部增加一个%0d%0d,也就是\r\n,然后插入一条语句
curl -I http://127.0.0.1:8080/%0d%0aSet-cookie:%20a=1
HTTP/1.1 302 Moved Temporarily
Server: nginx/1.13.0
Date: Fri, 24 Nov 2023 01:25:36 GMT
Content-Type: text/html
Content-Length: 161
Connection: keep-alive
Location: http://127.0.0.1:8080/
Set-cookie: a=1
可以看到我们利用%0d%0a成功的在头部增加了一行Set-cookie:a=1
(5)那么我们现在就可以使用Burpsuite来抓一个这个网页的包,尝试使用CRLF注入
在本机上打开BP软件,开启代理,然后打开本机的代理
我们可以构造一个反弹性xss的脚本插入到头部中:
<script>alert(1)</script>
可以看到我们成功的利用%0d%0a在头部插入了script弹窗代码,使用两个%0d%0a的原因是因为第一个是让我们来到新的一行,第二个换行是为了与头部分割一行,否则就无法正常插入
下面用这样一个案例来举例Python中的一个CRLF漏洞
Bottle HTTP头注入漏洞
这时我们要运行的bottle_demo.py代码
import bottle
from bottle import route,run, template,request,response
@route('/')
def index():
path = request.query.set('path','http://www.xianoupeng.com')
return bottle.redirect(path)
if __name__ == '__main__':
bottle.debug(True)
run(host='localhost', port=8081)
因为redirect函数是向response中插入一个HTTP头,也就是Location: xxx
,所以存在头注入。
(1)我们运行这个python脚本,然后尝试使用\r\n的方式进行弹窗
但实际测试的过程中遇到了一个有趣的问题,看看redirect函数的实现:
def redirect(url, code=None):
""" Aborts execution and causes a 303 or 302 redirect, depending on
the HTTP protocol version. """
if not code:
code = 303 if request.get('SERVER_PROTOCOL') == "HTTP/1.1" else 302
res = response.copy(cls=HTTPResponse)
res.status = code
res.body = ""
res.set_header('Location', urljoin(request.url, url))
raise res</pre>
其中使用了一个urljoin,将当前url和我传入的path进行了一次"join",经过这个操作事情就变得很微妙了:Location
头一定有一个值。这种情况下,浏览器就不会渲染页面,会直接跳转到Location头指向的地址。也就是说,如果我要利用CRLF构造XSS的话,这里是不会触发的。
回想上面提到过的新浪的那个CRLF,那个漏洞的Location
是可以为空的,如果浏览器发现Location
为空就不会进行跳转,进而渲染了后面注入的HTML,造成XSS。
(2)可以使用一下两种方式来解决这个问题
方法1:将跳转的url端口设为<80
http://localhost:8081/?path=www.oupeng.com:0%0d%0aX-XSS-Protection:0%0d%0a%0d%0a%3Cscript%3Ealert(location.href)%3C/script%3E
python中确实收到了请求
但是页面还是告诉我们无法访问
注:这种方法在现在的浏览器中无法正常实现,如果需要复现,需要降低浏览器的版本
方法2:使用CSP禁止iframe的跳转
可以在运行Python脚本的前提下去在本地新建一个web.php代码,通过访问php文件使用iframe将httpL//localhost:8081页面嵌入,从而实现弹窗。
php代码:
<?php
header("Content-Security-Policy: frame-src http://localhost:8081/"); //这里设置了CSP
?>
<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>
通过尝试发现Firefox可以弹窗,但是Chrome不能。
如果python代码是这样的则上面的两种方式均可以实现
import bottle
from bottle import route,run, template,request,response
@route('/')
def index():
server = bottle.request.query.get('server') # 接收Server参数
bottle.response.add_header('Server', server)
return bottle.response
if __name__ == '__main__':
bottle.debug(True)
bottle.run(host='localhost', port=8081)
总结一下,安全的做法应该为:
不安全:
location / {
return 302 https://$host$uri;
}
安全
location / {
return 302 https://$host$request_uri; //Request不会对%0d%0a解码,所以无法换行
}
2.目录穿越漏洞
这个常见于nginx做反向代理的情况,动态的部分被proxy_pass传递给后端端口,而静态的文件需要nginx来处理。
假设静态文件存储在/home/目录下,而该目录在url中名字为files,那么就需要用alias设置目录的别名:
Nginx在配置别名(Alias)的时候,如果忘记加/
,将造成一个目录穿越漏洞。
错误的配置文件示例(原本的目的是为了让用户访问到/home/目录下的文件):
location /files { #这里没有 /
alias /home/; #因为真正的文件在/home下,所以这里设置了别名/home/
}
此时我们的dokcer环境中的文件配置就是这样的:
server {
listen 8081;
root /usr/share/nginx/html;
index index.html;
server_name _;
autoindex on;
location /files {
alias /home/;
}
}
此时访问192.168.159.200:8081,就可以获取到/files/help.txt文件
但是我们注意到,url上/files没有加后缀/ ,而alias设置的/home/是有后缀/ 的,这个 / 就导致我们可以从 /home/ 目录穿越到它的上层目录:
Payload: http://your-ip:8081/files../
此时我们就可以看到我们可以移动到/目录下,这时就会出现任意文件下载的危险漏洞
解决方案:必须保证location和alias的值都有后缀/ 或者都没有这个后缀
我们修改配置文件为:
server {
listen 8081;
root /usr/share/nginx/html;
index index.html;
server_name _;
autoindex on;
location /files/ {
alias /home/;
}
}
重启docker:
docker restart 你的镜像编号
然后再次尝试路径穿越访问:
可以看到已经无法完成路径穿越了。
3. http add_header被覆盖
CSP:(Content-Security-Policy) 可以防御xss注入
Nginx配置文件子块(server、location、if)中的add_header
,将会覆盖父块中的add_header
添加的HTTP头,造成一些安全隐患。
如下列代码,整站(父块中)添加了CSP头:
server{
add_header Content-Security-Policy "default-src 'self'";
#default-src用来设置上面各个选项的默认值。
#上面代码限制所有的外部资源,都只能从当前域名加载。
add_header X-Frame-Options DENY;
//这里会覆盖父类的CSP,CSP就失效了
location = /test1 {
rewrite ^(.*)$ /xss.html break;
}
location = /test2 {
add_header X-Content-Type-Options nosniff;
rewrite ^(.*)$ /xss.html break;
}
#这里的location会将前面的location覆盖
}
但/test2
的location中又添加了X-Content-Type-Options
头,导致父块中的add_header
全部失效,
所以XSS可以被触发:
可以看到我输入的js代码中的标签被浏览器转义,这里并没有成功,那么可以尝试在低版本的浏览器中测试一下:
可以看到,在低版本中我们是可以利用这个错误配置正常弹窗的。