前言
参考:
https://www.leavesongs.com/PENETRATION/client-session-security.html
https://paoka1.top/2022/05/28/Flask-%E7%9A%84-SESSION-%E4%BC%AA%E9%80%A0/
Session ID
是服务器为每个用户在登录时生成的一个随机字符串,用于标识用户身份和会话状态,即作为认证用户身份的凭证,通常存储在 Cookie 中
特点:
- 用户不可以任意篡改
- A用户的 session 无法被B用户获取
出现场景&分类
PHP 中:$_SESSION 变量的内容默认会被保存在服务端的一个文件中,通过一个叫 PHPSESSID 的 Cookie 来区分用户。这类 session 是“服务端session”,用户看到的只是 session 的名称(一个随机字符串),其内容保存在服务端
Django 中:默认将 session 存储在数据库中
Flask 中:Flask 并不包含数据库框架的操作,只能将 session 存储在 cookie 中
Flask 实现 session 签名
from flask import Flask, session
app = Flask(__name__)
app.secret_key = 'SCFmkpovdDVCJPO21cvcds'
@app.route('/')
def set_session():
if 'name' in session:
name = session['name']
if name == "anonymous":
return 'Session 已设置,你是 anonymous'
elif name == 'admin':
return 'Session 已设置,你是 admin'
else:
return 'Session 已设置,你是 ???'
session['name'] = 'anonymous'
return 'Session 未设置,现已设置'
if __name__ == '__main__':
app.run(debug=False, port=8080)
过程
- json.dumps 将对象转换成 json 字符串,作为数据
- 如果数据压缩后长度更短,则用 zlib 库进行压缩
- 将数据用 base64 编码
- 通过 hmac 算法计算数据的签名,将签名附在数据后,用 “.” 分割
在第四步中 Flask 通过一个 secret_key 来对 cookie 内容进行签名,而签名的作用是防篡改,无法防止被读取,Flask 并没有提供加密操作,所以 session 的全部内容都可以在客户端被读取
一旦获取到 secret_key,结合客户端获取到 session 内容,我们就可以尝试签名伪造 session
CodeIgniter 2.1.4 session 伪造及对象注入漏洞
Codeigniter 2 的 session 也储存在 session 中,默认名为 ci_session,虽然是 PHP 框架,其 session 数据使用 PHP 自带的 serialize 函数进行序列化,并签名后作为 ci_session 的值,原理上和 Flask 相似
https://www.dionach.com/blog/codeigniter-session-decoding-vulnerability
https://github.com/Dionach/CodeIgniterXor
工具
简易解密脚本
#!/usr/bin/env python3
import sys
import zlib
from base64 import b64decode
from flask.sessions import session_json_serializer
from itsdangerous import base64_decode
def decryption(payload):
payload, sig = payload.rsplit(b'.', 1)
payload, timestamp = payload.rsplit(b'.', 1)
decompress = False
if payload.startswith(b'.'):
payload = payload[1:]
decompress = True
try:
payload = base64_decode(payload)
except Exception as e:
raise Exception('Could not base64 decode the payload because of '
'an exception')
if decompress:
try:
payload = zlib.decompress(payload)
except Exception as e:
raise Exception('Could not zlib decompress the payload before '
'decoding the payload')
return session_json_serializer.loads(payload)
if __name__ == '__main__':
print(decryption(sys.argv[1].encode()))
flask_session_cookie_manager 进行 Session 加密解密
在安装的文件夹中使用终端执行命令
解密:
python flask_session_cookie_manager3.py decode -s "secret_key" -c "需要解密的session值"
加密:
python flask_session_cookie_manager3.py encode -s "secret_key" -t "需要加密的session值"
由此可以抓包进行session伪造实现身份认证绕过
flask-unsign 进行 secret_key 爆破与 session 加解密
爆破key,dict.txt需要看情况自己生成,一般是固定位数的随机数
flask-unsign --unsign --cookie "session值" --wordlist dict.txt
加密:
flask-unsign --sign --cookie "要加密的字符串" --secret 'secret_key' --no-literal-eval
安全隐患&题目思路
- 签名使用 hash 函数而非 hmac 函数,导致利用 hash 长度扩展攻击来伪造 session
- 任意文件读取导致密钥泄露,进一步造成身份伪造漏洞或反序列化漏洞(常见)
- 如果客户端 session 仅加密未签名,利用 CBC 字节翻转攻击,我们可以修改加密 session 中某部分数据,来达到身份伪造的目的(ACTF-2025 not so web 1)