【组件攻击链】Shiro组件漏洞与攻击链分析

1 组件介绍

Apache Shiro是一个功能强大且易于使用的Java安全框架,功能包括身份验证,授权,加密和会话管理。使用Shiro的API,可以轻松地快速地保护任何应用程序,范围包括小型的移动应用程序到大型的Web和企业应用程序。以下是Shiro的结构图。

Shiro提供了应用安全API(被Shiro框架开发团队成为安全四大基石的Authentication(认证), Authorization(授权), Session Management(会话管理), Cryptography(加密))

  • Authentication(认证):证明用户身份,通常称之为“登录”。

  • Authorization(授权) :访问控制。

  • Cryptography(加密) :保护或隐藏数据,防止数据被窃取。

  • Session Management(会话管理) :管理每一个用户的会话状态。

在概念层,Shiro 架构包含三个主要的理念:Subject,SecurityManager和 Realm。

  • Subject:当前用户,Subject 可以是一个人,但也可以是第三方服务、守护进程帐户、时钟守护任务或者其它–当前和软件交互的任何事件。

  • SecurityManager:管理所有Subject,SecurityManager 是 Shiro 架构的核心,配合内部安全组件共同组成安全伞。

  • Realms:用于进行权限信息的验证,我们自己实现。Realm 本质上是一个特定的安全 DAO:它封装与数据源连接的细节,得到Shiro 所需的相关的数据。在配置 Shiro 的时候,你必须指定至少一个Realm 来实现认证(authentication)和/或授权(authorization)。

上图是Shiro组件在国内的使用量统计图。通过网络空间搜索引擎的数据统计,绘制柱状图表。Shiro 在国内有着近5千个的站点分布,其中以浙江、北京、广东等为主要地区。

2 高危漏洞介绍

漏洞名称 漏洞ID 影响版本 CVSS
Apache Shiro 1.2.4反序列化远程代码执行漏洞 CVE-2016-4437/SHIRO-550 Apache Shiro <= 1.2.4 8.1
Apache Shiro Padding Oracle Attack 远程代码执行漏洞 CVE-2019-12422/SHIRO-721 Apache Shiro < 1.4.2 7.5
Apache Shiro 身份验证绕过漏洞 CVE-2020-1957/SHIRO-682 Apache Shiro < 1.5.2 9.8
Apache Shiro 身份验证绕过漏洞 CVE-2020-11989/SHIRO-782 Apache Shiro < 1.5.3 9.8
Apache Shiro 身份验证绕过漏洞 CVE-2020-13933 Apache Shiro < 1.6.0 7.5
Apache Shiro 身份验证绕过漏洞(AJP协议绕过) SHIRO-760 基于Tomcat版本
Apache Shiro < 1.2.3 身份验证绕过漏洞 CVE-2014-0074/SHIRO-460 Apache Shiro < 1.2.3 7.5

Shiro组件漏洞主要分为两种类型,一种是java反序列化造成的远程代码执行漏洞,另一种是身份验证绕过漏洞。在官方对rememberMe字段反序列化进行秘钥随机化,AES加密模式更换后,暂无新型反序列化漏洞的出现。但是在身份验证绕过的补丁修复上,多次出现绕过情况,多是由于补丁修复过程中考虑不周全。Shiro在今年上半年爆出了三个较为严重的身份验证绕过的漏洞,且影响版本较新,用户需要重点关注。

3 漏洞利用链

3.1 组件风险梳理

根据Shiro组件的漏洞,结合敏感配置的梳理,得到如下风险梳理的思维导图。

3.2 利用链总结

基于风险梳理思维导图,总结出2种可行的漏洞利用链。

3.2.1 无权限 -> GetShell

Apache Shiro 1.2.4反序列化远程代码执行漏洞单独使用,即可完成GetShell。此漏洞利用前提是获取AES加密秘钥。一般情况,Shiro组件默认没有被更换,可以直接利用此漏洞。如果出现秘钥被更换,此漏洞需要结合任意文件读取或者任意文件下载,先获取到AES加密秘钥,再进行漏洞利用。

3.2.2 普通权限 -> GetShell

Apache Shiro Padding Oracle Attack 远程代码执行漏洞单独使用,即可完成GetShell。此漏洞利用前提需要获得一个正确的用户Cookie值,利用此漏洞进行GetShell至少要获取一个普通用户权限。

4 高可利用漏洞分析

从高危漏洞列表中,针对部分近年高可利用漏洞进行漏洞深入分析。

