Shiro CVE-2023-46749 路径穿越导致验证绕过

2025-02-13 34 0

漏洞描述

Apache Shiro before 1.13.0 or 2.0.0-alpha-4, maybe susceptible to a path traversal attack that results in an authentication bypass when used together with path rewriting

Mitigation:Update to Apache Shiro 1.13.0+ or 2.0.0-alpha-4+, or ensure blockSemicolonis enabled (this is the default).

[1]

从漏洞描述可知,是路径穿越导致验证绕过。
path rewriting在spring中指类似“/path/{param}”的路由配置

漏洞条件

  • shiro < 1.13.0 or 2.0.0-alpha-4,

  • blockSemicolon == false

    这个选项其实默认为ture,所以说条件还是很苛刻的。

漏洞复现

环境配置

shiro : 1.12.0
spring-boot: 2.7.4

pom.xml在必要配置基础上要添加以下依赖,使得springboot走AntPathMatcher路径匹配策略

<!-- CVE-2023-22602补丁在里面-->
<dependency>
	<groupId>org.apache.shiro</groupId>
	<artifactId>shiro-spring-boot-starter</artifactId>
	<version>1.12.0</version>
</dependency>

这个依赖与下面这个配置中ShiroRequestMappingConfig.class联合使用,可以让springBoot在获取查询路径时调用shiro的WebUtils.getPathWithinApplication,从而使得获取到的路径一致

shiro:

@Configuration
@Import({ShiroBeanConfiguration.class,
        ShiroWebConfiguration.class,
        ShiroRequestMappingConfig.class
        })
public class ShiroConfig{

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


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

        LinkedHashMap<String, String> map = new LinkedHashMap<>();
		
        //特例放前面
        map.put("/file/anonUser/**","anon");
        map.put("/file/**","authc");
        
        map.put("/login", "authc");

        bean.setFilterChainDefinitionMap(map);

        return bean;
    }

    private class MyShiroFilterFactoryBean extends ShiroFilterFactoryBean{
        @Override
        public AbstractShiroFilter createInstance() throws Exception{

            SecurityManager securityManager = getSecurityManager();


            FilterChainManager manager = createFilterChainManager();

			//设置BlockSemicolon
            InvalidRequestFilter filter = (InvalidRequestFilter) manager.getFilters().get("invalidRequest");
            filter.setBlockSemicolon(false);

            PathMatchingFilterChainResolver chainResolver = new PathMatchingFilterChainResolver();
            chainResolver.setFilterChainManager(manager);

            return new SpringShiroFilter((WebSecurityManager) securityManager, chainResolver);
        }

    }

    class SpringShiroFilter extends AbstractShiroFilter {
        public SpringShiroFilter(WebSecurityManager securityManager, FilterChainResolver filterChainResolver) {
            setSecurityManager(securityManager);
            setFilterChainResolver(filterChainResolver);
        }


    }

controller

@ResponseBody
    @GetMapping("/file/{user}/{fileName}")
    public String userFile(@PathVariable String user, @PathVariable String fileName) {
        if (!fileName.isEmpty()){
            return "get file from " + "/fileRoot/" + user + "/" + fileName;
        }

        return "no file";
    }

@GetMapping("/file")
public String fileRoot(){
    return "...."
}
   


测试

payload: /file/anonUser/..%3b

Shiro CVE-2023-46749 路径穿越导致验证绕过插图

漏洞分析

  • 获取请求路径

    PathMatchingFilterChainResolver.getChian(...):

    Shiro CVE-2023-46749 路径穿越导致验证绕过插图1

    public static String getPathWithinApplication(HttpServletRequest request) {
            return normalize(removeSemicolon(getServletPath(request) + getPathInfo(request)));
        }
    

    Shiro CVE-2023-46749 路径穿越导致验证绕过插图2

    去除分号还是和以前一样,去除分号后面全部内容,包括分号

    Shiro CVE-2023-46749 路径穿越导致验证绕过插图3

    normalize()中能规范化“/../"但是不能规范化尾部的”/.."

  • 匹配过滤链

    根据我们的配置,我们必然是匹配到 map.put("/file/anonUser/**","anon");所以不用任何验证,但是由于InvalidRequestFilter作为
    “Global Filter”会被添加到每个过滤链中,所以还要经过它:

    //核心算法
    //InvalidRequestFilter::
    @Override
        protected boolean isAccessAllowed(ServletRequest req, ServletResponse response, Object mappedValue) throws Exception {
            HttpServletRequest request = WebUtils.toHttp(req);
            // check the original and decoded values
            return isValid(request.getRequestURI())      // user request string (not decoded)
                    && isValid(request.getServletPath()) // decoded servlet part
                    && isValid(request.getPathInfo());   // decoded path info (may be null)
        }
    
        private boolean isValid(String uri) {
            return !StringUtils.hasText(uri)
                   || ( !containsSemicolon(uri)
                     && !containsBackslash(uri)
                     && !containsNonAsciiCharacters(uri))
                     && !containsTraversal(uri);
        }
    

    从以上我们的配置已知,blockSemicolon == false,所以不用担心containsSemicolon(uri),我们的payload中没有反斜杠,也没有非Ascii字符,所以看containsTraversal(uri)

    //指的是“/”
    private static final List<String> FORWARDSLASH = Collections.unmodifiableList(Arrays.asList("%2f", "%2F"));
    //“.”
    private static final List<String> PERIOD = Collections.unmodifiableList(Arrays.asList("%2e", "%2E"));
    
    
    
    private boolean containsTraversal(String uri) {
            if (isBlockTraversal()) {
                return !(isNormalized(uri)
                        && PERIOD.stream().noneMatch(uri::contains)
                        && FORWARDSLASH.stream().noneMatch(uri::contains));
            }
            return false;
        }
    
    private boolean isNormalized(String path) {
            if (path == null) {
                return true;
            }
            for (int i = path.length(); i > 0;) {
                int slashIndex = path.lastIndexOf('/', i - 1);
                int gap = i - slashIndex;
                if (gap == 2 && path.charAt(slashIndex + 1) == '.') {
                    return false; // ".", "/./" or "/."
                }
                if (gap == 3 && path.charAt(slashIndex + 1) == '.' && path.charAt(slashIndex + 2) == '.') {
                    return false;
                }
                i = slashIndex;
            }
            return true;
        }
    

    因为PERIOD是编码后的点号,而payload中是明文,故不匹配,且payload中也没有"%2f",从payload("/file/anonUser/..%3b")可知,第一轮的gap== 6所以最终同样绕过。

    所以最终到达资源点,由响应可知我们实际访问的目录是“/fileRoot",这相当于发出一个url == ”/file"的请求才能访问,但是现在因为路径穿越导致未经验证就访问到了,符合漏洞描述

    “/file"需要验证:

    Shiro CVE-2023-46749 路径穿越导致验证绕过插图4

