标题 | 简介 | 类型 | 公开时间 | ||||||||||
|
|||||||||||||
|
|||||||||||||
详情 | |||||||||||||
[SAFE-ID: JIWO-2024-2884] 作者: 浩丶轩 发表于: [2021-05-14]
本文共 [510] 位读者顶过
环境搭建 为了方便调试shiro包,这里采用IDEA搭建基础Shiro环境 先创建一个spring-boot的基础环境,
[出自:jiwo.org]
成功创建了一个Demo项目
接下来,由于是基于maven构造的依赖,所以我们在pom.xml添加我们想要的shiro版本,这个洞影响的是1.4.2版本以下的话,所以只要选择个shiro的版本比这个低就行了。
这里需要了解一些关于Shiro逻辑规则的前置知识:
然后这里我们需要重点关注就是
anon 不需要验证,可以直接访问
authc 需要验证,也就是我们需要bypass的地方
Shiro的URL路径表达式为Ant格式:
CVE时间线
这个可以从官方安全报告可以得到比较官方的时间线:https://shiro.apache.org/security-reports.hCVE-2020-17510tml
下面让我们逐步分析,这些CVE的形成原因,最后再对成因做一个总结。
CVE-2020-1957
影响版本: shiro<1.5.2
类型: 权限绕过
其他信息:
这个洞可以追溯下SHIRO-682,1957 在此1.5.0版本修复的基础上实现了绕过。
只是做了下Path的路径检测,然后去掉了结尾/
修改下shiro的检验配置:
config配置(这个很重要,必须)
Controller接口
然后我们在maven中修改下Shiro的版本为1.5.1,然后还有个坑点就是要复现这个的话spring-boot的版本记得改为:1.5.22.RELEASE,要不然是没办法复现成功的. 至于为什么这里简单说说吧,就是
lookupPath来源的问题,旧版本能够解析为/admin,而新版本直接解析为/static/../admin,然后基于lookupPath去寻找对应的RequestMapping方法自然是找不到的,要么就避免引入..
限于文章篇幅,关于理解下面两个版本的结果,可以先看看Tomcat URL解析差异性导致的安全问题的一些相关内容,这里就不去解释了。
旧版本是:
/web/servlet/handler/AbstractHandlerMethodMapping.class:175
而新版本是:
org.springframework.web.servlet.handler.AbstractHandlerMapping#initLookupPath
然后下面是针对不同的漏洞使用不同的Shiro版本maven文件。
<dependency>
直接访问是被拒绝的。
绕过:
spring新版本(不能引入):
POC:
/fsdf;/../hello/1111
那么如果map这样设置,这个洞依然是可以的,至于为什么,下面漏洞分析会说明。
通过diff 1.5.2 与 1.5.0的代码,可以确定在这里出现了问题
我们debug直接跟到这里:
然后在这里的话,首先会做urldecode解码然后会删除掉uri中;后面的内容,然后normalize规范化路径。
然后返回的是这个路径:
然后Shiro开始做匹配,从this.getFilterChainManager()获取定义的URL规则和权限规则来判断URL的走向。
这里没有定义fsdf,所以自然没有找到,直接返回了Null
然后开始走默认的default的URL规则,经过Spring-boot解析,tomcat解析之后到达了真正的函数点。
这里简化点,通俗来说就是, 一个URL
首先要走Shiro的过滤器处理,解析得到/fsdf发现没有匹配的拦截器,那么就默认放行,如果有那么就进行权限认证,shiro绕过之后,然后来到了Spring-boot解析,然后Spring-boot在查找方法的时候会调用tomcat的getServletPath,那么就会返回/hello/1111去RequestMapping去找相对应我们定义的方法,那么可以绕过了。
其实关于这个payload我们还可以这样:
原因是:
在流向的过程中,tomcat会对特殊字符;处理去掉((;XXXX)/)括号里面的内容得到`/fsdf/../a/../hello/1 ,传递给getServletPath,最终得到/hello/1作为lookupPath,去RequestMapping对应的函数来调用。
这里我们修改maven,shiro升级到1.5.2
修复代码,细究下:
可以看到原先是由
request.getRequestURI():根路径到地址结尾,原封不动,不走任何处理。
现在变为了:
项目根路径(Spring MVC下如果是根目录默认是为空的)+相对路径+getPathInfo(Spring MVC下默认是为空的)
其实就是统一了request.getServletPath()来处理路径再进行比较,这里是Shiro主动去兼容Spring和tomcat。
CVE-2020-11989
影响版本: shiro<1.5.3
类型: 权限绕过
其他信息:
https://xlab.tencent.com/cn/2020/06/30/xlab-20-002/
https://xz.aliyun.com/t/7964
其实这两篇文章成因很显然是不同的,但是修补方式是可以避免这两种绕过方式的,让我们来分析下吧。
这个漏洞的话,限制比CVE2020-1957多点,比如对于/**这种匹配的话是不存在漏洞还有就是针对某类型的函数,第二种利用则是需要context-path不为空,这个利用就和CVE-2020-1957差不多。
第一种:
这个还不会受到Spring MVC版本的影响。
同时我们还需要改一下我们的方法:
需要获取的参数为String的,因为后面就是基于这个String类型来针对这种函数的特殊情况来绕过的。
第一种绕过方式对于这种是无效的,必须是动态获取到传入的内容,然后把传入的内容当做参数才行,像下面这个没有动态参数的话,那么根本就没办法匹配到more:
第二种:
这种情况就和CVE2020-1957的绕过原理很像,就是基于;这个解析差异来实现绕过,但是官方缺乏考虑边缘情况,导致了绕过
这里新版本Spring是不行,因为在getPathWithinServletMapping实现不同,pathWithinApp
变成了contextPath
2.0之后的新版本配置Context-path:
第一种:
%25%32%66其实就是%2f的编码
第二种:
/;/shiro/hello/hi
先说第一种,还是路径解析差异导致,但是属于多一层URL解码,emm
还是在原来那个地方下一个断点
这一行和上面分析差不多,然后这里注意下:
这里传入URL的时候,request.getServletPath()会做一层URL解码处理(Tomcat URL解析差异性导致的安全问题),
然后我们继续跟进去:normalize(decodeAndCleanUriString(request, uri));
可以看到这里又做了一层decode处理,下一个断点,跟进去这个是什么处理的。
没什么好说的,检测一下编码,然后URLDecoder解码,把本来我想着有没有那种纯数字编码的,这样利用范围就会大一些,比较极端的情况啦,确实没有,解码之后传入normalize做一些规范化处理,这个函数做了什么规范化处理呢,其实也可以看看。
感觉emm,会有点多余啦,这里写了个循环去删除/./和/../,这个其实都会被处理掉的
这里就先姑且当做双重保险,normalize函数的作用跟我们这次漏洞没啥关系。
最终传入Shiro进行和/hello/*匹配的是
原始hello/luanxie%25%32%661->经过Shiro的getRequestUri->组装URL`request.getServletPath(这里解码一次) ->decodeAndCleanUriString(这里解码一次)->normalize->最终变成了-/hello/luanxie/1,然后进入了Shiro的匹配了,所以如果/hello/**这样的配置是可以匹配到多路径的,但是单*号的话,是没办法处理这个路径的,直接放行,然后request继续走呀走呀,走到Spring那里直接取request.getServletPath也就是/hello/luanxie%2f1,作为lookpath,去寻找RequestMapping有没有合适的定义的方法,结果发现
这个参数hello/luanxie%2f1正好就是/hello/String的模式呀,那么就直接调用了这个函数hello1,实现了绕过。
下面说说第二种绕过方式,说实话,这种绕过方式其实应用场景更广
这个问题主要tomcat的getContextPath的实现上
org.apache.catalina.connector.Request#getContextPath
可以看到这个函数执行操作是POS会一直++直到匹配到`/shiro,
然后返回的时候直接返回0-Pos位置的字符串,怎么说呢,这个设计可能是为了兼容../的类似情况,然后导致最终解析的URL引入了;
然后后面的话,就回到我们之前2020-1957的分析的,只不过这次
;的引入不再是由request.getRequestURI()引入,这次引入是补丁中的getContextPath这个拼接的时候引入的,然后Shiro对于;处理也说了,直接删掉;后面的内容,所以最终返回的是fsdf去匹配Shiro我们定义的正则。
所以这样去绕过也可以的。
直接比对下代码:https://github.com/apache/shiro/compare/shiro-root-1.5.2…shiro-root-1.5.3
这次的修补地方,主要是改了getPathWithinApplication(这个函数返回的uri是用于后面Shiro进行URL过滤匹配的)。
这样没有了多一重的URL解码,解决了问题1,然后删掉了ContextPath,解决了问题2。
其实可以思考下,getPathInfo如果也可以引入;那么一样是会存在漏洞的,笔者对于挖Shiro的这种有限制的0day并不感兴趣,有兴趣的读者可以去挖。
|