Shiro组件漏洞分为两个种类:java反序列化漏洞、身份验证绕过漏洞。本章节重点进行两个类别的漏洞分析。

4.1 java反序列化漏洞

技术背景

Java反序列化漏洞原理: Java 提供了一种对象序列化的机制,该机制中,一个对象可以被表示为一个字节序列,该字节序列包括该对象的数据、有关对象的类型的信息和存储在对象中数据的类型。Java反序列化则是从一个源输入流中读取字节序列,再把它们反序列化为一个对象,并将其返回。Java反序列化漏洞的成因是攻击者通过序列化函数将自己精心构造的恶意对象序列化,将序列化数据发送到目标服务器的反序列化接口,当服务器在没有对序列化数据进行有效的安全验证,直接对序列化数据进行反序列化处理,执行恶意对象中的代码,造成攻击。

AES加密算法: 使用Rijndael分组密码算法,属于对称加密。

AES加密算法涉及4种操作:字节替代(SubBytes)、行移位(ShiftRows)、列混淆(MixColumns)和轮密钥加(AddRoundKey)。
总体加密流程:

字节替代(SubBytes)
字节代替的主要功能是通过S盒完成一个字节到另外一个字节的映射。

解密使用S盒的逆进行还原。

行移位(ShiftRows)
行移位是一个4×4的矩阵内部字节之间的置换,用于提供算法的扩散性。
正向行移位:正向行移位用于加密,其原理图如下。其中:第一行保持不变,第二行循环左移8比特,第三行循环左移16比特,第四行循环左移24比特。

解密使用逆向行移位:第一行保持不变,第二行循环右移8比特,第三行循环右移16比特,第四行循环右移24比特。

列混淆(MixColumns)
将行移位得到的新的4×4矩阵,与另一个矩阵进行左乘运算,更改每一列的数值。

解密时,左乘与之前选取的中间矩阵的互逆矩阵,即可还原。

轮密钥加(AddRoundKey)
加密过程中,每轮的输入与轮子密钥异或一次。
解密过程,由于任何数和自身的异或结果为0,因此,解密时再异或上该轮的轮子密钥即可恢复。

AES CBC模式

加密

解密

4.1.1 Apache Shiro 1.2.4反序列化远程代码执行漏洞

1 漏洞信息

1.1 漏洞简介

  • 漏洞名称:Apache Shiro 1.2.4 Deserialize Remote Code Execution Vulnerability

  • 漏洞编号:CVE-2016-4437

  • 漏洞类型:远程代码执行

  • CVSS评分:【CVSS v2.0:6.8】【CVSS v3.0:8.1】

  • 漏洞危害等级:高危

1.2 漏洞概述

Apache Shiro 1.2.5之前的版本在 org.apache.shiro.mgt.AbstractRememberMeManager 中存在AES默认秘钥 kPH+bIxk5D2deZiIxcaaaA== ,开启RememberMe功能的shiro组件将允许远程攻击者构造序列化数据,在目标服务器上执行任意命令

1.3 漏洞利用条件

  • 已知 Shiro AES 解密密钥。

  • 开启RememberMe功能。

1.4 漏洞影响

影响版本:
Apache Shiro <= 1.2.4

1.5 漏洞修复

获取Apache Shiro最新版本,下载链接:https://shiro.apache.org/download.html

2.漏洞复现

2.1 环境拓扑

2.2 应用协议

8080/HTTP

2.3 复现过程

基于Windows平台,使用环境目录下的shirodemo-1.0-SNAPSHOT.jar环境,执行java -jar shirodemo-1.0-SNAPSHOT.jar启动环境。效果如图

运行sniper工具箱,填写表单信息,输入ipconfig命令,点击Attack,效果如图。

有回显信息的exp插件

无回显信息的exp插件

3.漏洞分析

3.1 详细分析

3.1.1 漏洞利用过程

使用ysoserial生成存在恶意命令的反序列化payload。使用AES默认秘钥,生成的payload进行 AES/CBC/PKCS5Padding 模式加密,将加密后的结果传入到HTTP头部Cookie字段的rememberMe参数,通过HTTP协议发起请求。

注:由于Shiro重写了resolveClass方法,将原生方法中的forName方法替换为loadClass方法,由于loadClass无法加载数组类型的类,因此存在Transformer[]类的CommonCollections gadget无法成功利用此漏洞,(例如ysoserial CommonCollections1、CommonCollections3)

