探究python中pickle,_pickle和pickletools的解析差异问题
字数 1765 2025-08-22 18:37:27

Python中pickle、_pickle和pickletools的解析差异研究

前言

本文深入分析Python中三个与pickle相关的模块:pickle_picklepickletools之间的解析差异。这些差异在正常情况下可能不会引起注意,但在某些特定场景下(如CTF比赛或安全审计)可能成为关键点。

模块概述

  1. pickle:Python标准库中的纯Python实现
  2. _pickle:pickle模块的C语言加速实现
  3. pickletools:pickle的反汇编器和分析工具

关键差异点分析

1. STOP操作码后的栈检查差异

问题描述

  • pickletools.dis()会在操作结束后检查栈是否为空,不为空则报错
  • pickle_pickle则不会进行此检查

利用方法
构造payload使栈在操作结束后不为空:

payload = b"NN."  # NONE, NONE, STOP
  • pickle_pickle:成功解析
  • pickletools:报错"stack not empty after STOP"

2. APPENDS/ADDITEMS操作码的零元素检查差异

问题描述

  • _pickle(C实现)会检查APPENDS/ADDITEMS操作码是否有零个元素,若有则直接返回
  • pickle(Python实现)不进行此检查,会尝试调用append/add方法

利用方法
构造payload:

payload = b"N(e."  # NONE, MARK, APPENDS, STOP
  • _pickle:成功解析(零元素直接返回)
  • pickle:报错(尝试对None调用append方法)
  • pickletools:成功解析(仅静态分析)

3. BUILD操作码的state检查差异

问题描述

  • _pickle检查state是否为Py_None
  • pickle仅检查if state(空字典/元组等也会返回False)

利用方法
构造payload:

payload = b"]]b."  # EMPTY_DICT, EMPTY_DICT, BUILD, STOP
  • _pickle:报错(空字典被视为有效state)
  • pickle:成功解析(空字典被视为False)
  • pickletools:成功解析

4. MARK操作码的处理差异

问题描述

  • pickletools仅模拟操作,不实际执行
  • pickle_pickle有更严格的运行时检查

利用方法
构造payload:

payload = b"(."  # MARK, STOP
  • pickletools:成功解析(仅模拟)
  • pickle:报错(尝试从空栈弹出)
  • _pickle:报错(发现意外的MARK)

5. 空字节处理差异

问题描述

  • _pickle(C实现)将空字节视为字符串终止符
  • picklepickletools会尝试处理整个字符串

利用方法
构造包含空字节的INT操作:

payload = b"I\x00\x00\x00."  # INT with null bytes, STOP
  • _pickle:成功解析(读取到第一个空字节)
  • picklepickletools:报错(无效的整数字面量)

总结表

检查点 pickle _pickle pickletools 关键差异
1 成功 成功 失败 pickletools检查栈是否为空
2 失败 成功 成功 _pickle检查零元素APPENDS
3 成功 失败 成功 state检查严格性不同
4 失败 失败 成功 pickletools仅静态分析
5 失败 成功 失败 空字节处理方式不同

安全启示

  1. 不要混合使用不同实现进行验证和执行:用pickletools验证后用pickle加载可能导致安全问题
  2. 注意边界条件检查:不同实现在边界条件(如空集合、空字节等)处理上可能有差异
  3. 严格输入验证:即使通过了某个实现的验证,也可能在其他实现中导致问题

这些差异在正常情况下可能不会显现,但在安全敏感场景下可能成为漏洞利用的关键点。理解这些差异有助于编写更健壮的代码和进行更有效的安全审计。

Python中pickle、_ pickle和pickletools的解析差异研究 前言 本文深入分析Python中三个与pickle相关的模块: pickle 、 _pickle 和 pickletools 之间的解析差异。这些差异在正常情况下可能不会引起注意,但在某些特定场景下(如CTF比赛或安全审计)可能成为关键点。 模块概述 pickle :Python标准库中的纯Python实现 _ pickle :pickle模块的C语言加速实现 pickletools :pickle的反汇编器和分析工具 关键差异点分析 1. STOP操作码后的栈检查差异 问题描述 : pickletools.dis() 会在操作结束后检查栈是否为空,不为空则报错 pickle 和 _pickle 则不会进行此检查 利用方法 : 构造payload使栈在操作结束后不为空: pickle 和 _pickle :成功解析 pickletools :报错"stack not empty after STOP" 2. APPENDS/ADDITEMS操作码的零元素检查差异 问题描述 : _pickle (C实现)会检查APPENDS/ADDITEMS操作码是否有零个元素,若有则直接返回 pickle (Python实现)不进行此检查,会尝试调用append/add方法 利用方法 : 构造payload: _pickle :成功解析(零元素直接返回) pickle :报错(尝试对None调用append方法) pickletools :成功解析(仅静态分析) 3. BUILD操作码的state检查差异 问题描述 : _pickle 检查state是否为 Py_None pickle 仅检查 if state (空字典/元组等也会返回False) 利用方法 : 构造payload: _pickle :报错(空字典被视为有效state) pickle :成功解析(空字典被视为False) pickletools :成功解析 4. MARK操作码的处理差异 问题描述 : pickletools 仅模拟操作,不实际执行 pickle 和 _pickle 有更严格的运行时检查 利用方法 : 构造payload: pickletools :成功解析(仅模拟) pickle :报错(尝试从空栈弹出) _pickle :报错(发现意外的MARK) 5. 空字节处理差异 问题描述 : _pickle (C实现)将空字节视为字符串终止符 pickle 和 pickletools 会尝试处理整个字符串 利用方法 : 构造包含空字节的INT操作: _pickle :成功解析(读取到第一个空字节) pickle 和 pickletools :报错(无效的整数字面量) 总结表 | 检查点 | pickle | _ pickle | pickletools | 关键差异 | |--------|--------|---------|-------------|----------| | 1 | 成功 | 成功 | 失败 | pickletools检查栈是否为空 | | 2 | 失败 | 成功 | 成功 | _ pickle检查零元素APPENDS | | 3 | 成功 | 失败 | 成功 | state检查严格性不同 | | 4 | 失败 | 失败 | 成功 | pickletools仅静态分析 | | 5 | 失败 | 成功 | 失败 | 空字节处理方式不同 | 安全启示 不要混合使用不同实现进行验证和执行 :用pickletools验证后用pickle加载可能导致安全问题 注意边界条件检查 :不同实现在边界条件(如空集合、空字节等)处理上可能有差异 严格输入验证 :即使通过了某个实现的验证,也可能在其他实现中导致问题 这些差异在正常情况下可能不会显现,但在安全敏感场景下可能成为漏洞利用的关键点。理解这些差异有助于编写更健壮的代码和进行更有效的安全审计。