JAVA-不安全的反射--RCE

简介: JAVA不安全的反射造成的RCE小案例

实际上这是一个老洞了,始于2021,今天拿出来,是因为遇到了,觉得有一定的学习价值,就拿出来分享了,大佬绕过~

废话不多说,首先存在漏洞的点位是在一个叫JobInvokeUtil的类

图片.png

接下来先对代码进行一下解读

String invokeTarget = sysJob.getInvokeTarget();

invokeTarget 调用的SysJob类里面的getInvokeTarget()方法

图片.png

紧接着,String beanName = getBeanName(invokeTarget);

将invokeTarget的值传入getBeanName()方法,该方法用来截取第一个小括号并保留小括号之前的字符串,接着利用StringUtils.substringAfterLast 方法用于获取最后一个指定分隔符之前的子字符串(这里用来获取类的路径)

图片.png

String methodName = getMethodName(invokeTarget);

与上面相似,该方法用来截取第一个小括号并保留小括号之前的字符串,接着利用StringUtils.substringAfterLast 方法用于获取最后一个指定分隔符之后的子字符串(这里用来获取类的某一个方法)

图片.png

List<Object[]> methodParams = getMethodParams(invokeTarget);

此处将invokeTarget 拆分成List<Object[]> 对象,就从invokeTarget中解析出方法的参数列表

图片.png

图片.png

if (!isValidClassName(beanName))  用来判断传入的beanName是不是合法的,他的判断方法其实就是看他有几个小数点

图片.png

这里面其实是用来判断,调用的类名是spring bean的还是lib依赖包里的,如果是spring自带的,就会利用SpringUtils.getBean(beanName)获取bean,然后利用invokeMethod()去进行实例化;

如果调用的bean是外来的,就利用Class.forName反射调用外部的类然后进行实例化操作

因为该处漏洞利用需要调用外部的类,所以这里直接走else条件分支语句


这里细说一下Object bean = Class.forName(beanName).newInstance();

Class.forName(beanName)

是Java中用于动态加载类的方法。当你传递一个类的全名(包括包名)给这个方法时,它会尝试加载该类并返回该类的 Class 对象。如果类不存在或者因为其他原因无法加载,这个方法会抛出 ClassNotFoundException。


newInstance() 是 Class 类的一个方法,它用于创建一个该类的实例。但是,它有一些限制:


1.它只能用于具有无参数构造函数的类。

2.它只能用于不是抽象的类。

3.如果类的无参数构造函数是私有的,它也会失败。


于是乎我们可以知道,想要利用这里的反射去执行恶意的类,首先这个类不能是抽象的类并且他需要具有无参构造函数的类,并且无参构造函数需要有public属性


构造函数,大家可以理解为,他的函数名字和类名字是一样的,他没有返回类型、语句;在创建类对象的时候,他会被自动调用等等,而无参构造,其实就是他是空的,函数体里面没有东西,例如下面的这个Comxxxxxxxls()无参构造函数


图片.png

invokeMethod(bean, methodName, methodParams); 这个是用来调用反射的关键方法,它使用Java的反射API来动态地调用一个对象(bean)上的方法。这个方法接受三个参数:

1.Object bean:要调用其方法的对象。

2.String methodName:要调用的方法的名称。

3.List<Object[]> methodParams:一个列表,其中每个元素都是一个对象数组,表示要传递给方法的参数。

图片.png

使用StringUtils.isNotNull(methodParams)检查methodParams是否为非空。

如果methodParams非空且大小大于0,说明有参数需要传递给方法。由于我们需要利用不安全的反射,所以我们传入的参数一定不能是空的所以直接进入到if判断里面

Method method = bean.getClass().getDeclaredMethod(methodName, getMethodParamsType(methodParams));

这里可以更写为

Class.forName(“类名”).getDeclaredMethod(“方法名”, “方法参数”);

为了更加直观,笔者在这里写了一下二者之间的对比代码

图片.png

method.invoke(bean, getMethodParamsValue(methodParams));

该代码为实际执行的代码,

invoke 方法用于动态地调用 method 方法。它接受两个参数:第一个参数是调用该方法的对象(即 bean),第二个参数是一个对象数组,包含了调用该方法所需的参数值(即 getMethodParamsValue(methodParams) 的返回值)


最后两行代码加载一块,就是典型的反射调用

Class.forName(“类名”).getDeclaredMethod(“方法名”, “方法参数”).invoke(Class.forName(“类名”).newInstance(), “参数”);


对比完整代码如下

图片.png

图片.png

可以看到,二者几乎没有区别,那么思路正确


由于代码是扣的,所以源项目跑不起来,但是不要紧,笔者在本地魔改了一下代码,方便给大家展示节目效果

首先笔者创建了一个SysJob类,该类仅仅是为了传递invokeTarget参数

图片.png

接着是第二个类,JobInvokeUtil类,该类仅仅是为了传递参数,执行反射

图片.png

