Python中的反序列化安全问题
最近在研究反序列化的问题。打算把python,PHP,Java中的反序列化漏洞都说一说。python的最好理解,权当抛砖引玉。
简介
我们知道,在Java和PHP中都实现了对象的序列化和反序列化能力。而在python中,其实也有类似的实现。
在python中,序列化用于将存在于内存中的对象或变量转化成二进制的字节流.这样可以将类对象的状态和属性保存下来。在需要用到的时候再对其进行反序列化成对象。
官方库中,主要提供了三个模块用于序列化实现:pickle(cpickle),marshal,json
其中json模块应该是最为人所知的,它主要提供python字典,列表等数据类型和字符串之间互相转换的能力;
而marshal和pickle模块则可以对python中的类和对象进行序列化和反序列化。
实现方法
简单介绍一下pickle模块的用法。
在pickle中,序列化的接口常用的有两个:
1 | |
前者必要参数是进行序列化的对象和一个文件对象,序列化的内容将被保存至文件中。后者仅需要提供对,序列化的二进制数据将会作为返回;
相对应,反序列化也有两个:
1 | |
分别可以从文件或字节流中反序列化出对象。举个例子来看:
1 | |
代码首先定义一个 SerializePerson 类,类包含两个属性:name和age。
我们实例化了一个对象 person 同时使用构造函数__init__ 对变量赋值。
之后对person 对象,分别通过pickle.dump 和 pickle.load 做序列化和反序列化。
漏洞成因
我们都知道,Java中的反序列化漏洞主要在于反序列化时会调用对象的readObject方法,在PHP中则有更多的魔术方法可能在反序列化或toString等情况下调用。
而在python中,同样的有几个内置方法,会在对象被反序列化时调用。他们分别是:
1 | |
关于这三个方法的具体的用法可以参考官方文档中的描述:
参考PHP的反序列化。如果我们可以在类中构造这些方法,并在方法中执行恶意代码。那么实例在反序列化时就会执行我们的代码了。
更加尴尬的是,python并没有对pickle模块做任何安全性的限制:他没有验证反序列化的类是否在应用中注册,也没有类似Java中SerializeUID的存在。这也就导致了,攻击者任意构造的对象都会被实现了pickle.load的接口进行反序列化并执行Magic function。
可以使用一个简单的 poc 来验证:
1 | |
执行代码至pickle.loads(tmp),计算器被弹出:所以对于任意实现了pickle序列化的接口,如果我们可以控制传入的将要反序列化的对象,那么我们就可以执行任意代码了。
案例分析
由于不少的三方库都使用了pickle来做反序列化。因此如果没有其他的安全处理和过滤就很容易产生安全问题。
举个例子:pandas作为python里最为强大的数据分析和处理库,在几乎全版本中都存在pickle反序列化漏洞的问题。
其中的接口 pandas.read_pickle(filename) 实现了读取pkl类型文件的功能。
对于数据分析工程师和算法工程师来说,这个文件可能是数据集或者别人已经训练好的模型。而当读取的文件是我们恶意构造的对象时,他就可以在目标应用中执行任意代码
问题代码的 pandas.read_pickle() 位于 pands/io/pickle.py 100行左右。我们可以看到他是直接调用了 pickle.load() 这一函数。
1 | |
POC:
1 | |