刚好在学shiro安全,就看了看shiro权限绕过方面的洞
学习一下shiro的配置与使用
https://cloud.tencent.com/developer/article/1643122
影响版本:shiro < 1.5.3
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2myCmL55-1659858009785)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220807142203425.png)]](https://1000bd.com/contentImg/2022/08/12/073507937.png)
添加shiro配置
Shiro 中的匹配规则是通过 AntPathMatcher 来进行实现的
? 匹配一个字符
* 匹配一个或多个字符
** 匹配一个或多个目录
ps:这里的规则是 /admin/* 所以 Shiro 并不会对多个目录进行权限校验,例如:/admin/aaa/bbb 这种是不会对其进行权限校验的
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MJCtlhSX-1659858009786)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220807142401310.png)]](https://1000bd.com/contentImg/2022/08/12/073508140.png)
添加一个controller,这个只有在认证后才能访问
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ep1YYhlk-1659858009787)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220807142627847.png)]](https://1000bd.com/contentImg/2022/08/12/073508355.png)
正常情况访问/admin/aaa,因为没有认证所以会弹回登录页
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JHKwOtko-1659858009787)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220807142841519.png)]](https://1000bd.com/contentImg/2022/08/12/073508480.png)
payload:/admin/Hello%252faaaa
成功绕过shiro认证,进入到controller中
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-W8wp3MWR-1659858009787)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220807142954943.png)]](https://1000bd.com/contentImg/2022/08/12/073508894.png)
经过调试发现本质其实就是shiro和spring在处理url时候的规则不同导致的认证绕过
通过断点可以看到请求先经过shiro然后再进入spring分发请求到controller
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CUVyceYl-1659858342664)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220807154522278.png)]](https://1000bd.com/contentImg/2022/08/12/073509134.png)
Shiro的认证是通过新建filter来实现的,shiro首先会根据uri来获取对应的shiro-filter
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1rhULuQN-1659858009788)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220807143414926.png)]](https://1000bd.com/contentImg/2022/08/12/073509466.png)
跟进getRequestUri,发现uri是通过valueOrEmpty(request.getContextPath()) + “/” + valueOrEmpty(request.getServletPath()) + valueOrEmpty(request.getPathInfo())获得的,而其中
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jQnyqxLQ-1659858009788)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220807143542302.png)]](https://1000bd.com/contentImg/2022/08/12/073509703.png)
对于request.getServletPath(),request.getContextPath() 以及 request.getPathInfo(),以 Tomcat 为例的中间件是会对其进行 URL 解码操作的
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MdENsvPR-1659858009788)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220807143708879.png)]](https://1000bd.com/contentImg/2022/08/12/073509842.png)
此时uri为//admin/Hello%2faaaa
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-66TTIkH9-1659858009788)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220807143822654.png)]](https://1000bd.com/contentImg/2022/08/12/073510046.png)
然后再进入decodeAndCleanUriString函数进行一次解码,他这里会判断是否存在封号,如果存在就截取封号前面的,这样是为了避免http://www.xxxx.com/xxxx;jession=xxxxxx这种情况
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-D0Q6v48X-1659858009789)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220807143925165.png)]](https://1000bd.com/contentImg/2022/08/12/073510390.png)
所以总的来说在getRequestUri函数中,进行了两次url解码操作
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xWNQKLXK-1659858009789)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220807144133458.png)]](https://1000bd.com/contentImg/2022/08/12/073510660.png)
返回到最开始的位置,可以看到requestUri为/admin/Hello/aaaa
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9onawq6w-1659858009789)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220807144200421.png)]](https://1000bd.com/contentImg/2022/08/12/073510848.png)
继续往上层return,来到getChain方法,此时获取到了requestURI,下面就是匹配对应的shiro-filter了。首先获取所有shiro-filter
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-getkLC0C-1659858009790)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220807144442383.png)]](https://1000bd.com/contentImg/2022/08/12/073511207.png)
可以说是一个都匹配不上,最终返回null
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-F7RCsJKF-1659858009790)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220807144543663.png)]](https://1000bd.com/contentImg/2022/08/12/073511535.png)
return到上层,因为返回为null,所以进入了else,最终只返回了默认的 ApplicationFilterChain,在默认的 ApplicationFilterChain 中是没有任何权限校验
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7G1VTtLF-1659858009790)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220807144739558.png)]](https://1000bd.com/contentImg/2022/08/12/073511895.png)
没有一个shiro-filter被匹配到,至此 Shiro 层面的权限就成功绕过了
接下来就是要能让spring将我们的请求发送到对应的controller
熟悉 Spring 的师傅应该都知道在 Spring 中 DispatcherServlet 是负责请求派发的,即将对应的请求转发到对应的 Controller 来处理,其一个主要的操作是通过 HandlerMappings来将请求映射到处理器Handler。在处理过程中会调用 getHandler 方法来获取一个可以处理该请求的Handler(即与该请求匹配的handler,这个handler最终会调用相应controller)
HandlerMapping在这个SpringMVC体系结构中有着举足轻重的地位,充当着url和Controller之间映射关系配置的角色
http://www.51gjie.com/javaweb/921.html
继续往下debug,来到spring获取Handler的地方
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nwSI7MxT-1659858009790)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220807145120095.png)]](https://1000bd.com/contentImg/2022/08/12/073512082.png)
在该方法中会获取所有的handlerMappings,通过调用HandlerMapping的getHandler方法来进行判断是否这个Handler可以处理当前请求
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lzZZEJ8o-1659858009791)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220807145825794.png)]](https://1000bd.com/contentImg/2022/08/12/073512285.png)
继续跟进
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QMhoRtFH-1659858009791)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220807145932631.png)]](https://1000bd.com/contentImg/2022/08/12/073512530.png)
继续跟进来到getHandlerInternal,调用了父类getHandlerInternal,这里获取request的uri,这里也是导致绕过最重要的地方,跟进去看看
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NsfyvKlF-1659858009791)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220807150005367.png)]](https://1000bd.com/contentImg/2022/08/12/073512753.png)
跟进resolveAndCacheLookupPath
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VJse8UAk-1659858009791)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220807150049850.png)]](https://1000bd.com/contentImg/2022/08/12/073513019.png)
到了spring真正获取uri的地方了
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hP2YCXTH-1659858009791)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220807150134793.png)]](https://1000bd.com/contentImg/2022/08/12/073513316.png)
调用了getPathWithinApplication,调用了getRequestUri
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JHhPq66u-1659858009792)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220807150255585.png)]](https://1000bd.com/contentImg/2022/08/12/073513472.png)
注意看,这个getRequestUri与shiro中那个getRequestUri不一样,这个是调用了request.getRequestURI()
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jsitW6bY-1659858009792)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220807150724462.png)]](https://1000bd.com/contentImg/2022/08/12/073513679.png)
这个request.getRequestURI()是不会对uri进行解码的,以前的 shiro 使用 request.getRequestURI() 获取用户请求路径,并自行处理,此时 shiro 默认Servlet 容器(中间件)不会对路径进行 URL 解码操作,通过其注释可以看到;
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Zc0qvAtZ-1659858009792)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220807151002301.png)]](https://1000bd.com/contentImg/2022/08/12/073513879.png)
但是呢后来在 1.5.2 版本的 shiro 更新中,为了修复 CVE-2020-1957 ,将 request.getRequestURI() 置换为了 valueOrEmpty(request.getContextPath()) + "/" + valueOrEmpty(request.getServletPath()) + valueOrEmpty(request.getPathInfo());而对于 request.getContextPath() 以及 request.getPathInfo(),以 Tomcat 为例的中间件是会对其进行 URL 解码操作的,此时 shiro 再进行 decodeAndCleanUriString,就相当于进行了两次的 URL 解码,而与之后的 Spring 的相关处理产生了差异。
下图也可以看到并没有对uri解码
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OkJBlWOx-1659858009792)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220807151114474.png)]](https://1000bd.com/contentImg/2022/08/12/073514145.png)
此时再传进decodeAndCleanUriString,相当于是只进行了一次解码
回到上一层,可以看到只进行了一次解码
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eJ1SmQdp-1659858009792)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220807151205098.png)]](https://1000bd.com/contentImg/2022/08/12/073514332.png)
重新回到 getHandlerInternal 函数,可以看到 lookupPath 和 request 传入了 lookupHandlerMethod 函数中,这里的 lookupPath 其实就是获取到了我们请求对应的 uri的一次解码,接下来就可以根据lookupPath来匹配Controller的Handler了
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0DB0ddqQ-1659858009793)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220807151432152.png)]](https://1000bd.com/contentImg/2022/08/12/073514535.png)
跟进添加匹配的mapping
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-N8IZVuLo-1659858009793)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220807151556604.png)]](https://1000bd.com/contentImg/2022/08/12/073514738.png)
可以看到第一个mapping为/unauth这个,如果request能和这个mapping符合就匹配上这个mapping,显然是不符合的
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MhziDjeg-1659858009793)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220807151931719.png)]](https://1000bd.com/contentImg/2022/08/12/073514942.png)
跟进getMatchingMapping(判断是否符合)最终来到getMatchingCondition,进行了一系列的判断来到路由匹配
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LlliF0PW-1659858009793)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220807151801224.png)]](https://1000bd.com/contentImg/2022/08/12/073515161.png)
可以看一下路由/unauth,显然和request中的不匹配
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aZ39ZrZo-1659858009793)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220807152054183.png)]](https://1000bd.com/contentImg/2022/08/12/073515520.png)
所以返回null
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-e2Z7vuxT-1659858009794)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220807152120818.png)]](https://1000bd.com/contentImg/2022/08/12/073515692.png)
然后一个个mapping和request匹配,最终来到/admin/{name}这个mapping
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gITetrni-1659858009794)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220807152343864.png)]](https://1000bd.com/contentImg/2022/08/12/073516207.png)
跟进getMatchingMapping
最终来到路由匹配
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GUWzYANb-1659858009794)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220807152442016.png)]](https://1000bd.com/contentImg/2022/08/12/073516488.png)
spring只对uri一次解码,所以说uri是/admin/Hello%2faaaa,可以和/admin/{name}匹配上
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dgj9jygZ-1659858009794)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220807152625846.png)]](https://1000bd.com/contentImg/2022/08/12/073516723.png)
匹配成功就添加到 matches 中
上面的这些功能都是为了在所有匹配的Handler之后需要挑选一个最合适的Handler进行请求的处理,获取到合适的 Handler 之后就进行Handler的访问来处理请求
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iv1ErMe6-1659858009795)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220807152949904.png)]](https://1000bd.com/contentImg/2022/08/12/073516864.png)
总的来说就是shiro和spring对于url的处理方式不同导致的
shiro在获取uri时(为了下一步匹配shiro-filter)对其进行了两次解码
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vNbi8NKk-1659858009795)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220807153144300.png)]](https://1000bd.com/contentImg/2022/08/12/073517129.png)
spring在获取uri时(为了下一步匹配controller)对其进行了一次解码
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sJ3taMJX-1659858009795)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220807153344089.png)]](https://1000bd.com/contentImg/2022/08/12/073517270.png)
所以说/admin/Hello%252faaaa在shiro眼里就是/admin/Hello/aaaa,即并不会触发shiro的认证filter
而在后面的spring眼里是/admin/Hello%2faaaa,刚好匹配上了/admin/{name}这个controller,从而实现了认证绕过
https://www.yuque.com/tianxiadamutou/zcfd4v/emcdeq
https://su18.org/post/shiro-3/#cve-2020-11989
https://cloud.tencent.com/developer/article/1643122
https://www.anquanke.com/post/id/218270#h3-7
http://www.51gjie.com/javaweb/921.html