我们动态调试一下,验证我们的思路

大家可以很直观的看到,beanName、methodName以及methodParams,其中methodParams是一个list object类型,里面主要有两个参数,一个是方法传递的参数,一个是方法的类型

图片.png

进入bean判断,很明显我们是调用的外部的类,所以进入到else分支

图片.png

跟进 invokeMethod方法

图片.png

首先利用getClass()方法获取字节码对象,因为bean已经被实例化了,不能直接利用 getDeclaredMethod()方法获取里面的单个方法,所以需要先getClass()获取字节码,然后再getDeclaredMethod()获取某个特定的方法;由于getDeclaredMethod()方法是java内置的方法,所以这里就省略了getMethodParamsType(methodParams)是怎么来的,getDeclaredMethod()传递两个参数,一个是方法名,一个是方法参数

图片.png

invoke 方法用于动态地调用 method 方法。它接受两个参数:第一个参数是调用该方法的对象,第二个参数是调用该方法所需的参数值

图片.png

算了,还是简单看一下getMethodParamsType(methodParams)和getMethodParamsValue(methodParams)吧

getMethodParamsType(methodParams),其实就是获取List<Object[]对象里的下标为1的参数

图片.png

getMethodParamsValue(methodParams) 其实就是获取List<Object[]对象里的下标为0的参数

图片.png

再对比下图,想必大家就明白了吧

图片.png

再给大家打印一下

图片.png

而原始代码里,其实也过滤了一下东西

图片.png

不允许调用rmi/ldap/http/https 但是他其实是调的字符串,直接用curl就行了,远程下载的地址不填http就可以,我们看一下效果

图片.png

图片.png

总结如下:

想要利用这个洞,需要找到存在危险方法的、可以传参的、存在构造函数且具备public属性的外部的类,只要输入的字符串不带http/https/rmi/ldap就行


相关文章
|
12天前
|
Java 数据库连接 Spring
反射-----浅解析(Java)
在java中,我们可以通过反射机制,知道任何一个类的成员变量(成员属性)和成员方法,也可以堆任何一个对象,调用这个对象的任何属性和方法,更进一步我们还可以修改部分信息和。
|
2月前
|
监控 Java
Java基础——反射
本文介绍了Java反射机制的基本概念和使用方法,包括`Class`类的使用、动态加载类、获取方法和成员变量信息、方法反射操作、以及通过反射了解集合泛型的本质。同时,文章还探讨了动态代理的概念及其应用,通过实例展示了如何利用动态代理实现面向切面编程(AOP),例如为方法执行添加性能监控。
|
2月前
|
SQL 安全 Java
安全问题已经成为软件开发中不可忽视的重要议题。对于使用Java语言开发的应用程序来说,安全性更是至关重要
在当今网络环境下,Java应用的安全性至关重要。本文深入探讨了Java安全编程的最佳实践,包括代码审查、输入验证、输出编码、访问控制和加密技术等,帮助开发者构建安全可靠的应用。通过掌握相关技术和工具,开发者可以有效防范安全威胁,确保应用的安全性。
58 4
|
2月前
|
Java
Java的反射
Java的反射。
39 2
|
2月前
|
SQL 安全 Java
Java 异常处理:筑牢程序稳定性的 “安全网”
本文深入探讨Java异常处理,涵盖异常的基础分类、处理机制及最佳实践。从`Error`与`Exception`的区分,到`try-catch-finally`和`throws`的运用,再到自定义异常的设计,全面解析如何有效管理程序中的异常情况,提升代码的健壮性和可维护性。通过实例代码,帮助开发者掌握异常处理技巧,确保程序稳定运行。
47 0
|
3月前
|
存储 Java
[Java]反射
本文详细介绍了Java反射机制的基本概念、使用方法及其注意事项。首先解释了反射的定义和类加载过程,接着通过具体示例展示了如何使用反射获取和操作类的构造方法、方法和变量。文章还讨论了反射在类加载、内部类、父类成员访问等方面的特殊行为,并提供了通过反射跳过泛型检查的示例。最后,简要介绍了字面量和符号引用的概念。全文旨在帮助读者深入理解反射机制及其应用场景。
44 0
[Java]反射
|
3月前
|
安全 Java 测试技术
🌟Java零基础-反射:从入门到精通
【10月更文挑战第4天】本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
34 2
|
3月前
|
安全 Java 编译器
Java 泛型深入解析:类型安全与灵活性的平衡
Java 泛型通过参数化类型实现了代码重用和类型安全,提升了代码的可读性和灵活性。本文深入探讨了泛型的基本原理、常见用法及局限性,包括泛型类、方法和接口的使用,以及上界和下界通配符等高级特性。通过理解和运用这些技巧,开发者可以编写更健壮和通用的代码。
|
4月前
|
安全 Java API
java安全特性
java安全特性
34 8
|
3月前
|
IDE Java 编译器
java的反射与注解
java的反射与注解
29 0