补充

request.getServletPath()的数据是怎么来的?

定位到路径处理函数:org.apache.catalina.connector.CoyoteAdapter.postParseRequest(...)

只看关键代码:

Shiro CVE-2023-46749 路径穿越导致验证绕过插图5

  1. 第一个标红断点处,req.decodedURI(),获取的是从二进制数据解码后的明文数据,这时还没有url解码

  2. 到了标蓝处,这里是解析路径参数(path param,比如http://example.com/users/42;action=edit;view=summary)的,也就是在这里处理分号的,但如果对分号进行url编码,则可以绕过;

    protected void parsePathParameters(org.apache.coyote.Request req,
                Request request) {
    
            // Process in bytes (this is default format so this is normally a NO-OP
            req.decodedURI().toBytes();
    
            ByteChunk uriBC = req.decodedURI().getByteChunk();
        	//如果用%3b代替“;”则semicolon == -1
            int semicolon = uriBC.indexOf(';', 0);
            // Performance optimisation. Return as soon as it is known there are no
            // path parameters;
            if (semicolon == -1) {
                return;
            }
    
            // What encoding to use? Some platforms, eg z/os, use a default
            // encoding that doesn't give the expected result so be explicit
            Charset charset = connector.getURICharset();
            ....
    
            while (semicolon > -1) {
                // Parse path param, and extract it from the decoded request URI
                int start = uriBC.getStart();
                int end = uriBC.getEnd();
    
                int pathParamStart = semicolon + 1;
                int pathParamEnd = ByteChunk.findBytes(uriBC.getBuffer(),
                        start + pathParamStart, end,
                        new byte[] {';', '/'});
    
                String pv = null;
    
                if (pathParamEnd >= 0) {
                    if (charset != null) {
                        pv = new String(uriBC.getBuffer(), start + pathParamStart,
                                    pathParamEnd - pathParamStart, charset);
                    }
                    // Extract path param from decoded request URI
                    byte[] buf = uriBC.getBuffer();
                    for (int i = 0; i < end - start - pathParamEnd; i++) {
                        buf[start + semicolon + i]
                            = buf[start + i + pathParamEnd];
                    }
                    uriBC.setBytes(buf, start,
                            end - start - pathParamEnd + semicolon);
                } else {
                    if (charset != null) {
                        pv = new String(uriBC.getBuffer(), start + pathParamStart,
                                    (end - start) - pathParamStart, charset);
                    }
                    uriBC.setEnd(start + semicolon);
                }
    
                ....
    
                if (pv != null) {
                    int equals = pv.indexOf('=');
                    if (equals > -1) {
                        String name = pv.substring(0, equals);
                        String value = pv.substring(equals + 1);
                        request.addPathParameter(name, value);
                        .....
                    }
                }
    
                semicolon = uriBC.indexOf(';', semicolon);
            }
        }
    
  3. 到了try语句块,由注释可知,这里才开始url解码

  4. 到了Normalization,就是对路径进行规范化:以下是方法注释,源码太长就不展开了

    This method normalizes "\", "//", "/./" and "/../"
    

因此当路径带有 "/..%3b" 时,由于分号被url编码,所以绕过对分号的处理,然后解码后,变成”/..;"但是到规范化(Normalization)时”/..;"并不匹配上述注释中的任何一个,因此最终保留下来

漏洞修复

The InvalidRequestFilter is more flexible · apache/shiro@3b80f5c[2]

Reference


[1] Security Reports | Apache Shiro

[2] The InvalidRequestFilter is more flexible · apache/shiro@3b80f5c


4A评测 - 免责申明

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

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

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

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

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

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

相关文章

甲方安全何时需要自研安全产品?安全平台工程团队的价值与落地策略
Burpsuite基础使用教程
Kali linux hping3基础使用方法
DouCo DouPHP 安全漏洞(CVE-2024-57599)
9万个WordPress站点面临本地文件包含漏洞攻击
从中国视角看 NSA(方程式组织)的 TTP(攻击套路)

发布评论