Shiro漏洞分析
前言
Apache Shiro是一个Java 安全框架,它执行身份验证、授权、加密和会话管理。
Shiro介绍
Shiro 框架的有三个核心组件,分别为 Subject、SecurityManager 和 Realms 。
- Subject:用户操作的代理层,代表当前与应用程序交互的实体(用户、第三方服务等)
- SecurityManager:Shiro 架构的核心,接收 Subject 的请求,调用内部组件(如 Authenticator、Authorizer),协调所有安全操作(认证、授权、会话管理等)。
- Realms:连接 Shiro 与安全数据(数据库、LDAP 等)的桥梁,提供用户凭证和权限信息,验证用户身份
关系图如下:
总体流程:
- Subject 接收用户请求(如登录),委托给 SecurityManager;
- SecurityManager 调用 Authenticator(认证)或 Authorizer(授权)组件
- 认证/授权组件通过 Realms 从数据源获取安全信息(如密码、权限列表)
CVE
截至目前,Shiro总共出现了26个CVE,其中包括反序列化和权限绕过等漏洞;本文以vulhub的CVE-2010-3863、CVE-2016-4437、CVE-2020-1957三个漏洞环境为例来分析漏洞,另外再额外加一个CVE-2019-12422。
CVE编号如下:
- ……
- CVE-2022-32532(RegExPatternMatcher)
- CVE-2021-41303(requestURINoTrailingSlash)
- CVE-2020-17523(%20)
- CVE-2020-17510(%2e)
- CVE-2020-13933(%3b)
- CVE-2020-11989(%25%32%66、/;/绕过)
- CVE-2020-1957 (/、/;xx/)
- CVE-2019-12422(RememberMe、Padding Oracle Attack、CBC)
- CVE-2016-6802 (Context Path绕过、/x/../)
- CVE-2016-4437 (RememberMe、硬编码)
- CVE-2014-0074 (ldap空密码、空用户名、匿名)
- CVE-2010-3863 (/./)
环境搭建
1 | #拉取vulhub项目 |
CVE-2010-3863
Shiro
进行权限验证前未进行路径标准化,导致使用时可能绕过权限校验
指纹
判断该网站使用 Apache Shiro 框架
勾选记住密码后,登录返回的响应包中 set-Cookie 包含 rememberME=deleteMe 字段
影响版本
- Apache Shiro < 1.1.0
- JSecurity 0.9.x (Shiro前身)
原理分析
通过ShiroDemo分析漏洞原理
Demo介绍
项目结构大致如下:
其中realm.ini
配置文件中存在两种角色,分别为为用户和管理员
ShiroConfig
文件如下:
ShiroConfig源码:
1 | public class ShiroConfig { |
当请求路径为/admin.html
、/user.html
时需要身份认证才能访问,而/**
由于设置的为anon
因此不需要认证就可以访问。
anon
正常使用时是用来请求静态资源的,如/css/style.css
1 | #设置 |
其他源码可以自行查看
Demo分析
接下来分析一下Shiro处理请求URL的流程
Shiro使用PathMatchingFilterChainResolver
下的getChain()
方法用来获取和调用需要执行的Filter,跟进去看一下
看到filterChainManager.getChainNames()
方法,通过调试并表达式求值,可以知道该方法是用来取出配置的
总共三个配置
1 | [/admin.html, /user.html, /**] |
大概意思就是如果是admin用户就会匹配/admin.html
,user匹配/user.html
,如果两个都没匹配到就会到 /**
路径
除了上面的filterChainManager.getChainNames()
方法
还可以看到另一个方法getPathWithinApplication()
,它用来获取我们传入的请求路径。
跟进getPathWithinApplication()
方法
再跟进上图中的WebUtils.getPathWithinApplication()
方法
看到两个方法为getContextPath()
和getRequestUri()
,第一个是用来获取Context路径,第二个是用来获取请求路径。
跟进第二个方法getRequestUri()
看一下
调用了decodeAndCleanUriString()
方法
跟进decodeAndCleanUriString()
可以发现该方法是用来处理用户我们请求的URL的,只做了两个处理,一是进行URL解码,二是移除 URI 中第一个分号(;
及其后的内容
整体分析完Shiro对这个对请求URL的处理后可以发现该过程并没有对URI并没有进行路径的标准化处理,如果传入特殊字符就存在绕过风险。
比如说传入./admin,它就不无法与配置文件中的admin.html
和user.html
匹配,从而与不需要身份认证的/**
匹配,进入/**
的匹配范围,从而导致越权访问。
漏洞复现
分析完Demo后,回到vulhub复现
启动环境
1 | #进入环境目录 |
1 | #拉取并启动环境 |
1 | #查看映射端口 |
复现
抓包,直接访问/admin
会302跳转
请求路径直接拼接 /./admin
,无账号成功访问到admin页面,说明绕过了权限校验
修复
补丁 在更新中添加了标准化路径函数, 对 /
、//
、/./
、/../
等进行了处理。
CVE-2016-4437(Shiro550)
存在反序列化漏洞,利用静态密钥构造序列化payload传入Cookie中的
rememberMe
参数,目标接收后会对其反序列化从而执行恶意命令。
影响版本
- Apache Shiro 1.x < 1.2.5
原理分析
相关类与方法介绍
Shiro的解密流程涉及到以下这些类
AbstractRememberMeManager
是RememberMeManger
接口类的抽象类,getRememberedPrincipals()
方法是Shiro框架中RememberMeManager
接口的核心方法,负责从Cookie中恢复用户的身份信息(PrincipalCollection)。
如下图,它可以调用下方的两个方法,getRememberedSerializedIdentity()
和convertBytesToPrincipals
getRememberedSerializedIdentity()
方法主要是获取Cookie 中的内容并通过 Base64 解码,然后返回 byte 数组。
convertBytesToPrincipals()
方法主要是对数组进行AES解密和反序列化。
漏洞分析
跟进源码详情看一下
先跟进AbstractRememberMeManager
类下的getRememberedPrincipals()
方法看一下
可以看到上面提到的两个方法
其中第一个方法AbstractRememberMeManager
类的getRememberedSerializedIdentity()
方法会调用到它的子类CookieRememberMeManager
的这个方法上
跟进去看一下CookieRememberMeManager
类的getRememberedSerializedIdentity()
该方法获取Cookie的值并进行了Base64解码
接下来跟进AbstractRememberMeManager
类下的第二个方法convertBytesToPrincipals()
看到有一个decrypt()
的解密方法和一个deserialize()
反序列化方法
跟进decrypt()
方法
总共有两个参数,第一个是加密数据,第二个getDecryptionCipherKey()
方法获取的应该就是密钥
跟进getDecryptionCipherKey()
看一下
返回了decryptionCipherKey
进去跟进decryptionCipherKey
是一个private的属性
右键decryptionCipherKey
查找用法看一下是在哪里被赋的值
看到是在setDecryptionCipherKey()
方法中写入了
继续跟进setDecryptionCipherKey()
右键setDecryptionCipherKey()
查找用法
看到setCipherKey()
搜索一下setCipherKey()
看看哪里使用了
找到静态密钥
关于principals
用户最终的身份信息principals
会被保存在服务器中
context.setPrincipals(principals)
保存
principals会被保存在服务器,因此只要在Cookie的字段中构造恶意数据就可以被保存在服务器。
总结
根据上方的分析可以总结出Shiro对Cookie里RememberMe字段解密流程顺序为:Base解码->AES解密->反序列化,同时这个版本的Shiro的AES密钥是固定的,只要知道静态密钥就可以伪造恶意的序列化数据进行利用。
漏洞复现
启动环境
1 | #进入目录 |
既然已经知道了Cookie处存在反序列化漏洞,可以配合Yakit手工构造payload,但是现在的工具已经很多了,知道原理之后用工具比手搓方便多了,有两款工具ShiroAttack2、Pyke-Shiro都有爆破密钥和爆破利用链执行命令的功能,下面用工具直接利用。
使用Pyke-Shiro
工具先爆破静态密钥(因为密钥是固定的,用该工具的密钥库爆破密钥),再爆破能够使用的利用链。
用爆破出来的密钥与利用链执行命令。
能执行命令说明利用成功。
修复
在Shiro 1.2.5的 Commit 中,移除了硬编码密钥,改为启动时动态生成随机密钥,用户也可以手动配置一个cipherKey。
CVE-2020-1957
影响版本
- Apache Shiro < 1.5.2
漏洞原理
利用 Shiro 和 Spring 对 URL处理的差异化而越权
漏洞复现
直接访问/admin时,会被重定向到登陆页面
构造恶意路径/xxx/..;/admin/
,可绕过权限校验,访问到admin页面
修复
补丁使用 request.getContextPath()
、request.getServletPath()
、request.getPathInfo()
拼接构造uri
替代request.getRequestURI()
来修复;
绕过
CVE-2019-12422(Shiro721)
该漏洞环境不在vulhub靶场环境的范围内
影响版本
- Apache Shiro< 1.4.2
漏洞原理
这个漏洞出现的原因主要是AES加密算法本身出现的漏洞,可以通过Padding Oracle(字符填充)进行利用,通过这种方式可以无密钥解密任意密文或构造任意密文。
大概原理就是可以通过服务端对对于有效密文、填充错误的无效密文和有效密文但解密错误的密文的不同响应来判断提供的加密值是否被正确填充,本质上算是爆破。但是由于跑脚本会发出大量请求,因此这个漏洞在实际场景中不太可能利用成功,这个漏洞原理就不详细分析了,详情我是在tttang.com、goodapple.top参考的。
环境搭建
1 | # 下载项目 |
漏洞复现
访问主页,输入网站给的账户密码,勾选记住密码抓包并登录,来获取一个合法用户的rememberMe值。
登录时有两个请求包,查看第二个数据包,响应中可以得到Cookie的RememberMe字段
用ysoserial生成恶意序列化文件
1 | java -jar ysoserial-all.jar CommonsBeanutils1 "touch /tmp/jjxxx" > ser.class |
这是上面项目中自带的脚本
1 | # -*- coding: utf-8 -*- |
拿着上面获得的有效用户的rememberMe字段和用来执行命令的序列化文件来跑脚本
1 | python2 shiro_exp.py http://ip:8080/login.jsp ze7hkK/AJFA+cCniNzbxSySylskZ50e2TYaBWge9Ps/R4J0QsDj3+V8DfMfSrZ6nUtrG4adeyUZXiRuEpgt34XWuBv9UH5pGubCjI5xRSCyphBfd3QnaSrixiO3ff2cy62lwmHRjFLDSBQKJSwC9wVtqf5wTRLIkbRPckrpOaG7gvQkM0UOuUUnuoLvv2H2DBxkgtOBM6VFcXJcE/aPjPkkOBn1A9DupBvcnM9vTABZx+uDAU3ibgknXecDMnmanVcishvIsCSMmyndx0Gl9dJmB6KO4yWgxAYocTHCJlBbVbKeGaBC50gnK7fifI05zLHvwQWxcftObFffSMMQU9sOGuHMbepms0ft9MOO7FsnZThLnmtdlC5Hzro4Gf1Emq2dZGebSvZ6h+AsflDtKIg4RAP94q1QidVaXv5v/zsLhdKwiprVyDpZTN3mX3WQYvuWqX9ZPnvGz28bNlw0uL7bSHhvSjQUosg6OtntNfrYJf5eqzU8rQX1z+z2zEZG6 ser.class |
我这里跑出来大概用了8 9小时左右,得到一个恶意的rememberMe字段
将生成的 rememberMe字段值放到Cookie中再发一次包就可以了。
1 | rememberMe cookies: |
发包后进入容器查看
1 | docker exec -it df /bin/bash |
看到成功创建文件夹
其他可用工具ShiroExploit-Deprecated,感觉这个更方便。
修复
补丁将AES-CBC加密模式替换为更安全的 AES-GCM,密钥动态生成加固,密钥生成过程增强随机性,防止预测或爆破。
总结
通过四个漏洞分析了Shiro框架的权限绕过和反序列化漏洞,除了Shiro721真实场景下很难利用成功,其他的利用难度都不大。