声明
出品|先知社区(ID:alter99)
以下内容,来自先知社区的alter99作者原创,由于传播,利用此文所提供的信息而造成的任何直接或间接的后果和损失,均由使用者本人负责,长白山攻防实验室以及文章作者不承担任何责任。
CVE-2020-11989
漏洞信息
漏洞编号:CVE-2020-11989 / SHIRO-782
影响版本:shiro < 1.5.3
漏洞描述:在Shiro<1.5.3的情况下,将Shiro与Spring Controller一起使用时,相应请求可能会导致身份验证绕过。
漏洞补丁:Commit
参考: 腾讯安全玄武实验室Ruilin师傅 边界无限 淚笑师傅
这个漏洞有两种绕过方式,分别由腾讯安全玄武实验室的Ruilin师傅和来自边界无限的淚笑师傅报告
漏洞分析 —— 两次解码绕过
限制
这个场景下需要一些限制条件,首先配置文件的ant风格需要是*而不是**,测试发现,?也可以,另外controller需要接收的request参数(@PathVariable)的类型需要是String,否则将会出错。
复现
首先复现一下,测试版本 1.5.2。
编写Controller
@RequestMapping("/toJsonList/{name}")
@ResponseBody
public List<User> toJsonList(@PathVariable String name){
System.out.println("返回json集合数据");
User user1 = new User();
user1.setName("alter1");
user1.setAge(15);
User user2 = new User();
user2.setName("alter2");
user2.setAge(12);
List<User> userList = new ArrayList<User>();
userList.add(user1);
userList.add(user2);
return userList;
}
配置对应的shiro.ini
[urls]
/toJsonList/* = authc
此时请求/toJsonList/aaa那么将会被禁止。
但是这里我们可以通过url双编码的方式来绕过。
/ -> %2f ->%25%32%66
测试发现下面四种组合只有前两组可以绕过
yes
/toJsonList/a%25%32%66a
/toJsonList/%25%32%66
no
/toJsonList/%25%32%66a
/toJsonList/a%25%32%66
分析
首先要清楚Shiro支持 Ant 风格的路径表达式配置。ANT 通配符有 3 种,如下所示:
通配符 | 说明 |
---|---|
? | 匹配任何单字符 |
* | 匹配0或者任意数量的字符 |
** | 匹配0或者更多的目录 |
解释一下就是/** 之类的配置,匹配路径下的全部访问请求,包括子目录及后面的请求,如:/admin/** 可以匹配 /admin/a 或者 /admin/b/c/d 等请求。
对于/*的话 ,单个*不能跨目录,只能在两个/之间匹配任意数量的字符,如/admin/* 可以匹配 /admin/a 但是不能匹配 /admin/b/c/d。
那么问题来了,如果我们将其配置为/toJsonList/*,但是我们访问形如/toJsonList/a/b这种路径,此时就会绕过访问权限。
我们还记得为了修复CVE-2020-1957,shiro在1.5.2版本进行了更新,将request.getRequestURI()修改为request.getContextPath(),request.getServ-letPath()、request.getPathInfo() 拼接构造uri。
根据网上师傅们的总结,这几个方法的差异性如下:
-
request.getRequestURL():返回全路径;
-
request.getRequestURI():返回除去Host部分的路径;
-
request.getContextPath():返回工程名部分,如果工程映射为/,则返回为空;
-
request.getServletPath():返回除去Host和工程名部分的路径;
-
request.getPathInfo():仅返回传递到Servlet的路径,如果没有传递额外的路径信息,则此返回Null;
第一次解码发生在request.getServletPath()
第二次解码发生在
decodeAndCleanUriString() -> decodeAndCleanUriString() -> decodeRequestString() -> URLDecoder.decode()
因此org.apache.shiro.web.util.WebUtils#getRequest-Uri进行了两次解码,将/toJsonList/a%25%32%66a解码成/toJsonList/a/a
接着就走到org.apache.shiro.util.AntPathMatch-er#doMatch进行权限验证,/toJsonList/a/a不满足配置中的toJsonList/*,因此成功绕过。
但还要看Spring是怎么对其进行解析的,在org.s-pringframework.web.uti.UrlPathHelper#getPathWithinApplication中,将url解析为/toJsonList/a%2fa,这样其实就表示/toJsonList/{name}中的name值为a%2fa。
分析完之后, 也就解释了为什么下面四种组合只有前两组可以绕过
(这种二次解码的方式我测试只适用于1.5.2的版本,之前的版本使用a%25%32%66a测试,因为只有一次解码,会跳转至登陆界面;a%2fa测试直接返回400 Bad Request,应该是请求的问题,希望能有师傅帮忙解答一下)
漏洞分析 —— 根路径差异化解析绕过
限制
-
若 Shiro >= 1.5.2 的话,应用不能部署在根目录,如果为根目录则 context-path 为空, CVE-2020-1957 更新补丁将 URL 格式化。
-
Spring 控制器中没有另外的权限校验代码
#### 复现
本次复现使用的是1.4.2版本的shiro所以应用根目录是什么都没有关系
配置为
/alter/* = authc
/** = anon
新增一个controller
@RequestMapping("/alter/test")
@ResponseBody
public List<User> test(){
User user1 = new User();
user1.setName("alter");
user1.setAge(15);
List<User> userList = new ArrayList<User>();
userList.add(user1);
return userList;
}
输入地址:
http://localhost:8080/;/shirodemo/alter/test
org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver#getChain会进行如下操作获取uri
此时uri结果为/,绕过配置/alter/* = authc,符合配置/** = anon,达到绕过目的。Spring在处理uri时直接进行路径标准化,去掉了分号
Shiro < 1.5.2版本的话,根路径是什么没有关系
漏洞修复
Shiro在Commit中修改了URL获取的逻辑,不单独处理context-path,这样不会导致绕过,同时也避免了二次URL解码的问题。
回退了WebUtils#getRequestUri的代码,并将其标记为@Deprecated
可以看到,shiro建议使用getPathWithin-Application()方法获取路径减去上下文路径,或直接调用HttpServletRequest.getRequestURI()方法获取。
在WebUtils#getPathWithinApplication方法,修改了使用RequestUri去除 ContextPath 的方式,改为使用 getServletPath(request) + getPathInfo(request))。然后使用removeSemicolon方法处理分号问题,normalize方法进行路径标准化。
CVE-2020-13933
漏洞信息
**漏洞编号:**CVE-2020-13933 / CNVD-2020-46579
**影响版本:**shiro < 1.6.0
**漏洞描述:**Shiro 由于处理身份验证请求时存在权限绕过漏洞,特制的HTTP请求可以绕过身份验证过程并获得对应用程序的未授权访问。
**漏洞补丁:**Commit
漏洞分析
这个CVE其实就是对CVE-2020-11989 patch的绕过。上一个CVE使用getServletPath(request)+get-PathInfo(request))获取uri,回顾一下:
-
request.getServletPath():返回除去Host和工程名部分的路径;
-
request.getPathInfo():仅返回传递到Servlet的路径,如果没有传递额外的路径信息,则此返回Null;
Shiro在getChain内进行权限验证,首先通过getPathWithinAppli-cation(request)获得uri。
从下图可以看到,更新后使用HttpServlet-Request.getRequestURI() 方法获取uri;然后使用removeSemicolon去除uri中的分号,这里去除的是分号及分号后面的内容;然后使用normalize进行路径标准化。
此时得到的路径为/hello,绕过了配置中的权限。
接着看Spring是怎么处理路径的:
在org.springframework.web.util#UrlPathHelper中的getPathWithinApplicat-ion方法内,使用getRequest-Uri(request)方法获取uri
与Shiro处理的差异达到既绕过Shiro权限验证又成功访问的目的。
漏洞修复
shiro在1.6.0版本中,org.apache.shiro.spring.web-#ShiroFilterFactoryBean中增加了/**的默认路径配置,使其可以全局匹配进行过滤校验
默认的/**配置对应一个全局的filter:InvalidRequestFilter,这个类继承了 AccessCon-trolFilter。用来过滤特殊字符(分号、反斜线、非ASCII码字符),并返回 400 状态码。