Java安全|和反射相关的一些小补充

admin 2025年2月15日23:38:27评论7 views字数 3632阅读12分6秒阅读模式

前言

之前写了一篇Java反射的文章,现在这篇算是一点小小的补充。

正文

首先先补充一下forName方法,该方法的作用是要求JVM查找并加载指定的类,也就是说JVM「会执行该类的静态代码段」

forName有两个函数重载:

  • Class<?> forName(String name)

  • Class<?> forName(String name, 「boolean」 initialize, ClassLoader loader)

默认情况下, forName 的第⼀个参数是类名;第⼆个参数表示是否初始化;第三个参数就是 ClassLoader 。ClassLoader是一个“加载器”,在「Java安全第一篇 | 反射看这一篇就够了」,这篇文章中简单写了一点,可以去翻一翻。关于初始化,这里有一个小问题,看下面的代码:

public class ClassTest {

    public ClassTest() {
        System.out.printf("Initial %snt"this.getClass());
    }
    {
        System.out.printf("Empty block initial %snt"this.getClass());
    }
    static {
        System.out.printf("Static initial %snt", ClassTest.class);
    }
}

上述的三个“初始化”⽅法有什么区别,调⽤顺序是什么,在安全上有什么价值?我们运行一下,结果如下

Java安全|和反射相关的一些小补充
image-20220419214155369

我们发现⾸先调⽤的是 static {} ,其次是 {} ,最后是构造函数。其中, static {} 就是在“类初始化”的时候调⽤的,所以,我们的恶意代码就可以写在static {} 中,为啥呢?「因为有些恶意利用,整个过程中Java并没有执行Class文件中的任何方法,只是使用累加器加载和实例化了该类而已。所以我们需要让代码在实例化的时候就会被执行」。因此我们这类会采用静态块,也就是static {}。比如测试代码如下:

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

该模拟攻击代码比较简单,只是在mac电脑上打开文件管理器。

上文说到,我们可以通过forName加载任意类,获得类以后,我们可以继续使用反射来获取这个类中的属性、方法,也可以实例化这个类,并调用方法。而class.newInstance() 的作用就是调用这个类的无参构造函数

但是,这里又有了新的问题,就是我们有时候在写漏洞利用方法的时候,会发现使用 newInstance 总是不成功,例如,最常见的情况就是 java.lang.Runtime ,这个类在我们构造命令执行Payload的时候很常见,看如下代码:

Class clazz = Class.forName("java.lang.Runtime");
clazz.getMethod("exec", String.class).invoke(clazz.newInstance(), "id");

这时候,我们运行,发现报错了,如下:

Java安全|和反射相关的一些小补充
image-20220423181201781

我们发现这里的报错提示,说的是 Runtime 类的构造方法是私有的。

那如何解决呢?这里我们可以通过 Runtime.getRuntime() 来获取到 Runtime 对象。我们将上面的payload进行修改,代码如下:

        Class<?> cls = Class.forName("java.lang.Runtime");
        Method exec = cls.getMethod("exec", String.class);
        Method getRuntime = cls.getMethod("getRuntime");
        Object invokerun = getRuntime.invoke(cls);
        exec.invoke(invokerun,"/System/Applications/Calculator.app/Contents/MacOS/Calculator");

发现代码执行成功,弹出计算器。

Java安全|和反射相关的一些小补充
image-20220423183020393

这个问题解决了,那我们在思考一下,「如果使用的类构造函数即使是私有的,那么我们能调用吗?」

这里是有办法的,我们可以使用getDeclaredConstructor()来获取一个类的私有构造方法,现在我们将上面的代码进行如下修改

Class<?> cls = Class.forName("java.lang.Runtime");
Constructor exec = cls.getDeclaredConstructor();
exec.setAccessible(true);
cls.getMethod("exec", String.class).invoke(exec.newInstance(),"/System/Applications/Calculator.app/Contents/MacOS/Calculator");

在上面的代码中,exec.setAccessible(true);是必须要有的。我们在获取到一个私有方法后,必须用setAccessible 修改它的作用域,否则仍然不能调用。

再来一个问题,「如果一个类没有无参构造方法,也没有类似单例模式里的静态方法,我们怎样通过反射实例化该类呢?」

这里就需要用到一个新的反射方法 getConstructor,getConstructor 接收的参数是构造函数列表类型,因为构造函数也支持重载,所以必须用参数列表类型才能唯一确定一个构造函数。获取到构造函数后,我们使用 newInstance 来执行。比如,我们常用的另一种执行命令的方式ProcessBuilder

ProcessBuilder有两个构造函数
public ProcessBuilder(List<String> command)
public ProcessBuilder(String... command)  也就是可变参数

第一种的构造函数案例代码如下

Class<?> cls = Class.forName("java.lang.ProcessBuilder");
Method start = cls.getMethod("start");
start.invoke(cls.getConstructor(List.class).newInstance(Arrays.asList("/System/Applications/Calculator.app/Contents/MacOS/Calculator")));

执行成功,弹出计算器

Java安全|和反射相关的一些小补充
image-20220424215418686

第二个可变参数的构造函数,我们如何利用反射执行它呢?

其实,对于可变长参数,Java其实在编译的时候会编译成一个数组,也就是说,如下这两种写法在底层是等价的(也就不能重载):

public void hello(String[] names) {} 

public void hello(String...names) {} 

所以,我们我们可以如下构造代码:

Class<?> cls = Class.forName("java.lang.ProcessBuilder");
Method start = cls.getMethod("start");
//调用newInstance的时候,因为这个函数本身接收的就是可变参数,我们传给ProcessBuilder的也是可变参数,二者叠加为一个二维数组。
start.invoke(cls.getConstructor(String[].class).newInstance(new String[][]{{"/System/Applications/Calculator.app/Contents/MacOS/Calculator"}}));

执行成功,弹出计算器

Java安全|和反射相关的一些小补充
image-20220424220009095

参考

Java安全漫谈- 02.反射篇(2)

Java安全漫谈- 03.反射篇(3)

原文始发于微信公众号(小艾搞安全):Java安全|和反射相关的一些小补充

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年2月15日23:38:27
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   Java安全|和反射相关的一些小补充https://cn-sec.com/archives/940576.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息