shiro越权学习
前言
去年写的一篇分析shiro-682和CVE-2020-1957的学习笔记,整理了几个实战中黑盒盲打用的payload。后续又出了好几个新的绕过,有机会补一下。
环境
直接用threedr3am师傅的https://github.com/threedr3am/learnjavabug
前置知识
shiro拦截器
参考https://www.freebuf.com/vuls/231909.html讲得很好,偷过来mark一下
Shiro框架通过拦截器功能来实现对用户访问权限的控制和拦截。Shiro中常见的拦截器有anon,authc等拦截器。
1.anon为匿名拦截器,不需要登录就能访问,一般用于静态资源,或者移动端接口
2.authc为登录拦截器,需要登录认证才能访问的资源。
用户可以在Shiro.ini编写匹配URL配置,将会拦截匹配的URL,并执行响应的拦截器。从而实现对URL的访问控制,URL路径表达式通常为ANT格式。如下配置,访问 /index.html主页的时候,Shiro将不会对其进行登录判断,anon拦截器不需要登录就能进行访问。而对于/user/xiaoming 等 /user/xiaogang等接口,authc拦截器将会对其进行登录判断,有登录认证才能访问资源。
[urls]
/index.html = anon
/user/** = authc
Shiro的URL路径表达式为Ant格式,路径通配符支持以下三种写法。
?:匹配一个字符
*:匹配零个或多个字符串
**:匹配路径中的零个或多个路径
其中*表示匹配零个或多个字符串,/*可以匹配/hello,但匹配不到/hello/因为*通配符无法匹配路径。
假设/hello接口设置了authc拦截器,访问/hello将会被进行权限判断,如果请求的URI为/hello/呢,/*URL路径表达式将无法正确匹配,放行。然后进入到spring(Servlet)拦截器,spring中/hello形式和/hello/形式的URL访问的资源是一样的。
实战利用面
shiro这个越权仅仅是绕过了shiro的权限认证,如果这些接口都有验证当前的用户权限,那么shiro越权访问过去的身份其实是个无用户的状态在访问后台接口,所以如果后台存在一些用户状态检测的功能就会使得这个越权变得比较鸡肋。
可以尝试越权访问一些已知的上传接口或者sql注入接口等存在getshell可能的接口,特别是配合源码或者已知的历史漏洞。
尝试越权添加账号或者改密码等操作。
有的项目就只需要能登入核心重要系统,这种情况也可以用shiro越权来
混一混。
shiro-682
https://issues.apache.org/jira/browse/SHIRO-682
影响范围:shiro<1.5.0
成因
关于这个洞的文章比较少,因为确实相对比较简单,参考官方文档就能明白个大概,就是上面shiro拦截器提到的spring和shiro的路径表达式匹配差异导致了权限绕过.
在Spring web项目中,请求URI/resource/menus和/resource/menus/都可以访问到服务器的资源。
但在Shiro中的URL路径表达式pathPattern可以正确匹配/resource/menus,但不能正确匹配/resource/menus/,导致过滤链无法正确匹配,从而绕Shiro的防护机制。
复现
shiro配置如下图,访问到/bypass下的内容即越权成功
可以看到我们对/bypass用的是authc拦截器,正常访问是会跳转到登陆界面
我们只需要在要请求的路径后面加上/即比如我们想越权访问/bypass的内容我们可以请求/bypass/即可,比较简单
实战
影响相对有限,因为还得看具体的shiro配置,比如得下面这样配才能触发绕过问题
map.put(“/bypass/?”, “authc”);
map.put(“/bypass/*“, “authc”);
map.put(“/bypass”, “authc”);
map.put(“/bypass”, “authc”);
但显然除了第二种可能有人会误用,其他几种感觉稍微正常点的程序员都不太会这样干
而如果像下面这样配的
map.put(“/**“, “authc”);
map.put(“/bypass/**“, “authc”);
那么我们的poc是干不动的,后续绕过得参考cve-2020-1957来绕过
所以这个洞建议黑盒情况直接盲打一手/
CVE-2020-1957
https://www.freebuf.com/vuls/231909.html
https://paper.seebug.org/1196/
影响:shiro<=1.5.1
成因
该漏洞是之前shiro-682的修复绕过,shiro官方在shiro1.5.0时把之前提出shiro-682作者提供的修复方案合并至官方项目。
过一眼修复代码,判断当前requestURI是否以/结尾,如果以/
结尾就去掉尾部的/
。也就是我们再以/bypass/
去请求/bypass
时,会被清除尾部的/
后再去进行url路径匹配,至此为止显然shiro-682已经被修复。
上面的修复方案似乎解决了问题,可获取到的requestURI一定是可靠的吗?看一下shiro1.5.2新增的测试用例
图中标出的那一行和其他几行画风有点不太一样,是在暗示什么吗?学习一堆其他师傅的文章后知道这就是当前这个洞的POC
/asdf拼接;/../servlet/other后得到却是/asdf,这就是这个洞最大的成因
在PathMatchingFilterChainResolver.getChain
处下断点,
该函数作用根据URL路径匹配中配置的url路径表达式来匹配输入的URL,判断是否匹配拦截器,匹配成功将会返回响应的拦截器执行链,让ShiroFither执行权限操作的
说人话就是走不走shiro权限检查都是这个函数的运行结果说了算。
我们请求/aaaaa;/../bypass
这个url,通过调试可以看到requestURI的值却是/aaaaa
,显然针对shiro-682的修复此时被绕过了
requestURI
是getPathWithinApplication
方法获取到的,跟一下这个方法,最终一顿跳跳到了decodeAndCleanUriStrin
这个方法
decodeAndCleanUriStrin
先获取url中;
号的位置,如果存在;
号则会删除;
号后面的所有字符。所以遇到/aaaaa;/../bypass
这种URI请求就会截断得到/aaaaa
,于是绕过了shiro-682的修复.
至于为啥要在PathMatchingFilterChainResolver.getChain
处下断点,我们可以先在测试的filter处下断点
可以看到调用栈,可以看到shiro的处理过程如下,在executeChain
后就完全交给spring在处理了
跟进executeChain
方法
可以看到要先获取chain用到了getExecutionChain
方法,跟进
可以看到最终需要调用到getChain
函数,跟进声明函数的地方,发现没啥东西
所以其实这里应该得跳到实现它的地方而不是声明它的地方,最终实现的地方就是咱们之前下断点的getChain
方法
复现
shiro配置如下图,访问到/bypass下的内容即越权成功
这是threedr3am大哥配的一种比较严苛的配置,大哥说正常的程序员都会用下面这种全范围匹配,既方便又安全(如上面shiro-682这种poc遇到全范围匹配是打不动的)
map.put(“/**“, “authc”)
但是如大哥所说实际过程中一般是会放过一些接口的(比如登陆接口),就比如现在这个shiro配置中就是下面两处
map.put(“/login”, “anon”);
map.put(“/aaaaa/**“, “anon”);
与一些复现文章不同,加了全范围匹配后我们乱输入一个路径,比如/xxx;/../bypass
这种是绕不过权限认证的。
这种情况下我们得用/login;/../bypass
或者/aaaaa;/../bypass
才可以绕过权限认证,也就是用一些原本放过权限认证的接口做跳板..这种情景个人感觉是很贴合实战的。
实战
首当其冲的是设置了全范围匹配的站
map.put(“/**“, “authc”)
这种情况要绕过权限认证就得利用一些原本放过权限认证的接口做跳板来构造payload,构造方法参考上面复现中的内容。
其次针对没有全范围匹配的站,比如上面复现内容中的shiro配置只注释全范围匹配那一行,这种情况就可以用随便乱输的xxx构造POC如/xxx;/../bypass
这种达成权限绕,/;/bypass
这种也可以
这种情景的实战案例可参考
可以看到上面我们复现的案例中有这样一行配置
map.put(“/bypass.*“, “authc”);
这一行配置明显画风比较奇怪,参考这个大哥的文章就能明白了,这是一种spring的特性,也就是说我们将/bypass
改为/bypass.
也能达成类似越权的效果,但是这种方法的如果/bypass下面还有多层路由就不好使了,比较鸡肋
所以这个洞直接黑盒盲打以下POC
/;/bypass
/aa;/../bypass
/能正常访问到的路径;/../bypass
/bypass.
修复
参考官方git
不再用request.getRequestURI()
去获取URI,而是用getContextPath()
,getServletPath()
,getPathInfo()
去拼接