Shiro 历史漏洞分析

声明

出品|先知社区(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 之后再返回。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值