浅谈Apache shiro 权限绕过漏洞(CVE-2023-34478)

Apache Shiro之前披露了CVE-2023-34478,对于类似目录穿越的请求,在非springframework特定的场景下可能存在权限绕过的可能。

0x00 前言

Apache Shiro之前披露了CVE-2023-22602,主要是由于 1.11.0 及之前版本的 Shiro 只兼容 Spring 的ant-style路径匹配模式(pattern matching),而高版本的 Spring使用的是PathPatternParser,当使用不同的路径匹配模式时,攻击者在特定的场景下访问可绕过 Shiro 的身份验证。

在修复该漏洞时,主要是通过Spring动态的读取文件留下的扩展接口来将路径匹配模式强制修改为了AntPathMatcher:

image.png

确实这样的话保证了两者请求解析模式的一致性,避免了解析差异导致的安全问题,但是众所周知,Shiro 是一个功能强大且易于使用的 Java 安全框架,可以提供提供身份验证的功能。那么有没有可能某个框架与Shiro间同样存在类似的解析差异问题导致身份认证绕过呢?

image.png

0x01 漏洞描述

大致的内容是对于类似目录穿越的请求,在非springframework特定的场景下可能存在权限绕过的可能。

image.png

0x02 原理分析

2.1 shiro请求解析方式

Shiro中对于URL的获取及匹配在org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver#getChain方法,其会根据URL路径匹配,解析出ServletRequest请求过程中要执行的过滤器链。其中主要是通过getPathWithinApplication方法获取应用程序内的URI的相对路径:

image.png

具体方法的实现如下,首先通过request.getServletPath()+request.getPathInfo()方法获取URI,然后再调用removeSemicolon和normalize方法处理:

image.png

在normalize方法中,会处理路径穿越符进行处理:

image.png

获取到对应的请求路径后,最终会根据URL进行路径匹配,解析出ServletRequest请求过程中要执行的过滤器链。详细的过程可以参考https://forum.butian.net/share/2231

2.2 Jersey请求解析方式

那么Jersey又是如何对请求进行处理的呢?

这里直接看Jersey是如何根据请求路径找到对应的资源的。ApplicationHandler是Jersey的核心组件之一,其handle方法主要是调用this.runtime.process(request)方法来处理HTTP请求:

image.png

process()方法会根据ContainerRequest对象的内容和Jersey应用程序的配置信息,定位到适合处理该请求的资源类或方法,并调用其对应的处理函数来处理客户端请求。

在这个方法中,首先设置了基础 URI,然后使用 Stages.process() 方法对请求进行处理,并获取了端点引用。如果无法找到端点,则抛出 NotFoundException 异常:

image.png

在Stages.process方法中实际上主要是遍历rootStage(其中存储了解析请求的阶段相关的信息,如请求 URI、请求方法、请求头等),调用其apply方法进行处理:

image.png

跟请求路径相关的主要是org.glassfish.jersey.server.internal.routing.RoutingStage,用于解析请求 URI 并将其映射到相应的资源方法或类上。查看其apply方法的具体实现,主要是调用 _apply() 方法来查找路由匹配结果,其会返回一个 RoutingStage.RoutingResult 对象,包含找到的端点引用以及处理后的请求上下文信息:

image.png

_apply() 方法中,使用传入的 requestrouter 参数调用 router.apply(request) 方法,返回一个 org.glassfish.jersey.server.internal.routing.Router.Continuation 对象,其中包含了匹配成功的子路由和处理后的请求上下文对象。然后,使用 continuation.next().iterator() 方法获取所有匹配成功的子路由,并迭代遍历它们。对于每个子路由,递归调用 _apply() 方法以查找匹配的端点引用。

如果找到了匹配的端点引用,则返回一个 RoutingStage.RoutingResult 对象,其中包含了找到的端点引用和处理后的请求上下文对象。否则,继续查找下一个子路由,直到找到匹配的端点引用为止:

image.png

