CVE-2023-22602
漏洞复现
参考链接:
https://forum.butian.net/share/2118
环境下载地址:
https://github.com/apache/shiro/tree/shiro-root-1.10.1
在 WebApp
里面添加上:
1 | chainDefinition.addPathDefinition("/admin/**", "roles[admin]"); |
然后把 AccountInfoController
的 RequestMapping 改了
1 | @RequestMapping("/admin/**") |
启动以后发现 /admin/ab
会跳转到登陆界面,但是 /admin/..
能直接进方法内。
漏洞分析
首先是访问正常的 /admin/abc
会在哪里判断权限不够呢,入口在这里:
1 | org.apache.shiro.web.servlet.AbstractShiroFilter#doFilterInternal |
这里面会调用到 executeChain
,然后又动态的给原 chain
做了个类似代理的操作(也可以理解为往中间添加了几个 Filter
)。
那么他是怎么加的呢?
关键点在这(这个):
1 | org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver#getChain |
可以看到首先是获取 requestURI
,然后和 shiro
里的规则做匹配,匹配成功就要多走一个过滤器。
当传入 /admin/abc
时 requestURI
自然是 /admin/abc
,但是当传入 /admin/..
时就不一样了:
一直深入能到这里:
1 | org.apache.shiro.web.util.WebUtils#getServletPath |
(这里为了测试我把路由改成了 /test1/test2/..
,鉴权改成 /test1/test2
需要权限)
再进去就是 tomcat
的了,反正就是会把 ServletPath
的 ../
去掉。
这肯定就匹配不上需要鉴权的路由了
那么为什么 Spring 可以匹配上呢?
找到 Spring 匹配路由的位置:
1 | org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#getHandlerInternal |
为了一探究竟,看看这个 attribute
怎么来的:
1 | org.springframework.web.util.ServletRequestPathUtils.ServletRequestPath#parse |
这就很简单了,说白了就是,Shiro 用的 getServletPath
,Spring 用的 getRequestURI
产生的差异。
修复方法
这里下载 1.13
的版本:
https://github.com/apache/shiro/tree/shiro-root-1.13.0-vote-1
上面有一个函数提到了但是没截图:
1 | org.springframework.web.servlet.handler.AbstractHandlerMapping#initLookupPath |
在低版本的话,这里 if
是会直接进去的,但是高版本进入了 else
块。
函数里面判断的是这个属性是否为空:
1 | org.springframework.web.servlet.handler.AbstractHandlerMapping#patternParser |
如果这个属性不为空就进入 IF
,而这个属性默认情况下是设置的。
但是为什么新版本不设置了呢,一直回溯就能到:
1 | org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration.WebMvcAutoConfigurationAdapter#configurePathMatch |
这里判断如果值是 PATH_PATTERN_PARSER
就会 set
进去。
只不过新版的 Shiro
做了一些配置:
1 | org.apache.shiro.spring.config.web.autoconfigure.ShiroEnvironmentPostProcessor |
CVE-2022-32532
可以直接用这个:
https://github.com/Lay0us1/CVE-2022-32532/tree/master
漏洞复现
其实感觉很简单,首先是需要从路由匹配相应的 Filter
在这:
1 | org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver#getChain |
由于 .
本身是不匹配换行的,就可以绕过了。
修复方法
那么新版是怎么修复的呢?
可以看到虽然代码一点没变,但是能匹配成功了,这是因为在里面一点:
1 | org.apache.shiro.util.RegExPatternMatcher#matches |
只要第二个参数大于 32
就可以返回 true
。