⼀篇⽂章带你理解漏洞之Python反序列化漏洞!
在分析了 PHP 的反序列化漏洞以后我打算将这个反序列化漏洞从语⾔层⾯进⾏扩展,然后我就看中了 python ,⽹上搜了⼀下,国内系统的讲 Python 反序列化漏洞的⽂章⽐较少,内容也是零零散散,于是我打算⾃⼰分析⼀下 Python 反序列化漏洞造成的命令执⾏和任意代码执⾏,然后弥补这个空缺。
0X01 Python 的序列化和反序列化是什么
Python 的序列化和反序列化是将⼀个类对象向字节流转化从⽽进⾏存储和传输,然后使⽤的时候再将字节流转化回原始的对象的⼀个过程。
1.⽤代码展⽰序列化和反序列化的过程:
为了看起来直观⼀些,我写了⼀个⽰例代码:
序列化⽰例代码:
import pickle
class People(object):
def __init__(self,name = "K0rz3n"):
self.name = name
def say(self):
print "Hello ! My friends"
a=People()
c=pickle.dumps(a)
print c
结果:
ccopy_reg
_reconstructor
p0
(c__main__
people
p1
c__builtin__
object
p2
p2
Ntp3
Rp4
(dp5
S'name'
p6
S'K0rz3n'
p7
sb.
先不要管这些乱七⼋糟的符号是什么(这是 PVM 虚拟机可以识别的有特殊含义的符号,我在后⾯会说),我们先看⼀下我们认识的,可以清楚地看到我们对象的属性 name K0rz3n 我们对象所属的类 people 都已近存储在⾥⾯了,如果了解过 PHP 反序列化漏洞,你⼀定知道这和 PHP 的序列化的内容⼤同⼩异,因为 PHP 也是将类名和对象的属性序列化进去的。
反序列化⽰例代码:
import pickle
class People(object):
def __init__(self,name = "K0rz3n"):
self.name = name
def say(self):
print "Hello ! My friends"
a=People()
c=pickle.dumps(a)
d = pickle.loads(c)
d.say()
结果:
Hello ! My friends
可以看到,我们成功通过反序列化的⽅式恢复了之前我们序列化进去的类对象并成功的执⾏了对象的⽅法
注意:
如果我在反序列化以前删除了 People
这个类,那么我们在反序列化的过程中因为对象在当前的运⾏环境中没有到这个类就会报错,从⽽反序列化失败。⽰例代码:
import pickle
class People(object):
def __init__(self,name = "K0rz3n"):
self.name = name
def say(self):
print "Hello ! My friends"
a=People()
c=pickle.dumps(a)
del People
d = pickle.loads(c)
结果:
AttributeError: 'module' object has no attribute 'People'
2.⽤语⾔来描述序列化和反序列化的过程:
1.序列化过程:
(1)从对象提取所有属性,并将属性转化为名值对
(2)写⼊对象的类名
(3)写⼊名值对
2.反序列化过程:
(1)获取 pickle 输⼊流
(2)重建属性列表
(3)根据类名创建⼀个新的对象
(4)将属性复制到新的对象中
注意:
这个对象只要能在当前环境下创建起来就能完成反序列化,否则则不能实现对象的重构
0X02 为什么要实现序列化和反序列化
和其他语⾔的序列化⼀样,Python 的序列化的⽬的也是为了保存、传递和恢复对象的⽅便性,在众多传递对象的⽅式中,序列化和反序列化可以说是最简单和最容易试下的⽅式
0X03 python 是怎么实现序列化和反序列化的
1.⼏个重要的函数
Python 为我们提供了两个⽐较重要的库 pickle 和 cPickle(其中 cpickle 底层使⽤ c 语⾔书写,速度是pickle 的 1000倍,但是他们的调⽤接⼝是⼀样的,我这⾥就⼀ pickle 为例)以及⼏个⽐较重要的函数来实现序列化和反序列化
序列化:
pickle.dump(⽂件)
pickle.dumps(字符串)
反序列化:
pickle.load(⽂件)
pickle.loads(字符串)
但是底层是怎么实现的呢?这⾥就不得不提及 PVM(python 虚拟机) 了,它是实现 Python 序列化和反序列化的最根本的东西
2.PVM 的组成
PVM 由三个部分组成,引擎(或者叫指令分析器),栈区、还有⼀个 Memo (这个我也不知道怎么解释,我们姑且叫它“标签区“)
1.引擎的作⽤
从头开始读取流中的操作码和参数,并对其进⾏处理,zai在这个过程中改变栈区和标签区,处理结束后到达栈顶,形成并返回反序列化的对象
2.栈区的作⽤
作为流数据处理过程中的暂存区,在不断的进出栈过程中完成对数据流的反序列化,并最终在栈上⽣成发序列化的结果
3.标签区的作⽤
数据的⼀个索引或者标记
如图所⽰:
私信⼩编007即可获取是⼗套PDF以及⼤量的学习教程!
注意:PVM 指令的书写规范
(1)操作码是单字节的
(2)带参数的指令⽤换⾏符定界
3.PVM 操作码
为了表达的清楚完整我直接附上截图
这⾥⾯要重点关注⼏个
S : 后⾯跟的是字符串
( :作为命令执⾏到哪⾥的⼀个标记
t :将从 t 到标记的全部元素组合成⼀个元祖,然后放⼊栈中
c :定义模块名和类名(模块名和类名之间使⽤回车分隔)
R :从栈中取出可调⽤函数以及元祖形式的参数来执⾏,并把结果放回栈中
. :点号是结束符(图中没有,这⾥补充)
4.反序列化流程
序列化就是⼀个将对象转化成字符串的过程,这个我们能直接使⽤ pickle 实现,这个过程我们也⽆需利⽤和分析,这⾥不做深究,我这⾥就是说⼀下如何进⾏的反序列化python怎么读的
我们将下⾯这个字符串存储为⼀个⽂件 shell.pickle
cos
system
(S'/bin/sh'
tR.
当我们使⽤下⾯这个函数对其进⾏加载的时候
>>> import pickle
>>> pickle.load(open('shell.pickle'))
执⾏结果如图所⽰:
可以看到成功返回了 sh 的 shell
我们现在来结合我们上⾯讲述的 PVM 的操作码看这个⽂件中的字符串是怎么⼀步⼀步执⾏的
(1)c 后⾯是模块名,换⾏后是类名,于是将 os.system 放⼊栈中
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论