首先,通过调用org.glassfish.jersey.server.internal.routing.MatchResultInitializerRouter#apply将请求对象和根路由器对象打包成一个 Continuation 对象,并返回给调用方。通过这个 Continuation 对象,Jersey 可以获取与请求相关的所有信息,并继续处理后续的请求逻辑:

image.png

在Jersey中,会使用 RoutingContext 对象执行路由匹配操作,是一个十分重要的属性,具体看看这里pushMatchResult方法的调用,这里主要是调用org.glassfish.jersey.server.ContainerRequest#getPath对请求的上下文进行处理,如果decode属性为ture(默认为false),会进行一系列的解码处理:

image.png

否则会调用encodedRelativePath方法获取当前请求的编码后的相对路径:

image.png

处理结束后会创建SingleMatchResult实例:

image.png

这里会调用stripMatrixParams方法进行额外的处理,这里主要是剔除URI 中的矩阵参数,将 URI 按照斜杠字符进行分割,并将每个分段中的第一个分号字符及其后面的所有内容全部删除。最后,它将所有分段重新拼合起来,并返回处理后的结果:

image.png

然后返回对应的结果并push到matchResults列表中:

image.png

可以看到Jersey在整个解析过程中并没有对路径穿越符../进行额外的处理。有点类似Spring高版本PathPattern。

0x03 漏洞复现

根据前面的分析,Jersey跟Shiro在解析过程中是存在差异的,利用这一点在某种特定情况下会造成身份验证绕过的风险。

以shiro为例,对应的身份认证如下,/api目录下的所有接口都需要经过安全认证才能访问:

@Bean
ShiroFilterFactoryBean shiroFilterFactoryBean(){
    ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
    bean.setSecurityManager(securityManager());
    bean.setLoginUrl("/login");
    bean.setSuccessUrl("/index");
    bean.setUnauthorizedUrl("/unauthorizedurl");
    Map map = new LinkedHashMap<>();
    map.put("/doLogin/", "anon");
    map.put("/api/**", "authc");
    bean.setFilterChainDefinitionMap(map);
    return  bean;
}

假设对应的请求资源Resource Class如下:

@GET
@Path("/{path : .*}")
public Response getUser(@PathParam("path") @Encoded String path) throws IOException {
    return Response.ok().entity(path).build();
}

正常情况下,在缺少安全认证的情况下访问/api/page,会返回302状态码重定向到login页面:

image.png

结合前面分析的,shiro会解析..而Jersey不会的差异,因为可以这里路由匹配的正则表达式为.*表示匹配任意字符,那么发送如下请求达到绕过权限控制的效果:

image.png

0x04 修复方式

漏洞的修复主要是通过InvalidRequestFilter过滤器进行处理。

可以看到在过滤器的isValid中增加了一个containsTraversal方法:

image.png

在该方法中对类似./的字符进行了检测,同时也考虑了url编码绕过的问题:
image.png

image.png

0x05 其他

作为Shiro的“同僚”SpringSecurity,其也是一个功能强大且高度可定制的身份验证和访问控制框架。针对类似的安全问题,在Spring Security中提供了一个HttpFirewall接口,用于处理掉一些非法请求。目前一共有两个实现类:

  • StrictHttpFirewall(严格模式)
  • DefaultHttpFirewall

Spring Security缺省使用的是StrictHttpFirewall,其会拦截和处理的一些内容:

拦截内容
分号(;或者%3b或者%3B)
斜杠(%2f或者%2F)
反斜杠(\或者%5c或者%5B)
%25(URL编码了的百分号%)
英文句号.(%2e或者%2E)

虽然没办法兼顾各个框架间的解析差异,但是确实对一些不合法的http请求path进行拦截能在一定程度上规避掉风险。

  • 发表于 2023-08-07 09:00:01
  • 阅读 ( 10873 )
  • 分类:漏洞分析

0 条评论

请先 登录 后评论
tkswifty
tkswifty

64 篇文章