3.1.2 代码分析

传入的payload首先被服务器接收,并传送给Shiro拦截器处理(org.apache.shiro.web.servlet.OncePerRequestFilter#doFilter方法作为入口)

调用createSubject方法创建Subject

在org.apache.shiro.mgt.DefaultSecurityManager#getRememberedIdentity方法调用getRememberedSerializedIdentity方法获取rememberMe认证的序列化数据。

在此方法中可以获取传入的HTTP请求,响应数据,并解析获取http请求中的cookie字段值(payload所在字段)(base64形式)。

并在后续的程序中进行base64解码,将解码后的byte流存储在decoded变量中,作为后续进行AES解密的密文。

返回的密文数据流将会在org.apache.shiro.crypto.JcaCipherService#decrypt方法中完成AES解密操作。

获得的AES解密后的明文将在org.apache.shiro.web.mgt.AbstractRememberMeManager#deserialize方法中反序列化,执行其中的恶意代码。

3.1.3 漏洞触发过程

3.1.4补丁分析

对比Shiro 1.2.4与1.2.5版本的改动,在org.apache.shiro.mgt.AbstractRememberMeManager类中声明秘钥的方式,从原来的硬编码更改为自动生成秘钥。

新版本中调用generateNewKey方法进行秘钥生成,通过传入秘钥长度,根据传入的长度参数,返回对应长度的随机秘钥。

由于秘钥的随机性,使攻击者无法轻易的通过使用秘钥加密恶意序列化数据的方式进行攻击,从而修复了此漏洞。

4.1.2 Apache Shiro Padding Oracle Attack 远程代码执行漏洞

1.漏洞信息

1.1 漏洞简介

  • 漏洞名称:Apache Shiro Padding Oracle Attack Remote Code Execution Vulnerability

  • 漏洞编号:CVE-2019-12422

  • 漏洞类型:远程代码执行

  • CVSS评分:【CVSS v2.0:5.0】【CVSS v3.1:7.5】

  • 漏洞危害等级:高危

1.2 漏洞概述

Apache Shiro 1.4.2之前的版本默认使用AES/CBC/PKCS5Padding模式加密,开启RememberMe功能的Shiro组件将允许远程攻击者构造序列化数据,通过Padding Oracle Attack进行爆破,即使在秘钥未知的条件下,也可以在目标服务器上执行任意命令。

1.3 漏洞利用条件

  • 使用AES CBC模式。

  • 开启RememberMe功能。

  • 密文可控。

  • 获取到正常Cookie。

1.4 漏洞影响

影响版本:
Apache Shiro < 1.4.2

1.5 漏洞修复

获取Apache Shiro最新版本,下载链接:https://shiro.apache.org/download.html

2.漏洞复现

2.1 环境拓扑

2.2 应用协议

8080/HTTP

2.3 复现过程

基于Windows平台,使用环境目录下的apache-tomcat-8.5.2环境,进入bin目录下运行startup.bat,linux环境运行startup.sh启动环境。效果如图

运行sniper工具箱,填写表单信息,点击Attack,效果如图。

3.漏洞分析

3.1 详细分析

3.1.1 漏洞利用过程

使用ysoserial生成存在恶意命令的反序列化payload。获取一个用户的Cookie字段值(可以获取普通用户的Cookie)。取出rememberMe字段值,通过逐位爆破,构造正常padding数据,最终将每一块构造的block拼接在一起,形成任意构造的明文gadget加密后的密文。

注:由于Shiro重写了resolveClass方法,将原生方法中的forName方法替换为loadClass方法,由于loadClass无法加载数组类型的类,因此存在Transformer[]类的CommonCollections gadget无法成功利用此漏洞,(例如ysoserial CommonCollections1、CommonCollections3)

3.1.2 代码分析

CVE-2019-12422进行反序列化攻击的原理与CVE-2016-4437漏洞原理一致,而由于Shiro在1.2.4以上的版本已经将硬编码秘钥更换为随机生成的秘钥,因此无法获取到AES加密秘钥。CVE-2019-12422漏洞利用了AES CBC模式加密的漏洞,绕过了使用AES秘钥加密明文gadget的前提条件。具体原理如下:

在进行AES解密时,会对生成的明文进行padding校验,在com.sun.crypto.provider.CipherCore#doFinal中获取padding长度

com.sun.crypto.provider.PKCS5Padding#unpad方法进行padding检测,如果检测失败,返回-1状态码

当校验失败时,com.sun.crypto.provider.CipherCore#doFinal方法抛出异常

最终程序走到org.apache.shiro.web.servlet#removeFrom方法中,构造响应信息,进行回显。在代码中可以看见padding失败后会回显rememberMe deleteMe等特征字符串。可以作为padding oracle attack回显特征进行爆破。

攻击者可以凭借此特征,分组爆破。由于AES CBC模式解密时会将前一块的密文作为本轮解密的IV值,当密文可控时,可以逐位爆破前一块密文,根据padding error的回显不同,获取padding字符为\x10*16的IV’值。
IV ^ middle = 16 * \x10
IV’ ^ middle = 构造gadget密文块
IV’ = 构造gadget密文块 ^ 16 * \x10 ^IV
根据公式,循环获取所有密文块。通过这种方式,获取任意明文对应的密文。

3.1.3 漏洞触发过程

3.1.4补丁分析

对比Shiro 1.4.1与1.4.2版本的改动,在org.apache.shiro.crypto.AesCipherService类中声明AES加密的模式,从原来的CBC模式更改为GCM模式。

由于AES加密模式更换,攻击者无法使用padding oracle attack进行攻击,从而修复了此漏洞。

4.2 身份验证绕过漏洞

技术背景

拦截器和过滤器的详细区别

Filter Interceptor Summary
Filter 接口定义在 javax.servlet 包中 接口 HandlerInterceptor 定义在org.springframework.web.servlet 包中
Filter 定义在 web.xml 中
Filter在只在 Servlet 前后起作用。Filters 通常将 请求和响应(request/response) 当做黑盒子,Filter 通常不考虑servlet 的实现。 拦截器能够深入到方法前后、异常抛出前后等,因此拦截器的使用具有更大的弹性。允许用户介入(hook into)请求的生命周期,在请求过程中获取信息,Interceptor 通常和请求更加耦合。 在Spring构架的程序中,要优先使用拦截器。几乎所有 Filter 能够做的事情, interceptor 都能够轻松的实现
Filter 是 Servlet 规范规定的。 而拦截器既可以用于Web程序,也可以用于Application、Swing程序中。 使用范围不同
Filter 是在 Servlet 规范中定义的,是 Servlet 容器支持的。 而拦截器是在 Spring容器内的,是Spring框架支持的。 规范不同
Filter 不能够使用 Spring 容器资源 拦截器是一个Spring的组件,归Spring管理,配置在Spring文件中,因此能使用Spring里的任何资源、对象,例如 Service对象、数据源、事务管理等,通过IoC注入到拦截器即可 Spring 中使用 interceptor 更容易
Filter 是被 Server(like Tomcat) 调用 Interceptor 是被 Spring 调用 因此 Filter总是优先于interceptor执行

拦截器和过滤器的执行顺序

Shiro拦截器:

NameableFilter: NameableFilter给Filter起个名字,如果没有设置默认就是FilterName

当我们组装拦截器链时会根据这个名字找到相应的拦截器实例
OncePerRequestFilter: 用于防止多次执行Filter,一次请求只会走一次Filter链。enabled属性:是否开启该拦截器,默认enabled=true表示开启
ShiroFilter: 整个Shiro的入口点,用于拦截需要安全控制的请求进行处理。
AdviceFilter: 提供AOP风格的支持,类似于SpringMVC中的Interceptor。
PathMatchingFilter: 提供基于Ant风格的请求路径匹配功能及拦截器参数解析的功能,如roles[admin,user]自动根据,分割解析到一个路径参数配置并绑定到相应的路径。
AccessControlFilter: 提供访问控制的基础功能,比如是否允许访问/当访问拒绝时如何处理等。isAccessAllowed表示是否允许访问;mappedValue就是[urls]配置中拦截器参数部分,如果允许访问返回true,否则false。onAccessDenied 表当访问拒绝时是否已经处理,如果返回true表示需要继续处理,如果返回false表示该拦截器实例已经处理了,直接返回即可。

4.2.1 Apache Shiro 身份验证绕过漏洞

1.漏洞信息

1.1 漏洞简介

  • 漏洞名称:Apache Shiro Authentication Bypass Vulnerability

  • 漏洞编号:CVE-2020-1957

  • 漏洞类型:Authentication Bypass

  • CVSS评分:【CVSS v2.0:7.5】【CVSS v3.1:9.8】

  • 漏洞危害等级:高危

1.2 漏洞概述

Apache Shiro 1.5.2之前的版本,由于Shiro拦截器与requestURI的匹配流程与Web框架的拦截器的匹配流程有差异,攻击者构造一个特殊的http请求,可以绕过Shiro的认证,未授权访问敏感路径。此漏洞有两种攻击方式,第一种攻击方式适用于Shiro < 1.5.0版本,由于Shiro 1.5.0版本修复补丁考虑不全面,导致补丁绕过,出现了第二种攻击方式,适用于Shiro < 1.5.2版本。

1.3 漏洞利用条件

1.4 漏洞影响

影响版本:
Apache Shiro < 1.6.0

1.5 漏洞修复

获取Apache Shiro最新版本,下载链接:https://shiro.apache.org/download.html

2.漏洞复现

2.1 环境拓扑

2.2 应用协议

8080/HTTP

2.3 复现过程

基于Windows平台,使用环境目录下的shiro-basic.zip环境,解压后,用Idea打开shiro-basic文件夹,下载maven资源,运行ShiroBasicApplication类,即可启动环境。效果如图。

Shiro < 1.5.0

构造HTTP请求,发送到服务器,完成身份验证绕过,效果如图

Shiro < 1.5.2

3.漏洞分析

3.1 详细分析

3.1.1 漏洞利用过程

Shiro < 1.5.0

获取到无权限访问的敏感路径,在路径的结尾添加


/
进行身份验证绕过。

Shiro < 1.5.2

获取到无权限访问的敏感路径,在auth认证路径中的添加


;
进行身份验证绕过。

3.1.2 代码分析

Shiro < 1.5.0

传入的payload首先被服务器接收,并传送给Shiro拦截器处理(org.apache.shiro.web.servlet.OncePerRequestFilter#doFilter方法作为入口)。

调用createSubject方法创建Subject,并调用execute方法进入Shiro FilterChain中。

进入org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver#getChain方法中,首先获取请求uri路径,之后迭代获取拦截器的表达式。

这里我们重点关注/hello/*表达式。代码进入pathMatches方法,最终调用org.apache.shiro.util.AntPathMatcher#doMatch方法进行传入的requestURI与拦截器表达式进行匹配。

匹配过程中,分别将拦截器表达式与requestURI以/作为分隔符进行字符串到数组的转换,通过循环匹配数组中对应的元素,判断requestURI是否符合拦截器表达式匹配形式。

如果表达式中存在通配符*,会将containsStar标志位赋值为true,进入 else if (patIdxEnd == 0)判断条件,返回true。

继续跟进代码,在requestURI与拦截器表达式匹配结束后,还会进行一次判断,而漏洞产生的原因也是由于判断的条件。如果Shiro拦截器表达式不以/结尾,且requestURI以/结尾,判断代码将返回false表示匹配失败,从而绕过Shiro认证。

跟进到Spring处理URI的代码,进入org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#getHandlerInternal方法,获取requestURI。

进入lookupHandlerMethod方法,调用addMatchingMappings方法,获取Spring拦截器。

进入org.springframework.web.servlet.mvc.condition.PatternsRequestCondition#getMatchingCondition方法调用doMatch方法进行requestURI和拦截器表达式的匹配。

Spring拦截器匹配流程和Shiro大致相同,都是将字符串转换为数组进行匹配。

由于Spring多了一个环节,在检测拦截器表达式与requestURI结尾是否为/之后,并没有直接返回false。而是将拦截器表达式结尾添加/,并继续进行path.startsWith(this.pathSeparator) != pattern.startsWith(this.pathSeparator)测试,从而完成了拦截器表达式与requestURI的匹配。

上述攻击方式在Shiro 1.5.0版本中修复,但是被二次绕过,绕过分析如下。

Shiro < 1.5.2

Shiro 1.5.0 – 1.5.1在认证过程中基本没有变化,主要分析一下二次绕过的利用点。还是以org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver#getChain作为起点。

在获取requestURI时,依旧会在getPathWithinApplication方法中调用getRequestUri方法进行requestURI的解析并获取,但是在URI正规化处理时,先调用decodeAndCleanUriString方法进行路径的解码,并清理URI。

进入decodeAndCleanUriString方法,发现此方法会以分号将传入的URI进行截断,并将分号以及分号后面的数据进行清空,返回分号前面的URI数据,从而让/a/b;/c变为/a/b。

继续跟进到Spring拦截器的decodeAndCleanUriString方法中

从代码中可以发现,Spring对于分号处理的方式与Shiro不同,Spring会先获取分号的位置,并检测分号后是否存在/,如果有,将/的位置记录在slashIndex变量中,并将分号前的数据与/之后的数据进行拼接,从而让/a/b;/c变为/a/b/c。返回处理后的requestURI。

由于Spring与Shiro的decodeAndCleanUriString方法不同,攻击者可以使用分号构造路径,绕过Shiro认证,并可以匹配Spring的动态控制器。

3.1.3 漏洞触发过程

Shiro < 1.5.0

Shiro < 1.5.2

3.1.4补丁分析

对比Shiro 1.4.2与Shiro 1.5.0版本的改动,在org.apache.shiro.web.filter.PathMatchingFilter类中添加了删除requestURI结尾的 / 的代码。

由于requestURI和拦截器表达式结尾的 / 都会被清除,因此防御了此漏洞。

对比Shiro 1.5.1与Shiro 1.5.2版本的改动

由于Shiro 1.5.2版本中,在进行decodeAndCleanUriString方法之前会先进行uri解析,调用request.getServletPath()和request.getPathInfo()获取ServletPath 和PathInfo 并进行路径拼接,避开了decodeAndCleanUriString对于分号的处理,从而修复了此漏洞。

4.2.2 Apache Shiro 身份验证绕过漏洞

1.漏洞信息

1.1 漏洞简介

  • 漏洞名称:Apache Shiro Authentication Bypass Vulnerability

  • 漏洞编号:CVE-2020-11989

  • 漏洞类型:Authentication Bypass

  • CVSS评分:【CVSS v2.0:7.5】【CVSS v3.1:9.8】

  • 漏洞危害等级:高危

1.2 漏洞概述

Apache Shiro 1.5.3之前的版本,由于Shiro拦截器与requestURI的匹配流程与Web框架的拦截器的匹配流程有差异,攻击者构造一个特殊的http请求,可以绕过Shiro的认证,未授权访问敏感路径。此漏洞存在两种攻击方式。

1.3 漏洞利用条件

First Attack

Second Attack

  • 应用不能部署在根目录(1.5.1 < Shiro < 1.5.3)

1.4 漏洞影响

影响版本:
Apache Shiro < 1.5.3

1.5 漏洞修复

获取Apache Shiro最新版本,下载链接:https://shiro.apache.org/download.html

2.漏洞复现

2.1 环境拓扑

2.2 应用协议

8080/HTTP

2.3 复现过程

基于Windows平台,使用环境目录下的shiro-basic.zip环境,解压后,用Idea打开shiro-basic文件夹,下载maven资源,运行ShiroBasicApplication类,即可启动环境。效果如图。

First Attack

构造HTTP请求,发送到服务器,完成身份验证绕过,效果如图

Second Attack

构造HTTP请求,发送到服务器,完成身份验证绕过,效果如图

3.漏洞分析

3.1 详细分析

3.1.1 漏洞利用过程

First Attack

获取到无权限访问的敏感路径,在authc认证路径后添加


%25%32%66
进行身份验证绕过。

Second Attack

获取到无权限访问的敏感路径,在路径的头部添加


/;/
进行身份验证绕过。

3.1.2 代码分析

First Attack

传入的payload首先被服务器接收,并传送给Shiro拦截器处理(org.apache.shiro.web.servlet.OncePerRequestFilter#doFilter方法作为入口)。

调用createSubject方法创建Subject,并调用execute方法进入Shiro FilterChain中。

进入org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver#getChain方法中,首先获取请求uri路径。

在Shiro1.5.2版本中,对于requestURI处理的方式存在一些不同,此处也是漏洞触发点所在。Shiro1.5.2使用的是request.getContextPath(),request.getServletPath(),request.getPathInfo()拼接的方式。由于getServletPath()方法会对requestURI进行一次url解码,在之后的decodeAndCleanUriString方法中进行第二次url解码。

回到getChain方法中,迭代获取拦截器的表达式。

这里重点关注/hello/*表达式。代码进入pathMatches方法,最终调用org.apache.shiro.util.AntPathMatcher#doMatch方法进行传入的requestURI与拦截器表达式进行匹配。

匹配过程中,分别将拦截器表达式与requestURI以/作为分隔符进行字符串到数组的转换,通过循环匹配数组中对应的元素,判断requestURI是否符合拦截器表达式匹配形式。

如果表达式中存在通配符*,会将containsStar标志位赋值为true,进入 else if (patIdxEnd == 0)判断条件,返回true。

最终回到doMatch方法中,通过判断表达式数组的元素个数与requestURI的元素个数,以及表达式中是否包含**,完成后续的匹配。

跟进到Spring处理URI的代码,进入org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#getHandlerInternal方法,获取requestURI。由于Spring获取requestURI时使用getRequestURI()方法,此方法不会进行URL解码。只会在decodeAndCleanUriString完成一次url解码。

进入lookupHandlerMethod方法,调用addMatchingMappings方法,获取Spring拦截器。

进入org.springframework.web.servlet.mvc.condition.PatternsRequestCondition#getMatchingCondition方法调用doMatch方法进行requestURI和拦截器表达式的匹配。

Spring拦截器匹配流程和Shiro大致相同,同样是将字符串转换为数组进行匹配。

由于Spring只进行了一次URL解码,所以将未完全解码的部分作为一个整体,从而完成了拦截器表达式与requestURI的匹配。

Second Attack

漏洞触发点同样是Shiro在修复CVE-2020-1957漏洞时,使用request.getContextPath(),request.getServletPath(),request.getPathInfo()拼接的方式,进行requestURI的获取。

直接跟踪到uri = valueOrEmpty(request.getContextPath()) + “/” + valueOrEmpty(request.getServletPath()) + valueOrEmpty(request.getPathInfo());

在调用getContextPath()方法获取context-path时,会调用removePathParameter方法清除掉分号以及分号到下一个/中间的数据。

接下来进入for循环中匹配candidate与conotext-path是否相同

如果不同,则从传入的URL中继续读取下一级目录,直到condidate与context-path相同,返回从URL截取的目录作为contextPath。由于context-path获取方式和removePathparameters方法对URL的处理,攻击者可以请求,让contextPath变量获取到带有分号的非预期值。

在进行requestURI拼接时,构造出根路径带有分号的requestURI。利用CVE-2020-1957漏洞原理,经过decodeAndCleanUriString方法时,截断reqeustURI中分号后的数据,并返回。从而绕过了shiro权限控制。

3.1.3 漏洞触发过程

First Attack

Second Attack

3.2.4补丁分析

对比Shiro 1.5.2与Shiro 1.5.3版本的改动。

补丁主要优化了getPathWithinApplication方法,并单独定义了getServletPath方法,getPathInfo方法。补丁修复后,调用getPathWithinApplication方法获取requestURI只会在进行getServletPath方法中进行一次url解码,保持与Spring获取requestURI过程中相同的url解码次数。防御了双重url编码绕过。

获取requestURI直接调用getServletPath方法和getPathInfo方法进行拼接,由于不需要与contextpath拼接,从而防御了First Attack攻击。

4.2.3 Apache Shiro 身份验证绕过漏洞

1.漏洞信息

1.1 漏洞简介

  • 漏洞名称:Apache Shiro Authentication Bypass Vulnerability

  • 漏洞编号:CVE-2020-13933

  • 漏洞类型:身份验证绕过

  • CVSS评分:【CVSS v2.0:5.0】【CVSS v3.1:7.5】

  • 漏洞危害等级:高危

1.2 漏洞概述

Apache Shiro 1.6.0之前的版本,由于Shiro拦截器与requestURI的匹配流程与Web框架的拦截器的匹配流程有差异,攻击者构造一个特殊的http请求,可以绕过Shiro的认证,未授权访问敏感路径。

1.3 漏洞利用条件

1.4 漏洞影响

影响版本:
Apache Shiro < 1.6.0

1.5 漏洞修复

获取Apache Shiro最新版本,下载链接:https://shiro.apache.org/download.html

2.漏洞复现

2.1 环境拓扑

2.2 应用协议

8080/HTTP

2.3 复现过程

基于Windows平台,使用环境目录下的shiro-basic.zip环境,解压后,用Idea打开shiro-basic文件夹,下载maven资源,运行ShiroBasicApplication类,即可启动环境。效果如图。

构造HTTP请求,发送到服务器,完成身份验证绕过,效果如图

3.漏洞分析

3.1 详细分析

3.1.1 漏洞利用过程

获取到无权限访问的敏感路径,在authc认证路径后添加%3b进行身份验证绕过。

3.1.2 代码分析

传入的payload首先被服务器接收,并传送给Shiro拦截器处理(org.apache.shiro.web.servlet.OncePerRequestFilter#doFilter方法作为入口)。

调用createSubject方法创建Subject,并调用execute方法进入Shiro FilterChain中。

进入org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver#getChain方法中,首先获取请求uri路径。

在Shiro1.5.3版本中,对于requestURI处理的方式存在一些不同,虽然Shiro官方在此处做了很多优化,但是依然存在与Spring处理方式不一致的请求,导致漏洞的产生。在Shiro1.5.3版本中,requestURI直接调用getServletPath方法和getPathInfo方法进行拼接。并清除解码后的requestURI中分号之后的数据。

回到getChain方法中,迭代获取拦截器的表达式。

这里重点关注/hello/*表达式。代码进入pathMatches方法,最终调用org.apache.shiro.util.AntPathMatcher#doMatch方法进行传入的requestURI与拦截器表达式进行匹配。

匹配过程中,分别将拦截器表达式与requestURI以/作为分隔符进行字符串到数组的转换,通过循环匹配数组中对应的元素,判断requestURI是否符合拦截器表达式匹配形式。

由于解码后的requestURI被分号切割,导致pathDirs数组的元素个数少于pattDirs数组的元素个数。导致拦截器表达式与requestURI匹配失败,绕过认证

跟进到Spring处理URI的代码,进入org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#getHandlerInternal方法,获取requestURI。由于Spring获取requestURI时使用getRequestURI()方法,此方法不会进行URL解码。

进入decodeAndCleanUriString方法,对未进行url解码的requestURI调用removeSemicolonContent方法进行分号截断操作。由于此时的requestURI处于未解码状态,因此编码后的分号是无法被解析的。

进入lookupHandlerMethod方法,调用addMatchingMappings方法,获取Spring拦截器。

进入org.springframework.web.servlet.mvc.condition.PatternsRequestCondition#getMatchingCondition方法调用doMatch方法进行requestURI和拦截器表达式的匹配。

Spring拦截器匹配流程和Shiro大致相同,同样是将字符串转换为数组进行匹配。

由于Spring对requestURI的先进行分号分割操作,再进行了一次URL解码。所以编码后分号之后的数据并不能被有效的分割,进行URL解码后,分号以及分号之后的数据成为了一个新的目录,从而完成了拦截器表达式与requestURI的匹配。

总结:Shiro对于requestURI的处理,先进行URL解码,再进行分号分割。Spring对于requestURI的处理,先进行分号分割,再进行URL解码。二者在解析requestURI时存在差异,因此导致漏洞出现。

3.1.3 漏洞触发过程

3.2.4补丁分析

对比Shiro 1.5.3与Shiro 1.6.0版本的改动,在Shiro 1.6.0版本中新增org.apache.shiro.web.filter.InvalidRequestFilter类。

在InvalidRequestFilter类中定义了SEMICOLON和BACKSLASH变量分别匹配路径中的
;

\
以及URL编码特征。调用isAccessAllowed方法,分别调用containsSemicolon,containsBackslash,containsNonAsciiCharacters方法进行
;

\
和不可见字符的检测。如果上述三个特征存在任意一个,则返回400状态。

在org.apache.shiro.web.filter.mgt.DefaultFilter类中添加InvalidRequestFilter拦截器。在org.apache.shiro.web.config.IniFilterChainResolverFactory类中添加/**拦截器表达式,并为所有拦截器表达式赋予invalidRequest,目的是让所有传入的路径都可以经过InvalidRequestFilter检测。

5.漏洞利用

5.1、Apache Shiro 1.2.4反序列化远程代码执行漏洞

有回显exp插件利用视频:

无回显exp插件利用视频:

5.2、Apache Shiro Padding Oracle Attack 远程代码执行漏洞

6.参考链接

1.https://www.infoq.com/articles/apache-shiro/
2.https://zhuanlan.zhihu.com/p/54176956
3.https://github.com/apache/shiro
4.https://www.cnblogs.com/luop/p/4334160.html
5.http://blog.orange.tw/2018/03/pwn-ctf-platform-with-java-jrmp-gadget.html
6.https://cloud.tencent.com/developer/article/1367702
7.https://www.cnblogs.com/geektcp/p/10061908.html

深信服千里目安全实验室
我还没有学会写个人说明!
上一篇

如何写出更稳定的Python 代码?

下一篇

商店页面显示PS4 Pro暂无将来补货计划 或将停产?

你也可能喜欢

评论已经被关闭。

插入图片