Java 反射与 URLDNS 链分析
Java反序列化基础篇-02-Java 反射与 URLDNS 链分析
代码详见上一篇文章Java反序列化基础篇-01-反序列化概念与利用
切记,反射只是一种攻击的中间手段而不是最终的手段。
真正的攻击思路还是我们上篇文章里讲的
首先的攻击前提:继承 Serializable
入口类:source (重写 readObject 调用常见的函数;参数类型宽泛,比如可以传入一个类作为参数;最好 jdk 自带)
找到入口类之后要找调用链 gadget chain 相同名称、相同类型
执行类 sink (RCE SSRF 写文件等等)比如 exec
这种函数
0x01 前言
- 书接上文,我们在上文当中详细介绍了 Java 反序列化的一些基础性的知识,到最后分析了 URLDNS 链的利用。但是离最后的 POC 我们还差一步之遥。
URLDNS 的链想要成功构造,必须要懂反射,而本篇文章中,我会细致地带大家入门 Java 反射。
引用 P神 的一句话
Java 安全可以从反序列化说起,而反序列化可以从反射说起。
0x02 反射理解
- 反射的作用:让 Java 具有动态性
Java 的反射机制是指在运行状态中,对于任意一个类都能够知道这个类所有的属性和方法; 并且对于任意一个对象,都能够调用它的任意一个方法;这种动态获取信息以及动态调用对象方法的功能成为 Java 语言的反射机制。
Java 本身是一种静态语言,为什么这么说,看这一段代码就知道了。
1 | Student student = new Student(); |
那反过来,什么是动态语言呢?PHP 本身就拥有很多动态特性,我们来看这一段代码。在这一段代码里面,我们输入 eval
,php 就执行 eval
命令;输入 echo
就执行 echo
命令;这就是语言的动态特性。
1 | eval( eval() ) |
1. 正射与反射
- 提一嘴正射与反射的概念是为了让大家能够更好的理解 Java 反射。
正射
我们在编写代码时,当需要使用到某一个类的时候,都会先了解这个类是做什么的。然后实例化这个类,接着用实例化好的对象进行操作,这就是正射。
1 | Student student = new Student(); |
反射
反射就是,一开始并不知道我们要初始化的类对象是什么,自然也无法使用 new 关键字来创建对象了。我们以这一段经典的反射代码为例说明。
还记得我们第一课讲的 Person.java 文件吗?
我们这里新建一个 ReflectionTest.java 文件,并在其中添加如下代码。
1 | public static void main(String[] args) throws Exception{ |
我们注意到在代码块中出现了大写 C 开头的 Class;
理解反射的第一步就必须先搞清楚 Class
是什么。
2. Java Class 对象理解
我们程序在运行的时候会编译生成一个 .class
文件,而这个 .class
文件中的内容就是相对应的类的所有信息,比如这段程序当中:
1 | public static void main(String[] args) throws Exception{ |
其实 **person.class
就是 Class
**,Class 也就是描述类的类。
Class 类的对象作用是运行时提供或获得某个对象的类型信息。
所以反射其实就是操作
Class
,看清楚了,是大 C
0x03 Java 反射组成相关的类
反射机制相关操作一般位于java.lang.reflect包中。
而java反射机制组成需要重点注意以下的类:
java.lang.Class:类对象;
java.lang.reflect.Constructor:类的构造器对象;
java.lang.reflect.Field:类的属性对象;
java.lang.reflect.Method:类的方法对象;
0x04 Java 反射使用方法
前文铺垫了那么多的基础知识,所以我们的 Java 反射该如何利用与实现,又该如何辅助我们帅气的 **弹shell **呢?
获取类的方法:forName
实例化类对象的方法:newInstance
获取函数的方法:getMethod
执行函数的方法:invoke
1. 首先需要实例化对象
对于普通用户我们可以采用以下方法创建实例:
1 | Person test = new Person(); |
而我们在创建 Class 类的实例对象却不能使用上述方法,运行会抛出错误
1 | Class test = new Class(); |
同时我们可以跟进 Class 类的源码进行查看,发现其构造器是私有的,所以只有 JVM 能够创建 Class 对象。
因为 Class 类是 private
私有属性,我们也无法通过创建对象的方式来获取 class 对象,那么我们怎样才能够获取到 class 对象呢?一般我们获取 class 对象就有以下三种方法,我们来逐一看看。
方法一、实例化对象的getClass()方法
如果上下⽂中存在某个类的实例 obj
,那么我们可以通过 obj.getClass
来获取它的类。
1 | TestReflection testReflection = new TestReflection(); |
方法二、 使用类的 .class 方法
如果你已经加载了某个类,只是想获取到它的 java.lang.Class
对象,那么就直接拿它的 class
属性即可。这个⽅法其实不属于反射。
1 | Class class2 = TestReflection.class; |
方法三、Class.forName(String className):动态加载类
如果你知道某个类的名字,想获取到这个类,就可以使⽤ forName
来获取,后续要利用的话是需要实例化的。
1 | Class class1 = Class.forName("reflection.TestReflection"); |
我们可以写个简单的示例代码,分别利用这三种方法获取当前类Class对象的当前类名。
ReflectionTest01.java
1 | package src; |
当时学的时候这里挺疑惑的,就总想着用反射直接攻击别人,其实这是挺不靠谱的。
问了好多师傅们,都说反射只是一种手段,后面看了狂神的视频之后才醒悟过来,发现反射确实只是一种手段。而最后的攻击的 gadget chain 还是要自己构造的 ~
下面讲的三种,都是获取类里面的东西。
2. 获取成员变量 Field
获取成员变量Field位于 java.lang.reflect.Field
包中
Field[] getFields() :获取所有 public 修饰的成员变量
Field[] getDeclaredFields() 获取所有的成员变量,不考虑修饰符
Field getField(String name) 获取指定名称的 public 修饰的成员变量
Field getDeclaredField(String name) 获取指定的成员变量
3. 获取成员方法 Method
- 要注意以下,第一个参数是传参,第二个参数是确定重载的是哪个函数。
1 | Method getMethod(String name, 类<?>... parameterTypes) //返回该类所声明的public方法 |
在 Person.java 中添加如下代码
1 | public void study(String s) { |
并在 ReflectionTest02.java 中添加如下
1 | package src; |
运行结果
4. 获取构造函数 Constructor
1 | Constructor<?>[] getConstructors() :只返回public构造函数 |
在 forName 之后获取构造函数
- 新建一个文件 PersonConstructor.java
1 | package src; |
ReflectionTest03.java
1 | package src; |
0x05 反射的进阶使用
1. 反射创建对象
反射创建对象,也叫做反射之后实例化对象,这里用到的是我们之前讲过的 newInstance()
方法
- 代码
1 | Class c = Class.forName("类的名称"); // 创建Class对象 |
这里也顺便说下 invoke 方法,invoke 方法位于 java.lang.reflect.Method 类中,用于执行某个的对象的目标方法。
一般会和 getMethod 方法配合进行调用。
用法
1 | public Object invoke(Object obj, Object... args) |
第一个参数为类的实例,第二个参数为相应函数中的参数
obj:从中调用底层方法的对象,必须是实例化对象
args: 用于方法的调用,是一个 object 的数组,参数有可能是多个
但需要注意的是,invoke 方法第一个参数并不是固定的:
如果调用这个方法是普通方法,第一个参数就是类对象;
如果调用这个方法是静态方法,第一个参数就是类;
将我们的知识进行整合归纳下,我们可以写个完整的小例子。
1 | package src; |
这就是反射,简单吧。回归到我们上篇文章讲的 URLDNS,我们需要完成这样一些操作。
0x06 关于反射的小结
先讲完反射,再去以 URLDNS 链为例
说白了反射也就这点东西,先获取类,并进行实例化对象;
然后获取类里面的属性;调用类里面的方法,就没了。
0x07 URLDNS 链讲解
上节我们半路上杀出了一个程咬金,这里我们把它解决掉。
要修改 hashCode 先为不是 -1 的值,再改回 -1 其实很简单,我们先获取类,并且实例化对象。我们来看 Payload,其实一下就明白了。
1 | // 这里不要发起请求 |
getClass() 实例化对象,接着就是获取类的方法,获取类的属性并修改。
0x08 利用反射弹计算器
用我们的 forName
与 newInstance()
实例化对象后,再进行获取方法,执行。
1 | package src.ReflectDemo; |
这里报错了,原因是 java.lang.Runtime
是私有的。
我们换一种方式弹计算器,不过加上 setAccessible(true)
也可以
1 | package src.ReflectDemo; |
成功弹出来了。
当然,上方的代码太冗长,可以将上方的代码稍微修整一下:
1 | Class c1 = Class.forName("java.lang.Runtime"); |
0x09 小结
这篇文章就打算先如此了,因为篇幅过长,再写一篇 Java 反射进阶的。
对于 Java 反射其实梳理的还可以吧,简单来说,反射可以辅助于反序列化。
- 本文标题:Java反序列化基础篇-02-Java反射与URLDNS链分析
- 创建时间:2022-05-20 12:14:10
- 本文链接:2022/05/20/Java反序列化基础篇-02-Java反射与URLDNS链分析/
- 版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!