【代码审计】Java代码审计反射篇

「Java安全」https://pan.quark.cn/s/56c5a74545e7
Java的安全问题常常从反序列化漏洞谈起,而反序列化漏洞又与反射紧密相关。反射是Java中的一个强大功能,它允许程序在运行时动态地加载类、调用方法、访问字段等。通过反射,Java能够将这种静态语言赋予动态特性。
“动态特性”是指代码在运行时可以改变其行为。例如,通过改变代码中的某些变量,可能会导致代码的执行结果发生变化。虽然Java不像PHP那样灵活,但通过反射,Java也能够提供一定的动态特性。
反射在安全研究中常用于绕过沙盒(如限制某些类的访问)。例如,通过反射可以绕过输入限制来加载并执行潜在危险的类。假设你只能获取一个Integer类型的输入,但可以利用反射加载并执行Runtime类中的命令。

第一部分

反射的核心功能

反射是通过以下几种方法实现的:

  1. 获取类

    • Class.forName(className):通过类名加载类。
    • obj.getClass():通过已实例化对象来获取其类。
    • Test.class:如果类已经加载,可以直接通过.class属性来获取。
  2. 实例化对象

    • clazz.newInstance():通过反射创建类的实例。
  3. 获取方法并执行

    • clazz.getMethod(methodName):通过反射获取类中的方法。
    • method.invoke(clazz.newInstance()):调用获取到的方法。

Class.forName 的详细解析
Class.forName方法有两个重载版本:

  • Class<?> forName(String name):根据类名加载类。
  • Class<?> forName(String name, boolean initialize, ClassLoader loader):同样加载类,但可以指定是否初始化类,并通过指定ClassLoader来加载类。

第⼆个参数 initialize 常常被⼈人误解,我看到勾陈安全实验室有篇讲反射机制的⽂文章(http://www.polaris-lab.com/index.php/archives/450/)⾥里里说到:
注意,有一点很有趣,使⽤用功能”.class”来创建Class对象的引⽤用时,不会⾃自动初始化该Class对象,使用forName()会自动初始化该Class对象

图中有说“构造函数,初始化时执⾏”,其实在 forName 的时候,构造函数并不不会执行,即使我们设置 initialize=true 。
那么这个初始化究竟指什什么呢?可以将这个“初始化”理理解为类的初始化。initialize参数决定是否初始化类。对于一些类的加载,它可能会触发类的静态初始化块。例如,以下类的静态块会在类加载时执行:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class TrainPrint {
static {
System.out.printf("Static initial %s\n", TrainPrint.class);
}

{
System.out.printf("Empty block initial %s\n", this.getClass());
}

public TrainPrint() {
System.out.printf("Initial %s\n", this.getClass());
}
}

加载这个类时,首先会执行static块,然后是实例块,最后是构造函数。

总结来说就是:loadClass()方法只对类进行加载,不会对类进行初始化。Class.forName 会默认对类进行初始化。当对类进行初始化时,静态的代码块就会得到执行,而代码块和构造函数则需要适合的类实例化才能得到执行.

反射绕过沙盒的例子

关于绕沙盒,之前Code-Breaking 2018作者出了了⼀一道SpEL的题⽬目,分享⼀一篇第三⽅方Writeup:http://rui0.cn/archives/1015

假设你有一个可以控制输入的函数,传入一个类名并通过反射加载这个类:

1
2
3
public void ref(String name) throws Exception {
Class.forName(name);
}

如果name参数由攻击者控制,他们可以利用反射加载恶意类并执行其中的静态初始化块。例如,以下恶意类在静态块中执行Runtime.exec()命令来创建文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import java.lang.Runtime;
import java.lang.Process;

public class TouchFile {
static {
try {
Runtime rt = Runtime.getRuntime();
String[] commands = {"touch", "/tmp/success"};
Process pc = rt.exec(commands);
pc.waitFor();
} catch (Exception e) {
// do nothing
}
}
}

这个恶意类可能通过Class.forName触发,导致在目标系统上执行touch /tmp/success命令,从而创建一个文件。

OPNSense freeBSD 映做





Filter
javasec-vuls-struts2
JEESNS回答处存在XSS漏洞