Shiro CVE-2022-40664 请求转发导致的验证绕过

2025-02-04 5 0

漏洞描述

Apache Shiro before 1.10.0, Authentication Bypass Vulnerability in Shiro when forwarding or including via RequestDispatcher.

[1]

漏洞条件

  • shiro < 1.10.0

  • 请求转发(forwarding)或者including:
    存在一个不需要验证的路径转发(或including)到需要验证的路径。

漏洞复现

环境

shiro:1.9.1
springboot:2.7.4(tomcat 9.0.96)

shiro:

@Bean
    public IniRealm getIniRealm() {
        return new IniRealm("classpath:shiro.ini");
    }

    @Bean
    public DefaultWebSecurityManager securityManager(IniRealm iniRealm) {
        DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
        manager.setRealm(iniRealm);
        return manager;
    }

    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultWebSecurityManager securityManager) {
        ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
        bean.setSecurityManager(securityManager);
        bean.setLoginUrl("/login");
        bean.setSuccessUrl("/loginSuccess");
        bean.setUnauthorizedUrl("/unauthorized");

        // URL 过滤规则
        LinkedHashMap<String, String> map = new LinkedHashMap<>();
        map.put("/withAuthcPage", "authc");
        map.put("/login", "authc"); 
        bean.setFilterChainDefinitionMap(map);

        return bean;
    }

	//【补丁必要配置】!!!!
    @Bean
    public FilterRegistrationBean<AbstractShiroFilter> shiroFilterRegistration(ShiroFilterFactoryBean shiroFilterFactoryBean) throws Exception{
        FilterRegistrationBean<AbstractShiroFilter> registrationBean = new FilterRegistrationBean<>();

        // 获取 ShiroFilter 的实例
        registrationBean.setFilter(shiroFilterFactoryBean.getObject());


		// 设置 DispatcherType
        registrationBean.setDispatcherTypes(DispatcherType.REQUEST, DispatcherType.FORWARD); 

        return registrationBean;
    }

最后一项配置等效于原生环境:

