声明
出品|先知社区(ID:alter99)
以下内容,来自先知社区的alter99作者原创,由于传播,利用此文所提供的信息而造成的任何直接或间接的后果和损失,均由使用者本人负责,长白山攻防实验室以及文章作者不承担任何责任。
什么是Shiro
blog.csdnimg.cn/img_convert/4bfeca413896e9fbe5889b69e78f403d.png)
Apache Shiro is a powerful and easy-to-use Java security framework that performs authenticat-ion(身份验证), authorization(授权), crypto-graphy(加密), and session management(会话管理). With Shiro’s easy-to-understand API, you can quickly and easily secure any application – from the smallest mobile applications to the largest web and enterprise applications.
相关CVE
根据官方网站上的漏洞通报,Shiro 在历史上共通报了 11 个 CVE(以及最新披露的CVE),其中包含认证绕过、反序列化等漏洞类型,接下来我们来依次学习。
CVE-2010-3863
漏洞信息
**漏洞编号:**CVE-2010-3863 / CNVD-2010-2715
**影响版本:**shiro < 1.1.0 和JSecurity 0.9.x
**漏洞描述:**Shiro进行权限验证前未进行路径标准化,导致使用时可能绕过权限校验
**漏洞补丁:**Commit
漏洞分析
先分析一下Shiro身份验证的流程:Shiro使用org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver#getChain 方法获取和调用要执行的 Filter
逻辑如下:
在getPathWithinApplication()方法中调用 WebUtils.getPathWithinApp-lication()方法,用来获取请求路径。其中getContextPath(request)方法获取 Context 路径
\getRequestUri(request) 方法获取URI 的值,并调用 decodeAndCleanUriString() 处理。
在decodeAndCleanUriString()中对
; 进行了截取。
此时contextPath值为/samples_web_war,requestUri值为/samples_web_war/login.jsp
然后判断requestUri是否以contextPath开始,是的话将其替换为/
处理之后的请求URL将会使用AntPathMat-cher#doMatch进行权限验证。此时发现,Shiro中对URI并没有进行路径的标准化处理,这样当URI中存在特殊字符时,就存在绕过风险
复现
[urls]
/login.jsp = authc
/logout = logout
/account/** = authc
/remoting.jsp = authc, perms["audit:list"]
/** = anon
有了上面的配置,直接访问:/remoting.jsp,会因为没有权限而跳转至登陆界面。
当访问 /./remoting.jsp,由于其不能与配置文件匹配,导致进入了 /** 的匹配范围,导致可以越权访问
漏洞修复
Shiro 在 Commit 更新中添加了标准化路径函数。
对 /、//、/./、/…/ 等进行了处理。
CVE-2014-0074
漏洞信息
漏洞编号:
CVE-2014-0074/CNVD-2014-03861/SHIRO-460
**影响版本:**shiro 1.x<1.2.3
**漏洞描述:**当程序使用LDAP服务器并启用非身份验证绑定时,远程攻击者可借助空的用户名或密码利用该漏洞绕过身份验证。
**漏洞补丁:**Commit
漏洞分析
当使用了未经身份验证绑定的LDAP服务器时,允许远程攻击者通过空用户名或空密码绕过身份验证。
漏洞修复
Shiro 在 f988846 中针对此漏洞进行了修复
CVE-2016-4437
漏洞信息
漏洞编号:
CVE-2016-4437/CNVD-2016-03869/S
HIRO-550
**影响版本:**shiro 1.x < 1.2.5
**漏洞描述:**利用硬编码的密钥构造rememberMe参数,进行反序列化攻击
**漏洞补丁:**Commit
参考: Shiro 550反序列化漏洞分析
漏洞分析
关键代码处于
AbstractRememberMeManager#getRememberedPrincipals 方法中,参数是用户的身份Context信息,如下图
这个方法主要是把SubjectContext转化成Princi-palCollection。该方法调用了getRemembered-SerializedIdentity和convertBytesToPrincipals方法。
其中CookieRememberMeManager 的getRemem-beredSerializedIdentity的实现是获取Cookie并Base64解码
将解码后的byte数组传入convertBytesToPrincipals中进行:decrypt和deserialize。decrypt是使用AesCipherService进行解密。
deserialize调用deserialize()方法反序列化解密后的数据。
反序列化得到的PrincipalCollection会被set到SubjectContext 。
解密的调用栈入下图所示
此时就可以想象,如果我们有了加密密钥,使用密钥加密一个恶意序列化的payload,将rememberMe的值替换成base64后的payload传入服务器,那这样就可以触发漏洞了。关键点在于,我们如何获取加密密钥?
这就要提到AbstractRememberMeManager类,它是RememberMeManager 接口的实现。类中有几个关键变量:
-
DEFAULT_CIPHER_KEY_BYTES:对称密钥,使用Base64加密之后直接存在代码中
-
serializer:Shiro 提供的序列化器
-
cipherService:用来对数据加解密的类
漏洞利用
编写一个poc
package com.alter.Shiro;
import com.alter.Deserialize.CommonsCollections6;
import org.apache.shiro.crypto.AesCipherService;
import org.apache.shiro.util.ByteSource;
public class test {
public static void main(String[] args) throws Exception {
byte[] payloads = new CommonsCollections6().getPayload("/System/Applications/Calculator.app/Contents/MacOS/Calculator");
AesCipherService aes = new AesCipherService();
byte[] key = java.util.Base64.getDecoder().decode("kPH+bIxk5D2deZiIxcaaaA==");
ByteSource ciphertext = aes.encrypt(payloads, key);
System.out.printf(ciphertext.toString());
}
将生成的payload赋值给rememberMe,但是发送过去后,服务器报错。
下面根据p师傅的分析复现一下报错原因,发现是这个类:
org.apache.shiro.io.ClassResolvingObjectInputStream的问题。可以看到这是一个ObjectInputStream的子类,其重写了resolveClass方法(resolveClass是反序列化中用来查找类的方法):
对比一下它的父类,也就是正常的ObjectInputStream 类中的resolveClass 方法:
区别就是前者用的是org.apache.shiro.util.Class-Utils#forName(实际上内部用到了org.apache.-catalina.loader.ParallelWebappClassLoader#loadClass),而后者用的是Java原生的Class.forName
调试发现出现异常时加载的类名为[Lorg.apache.comm-ons.collections.Transformer;这个类名看起来怪,其实就是表示org.apache.-commons.collections.Transformer的数组。
所以,网上很多文章就给出结论,Class.forName支持加载数组,ClassLoader.loadClass不支持加载数组,这个区别导致了问题。但p师傅在Java漫谈中否定了这一观点,并写出结论:如果反序列化流中包含非Java自身的数组,则会出现无法加载类的错误。这就解释了为什么CommonsCollections6无法利用了,因为其中用到了Transformer数组。
p师傅在漫谈中分析讲解了两种poc,一个是使用TemplatesImpl改造的无数组CCShiro反序列化链,这个链需要有CC依赖,另一个是CB的无依赖Shiro反序列化链,这两个poc都可以测试成功了。
漏洞修复
Shiro 在 1.2.5 的 Commit 中对此漏洞进行了修复。系统在启动的时候会生成一个新key,用户也可以手动配置一个cipherKey。
其实在 SHIRO-441,就有人提出了硬编码可能带来的问题。
CVE-2016-6802
漏洞信息
**漏洞编号:**CVE-2016-6802 / CNVD-2016-07814
**影响版本:**shiro < 1.3.2
**漏洞描述:**Shiro未对ContextPath做路径标准化导致权限绕过
**漏洞补丁:**Commit
参考 : su18师傅
漏洞详解
本漏洞类似 CVE-2010-3863,依旧是路径标准化导致的问题,不过之前是在 RequestURI 上,本漏洞是在 ContextPath 上。
之前提到,Shiro 调用 WebUtils.getPathWithin-Application() 方法获取请求路径。逻辑如下:
public String getPathWithinApplication(HttpServletRequest request) {
String contextPath = this.getContextPath(request);
String requestUri = this.getRequestUri(request);
String path = this.getRemainingPath(requestUri, contextPath, true);
if (path != null) {
return StringUtils.hasText(path) ? path : "/";
} else {
return requestUri;
}
}
其中调用getContextPath()方法,获取contextPath;
调用getRequestUri()方法,获取uri ;
在getContextPath()方法调用decodeRequest-String进行URLDecode。
由于获取的 ContextPath 没有标准化处理,如果出现一些特殊字符使ContextPath与实际不符,都会导致在 StringUtils.startsWithIgnoreCase() 方法判断时失效,直接返回完整的RequestURI。
复现
登录账户lonestarr,该账户对页面remoting.jsp没有访问权限,在跟路径前加任意路径,再加…/即可实现绕过
漏洞修复
Shiro 在 1.3.2 版本的 Commit 中对此漏洞进行了修复。
通过代码可以看出,在 WebUtils.getContextPath 方法进行了更新,使用了修复 CVE-2010-3863 时更新的路径标准化方法 normalize 来处理 ContextPath 之后再返回。