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就行


相关文章
|
16天前
|
安全 Java API
Java反射(Reflection)的技术性文章
Java反射(Reflection)的技术性文章
24 1
|
27天前
|
存储 安全 Java
Java一分钟之-泛型擦除与类型安全
【5月更文挑战第20天】Java泛型采用类型擦除机制,在编译期间移除泛型信息,但在编译阶段提供类型安全检查。尽管需要类型转换且可能产生警告,但可以通过特定语法避免。使用泛型时应注意自动装箱拆箱影响性能,无界通配符仅允许读取。理解这些特性有助于编写更安全的代码。
46 4
|
2天前
|
存储 安全 Java
详解 Spring Security:全面保护 Java 应用程序的安全框架
详解 Spring Security:全面保护 Java 应用程序的安全框架
5 1
|
2天前
|
安全 Java
深入理解 Java 泛型工厂方法:类型安全与灵活性的结合
深入理解 Java 泛型工厂方法:类型安全与灵活性的结合
7 1
|
2天前
|
安全 Java 编译器
Java 泛型详解:全面掌握类型安全与灵活性的利器
Java 泛型详解:全面掌握类型安全与灵活性的利器
8 1
|
2天前
|
JSON IDE Java
Java反射详解:核心概念、使用方法与实际应用
Java反射详解:核心概念、使用方法与实际应用
8 2
|
9天前
|
存储 前端开发 Java
JAVA-反射知识点总结
JAVA-反射知识点总结
|
9天前
|
存储 设计模式 Java
Java语言中反射动态代理接口的解释与演示
Java语言中反射动态代理接口的解释与演示
8 1
|
14天前
|
网络安全 安全 Java
Java一分钟之-SSL/TLS:安全套接字层与传输层安全
【6月更文挑战第2天】本文介绍了SSL/TLS协议在保护数据传输中的作用,以及Java中使用JSSE实现SSL/TLS的基础。内容涵盖SSL/TLS工作流程、版本、常见问题及解决办法。通过`SSLSocket`和`SSLServerSocket`示例展示了服务器和客户端的实现,并强调证书管理、配置检查和依赖更新的最佳实践,以确保安全的通信。
41 4
|
16天前
|
缓存 安全 Java
【Java——反射机制详解】
RTTI(Run-Time Type Identification)运行时类型识别。在《Thinking in Java》一书第十四章中有提到,其作用是在运行时识别一个对象的类型和类的信息。主要有两种方式:一种是“传统的”RTTI,它假定我们在编译时已经知道了所有的类型;另一种是“反射”机制,它允许我们在运行时发现和使用类的信息。 反射就是把java类中的各种成分映射成一个个的Java对象 例如:一个类有:成员变量、方法、构造方法、包等等信息,利用反射技术可以对一个类进行解剖,把个个组成部分映射成一个个对象。