<filter>
        <filter-name>ShiroFilter</filter-name>
        <filter-class>org.apache.shiro.web.servlet.ShiroFilter</filter-class>
    </filter>

    <filter-mapping>
        <filter-name>ShiroFilter</filter-name>
        <url-pattern>/*</url-pattern>
        <dispatcher>REQUEST</dispatcher>
        <dispatcher>FORWARD</dispatcher>
    </filter-mapping>

controller:注意controller不要用@RestController,这会使得字符串关键字“forward”无效

//@Controller
//....

	@GetMapping("/noAuthcPage")
    public String noAuthcPage() {

        return "forward:/withAuthcPage";
    }
	
	//以上方法等效于:
	@GetMapping("/noAuthcPage")
    public void noAuthcPage(HttpServletRequest request, HttpServletResponse response) {
        try{
            RequestDispatcher requestDispatcher = request.getRequestDispatcher("/withAuthcPage");
            requestDispatcher.forward(request,response);
        }catch (Exception e){
            //ignore
        }

    }


	@ResponseBody
    @GetMapping("/withAuthcPage")
    public String withAuthcPage() {
        return "withAuthcPage";
    }

测试

Shiro CVE-2022-40664 请求转发导致的验证绕过插图

漏洞分析

OncePerRequestFilter所在类图:

Shiro CVE-2022-40664 请求转发导致的验证绕过插图1

左边的分支是Shiro内部的过滤器,右分支AbstractShiroFilter才是正真注册在Web容器中的Filter,shiro-spring中注册到Web容器中的Filter是org.apache.shiro.spring.web.ShiroFilterFactoryBean$SpringShiroFilter(implements AbstractShiroFilter)

OncePerRequestFilter.doFilter(...):省去日志相关内容

public final void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        String alreadyFilteredAttributeName = getAlreadyFilteredAttributeName();
    	//漏洞点
        if ( request.getAttribute(alreadyFilteredAttributeName) != null ) {

            filterChain.doFilter(request, response);
        } else //noinspection deprecation
            if (!isEnabled(request, response) ||
                 shouldNotFilter(request) ) {

            filterChain.doFilter(request, response);
        } else {
            // Do invoke this filter...
            //第一次会进入这个分支
            request.setAttribute(alreadyFilteredAttributeName, Boolean.TRUE);

            try {
                //由子类实现
                doFilterInternal(request, response, filterChain);
            } finally {
                // Once the request has finished, we're done and we don't
                // need to mark as 'already filtered' any more.
                request.removeAttribute(alreadyFilteredAttributeName);
            }
        }
    }

SpringShiroFilter第一次doFilter:
Shiro CVE-2022-40664 请求转发导致的验证绕过插图2

请求转发后SpringShiroFilter第二次进入:

Shiro CVE-2022-40664 请求转发导致的验证绕过插图3

由于请求转发后的请求本质上还是同一个请求对象,因此会进入图中分支。这里filterChain是ApplicationFilterChain,也就是web容器内的过滤链,filterChian.doFilter()将会导致,filterChian直接跨过当前的SpringShiroFilter,进入下一个过滤器(或直接进入servlet),从而绕过shiro。

漏洞修复

改变了判定条件:[3]

Shiro CVE-2022-40664 请求转发导致的验证绕过插图4Shiro CVE-2022-40664 请求转发导致的验证绕过插图5

但是这个补丁,是有限制的,必须要用上文环境部分的【补丁必要配置】,才能够生效

为什么要有【补丁必要配置】?

其实如果没有【补丁必要配置】,则转发的请求根本不会进入shiro,最终也能得到相同的测试结果。这是因为tomcat在请求转发后会重新生成生成过滤链,然后request重新经过filterChains-->servlet,如果没有该配置,则SpringShiroFilter不会被添加进这个过滤链,从而绕过shiro限制.[2]

核心类以及对应方法:

//org.apache.catalina.core.ApplicationDispatcher implements AsyncDispatcher, RequestDispatcher


private void invoke(ServletRequest request, ServletResponse response,
            State state) throws IOException, ServletException {

        // Checking to see if the context classloader is the current context
        // classloader. If it's not, we're saving it, and setting the context
        // classloader to the Context classloader
        ClassLoader oldCCL = context.bind(false, null);

        // Initialize local variables we may need
        ....

        // Check for the servlet being marked unavailable
        if (wrapper.isUnavailable()) {
           ......
        }

        // Allocate a servlet instance to process this request
        try {
            if (!unavailable) {
                servlet = wrapper.allocate();
            }
        } catch (ServletException e) {
            ....
        }

        // Get the FilterChain Here
    	//如果没有【补丁必要配置】,则SpringShiroFilter不会被添加进去
        ApplicationFilterChain filterChain =
                ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);

        // Call the service() method for the allocated servlet instance
        try {
            // for includes/forwards
            if ((servlet != null) && (filterChain != null)) {
               filterChain.doFilter(request, response);
             }
            // Servlet Service Method is called by the FilterChain
        } catch (ClientAbortException e) {
          .....
        }

        //.....释放资源

读者可以自行尝试在大于等于shiro 1.10.0的版本中,不用【补丁必要配置】,会发生什么。本人在shiro 1.13.0条件下,不用【补丁必要配置】(其余不变),发现是可以绕过的。这里就不展开了

Reference


[1] Security Reports | Apache Shiro

[2] 关于filter过滤器为何不能过滤转发的请求 - mrsl - 博客园

[3] commit


4A评测 - 免责申明

本站提供的一切软件、教程和内容信息仅限用于学习和研究目的。

不得将上述内容用于商业或者非法用途,否则一切后果请用户自负。

本站信息来自网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑或手机中彻底删除上述内容。

如果您喜欢该程序,请支持正版,购买注册,得到更好的正版服务。如有侵权请邮件与我们联系处理。敬请谅解!

程序来源网络,不确保不包含木马病毒等危险内容,请在确保安全的情况下或使用虚拟机使用。

侵权违规投诉邮箱:4ablog168#gmail.com(#换成@)

相关文章

KrbRelayEx:一款针对Kerberos的网络请求中继与转发工具
Hayabusa:一款针对Windows事件日志的威胁搜索与取证分析工具
Shiro CVE-2023-22602 补丁失效导致的路径匹配绕过
谷歌修复安卓内核零日漏洞,攻击者已利用该漏洞发起攻击
Shiro CVE-2023-34478 路径规范化不一致
2024年768个CVE漏洞被利用,较2023年增长20%

发布评论