JetBrains TeamCity 鉴权绕过浅析

CVE-2023-42793

  • 影响版本:2023.05.3 and below
  • 补丁分析
    主要是针对 RequestInterceptors 这个类做的,在 myPreHandlingDisabled 字段通过反射添加了一个路由 /RPC2 接着在 myPreHandlingDisabled 中的 myMatchingPaths 字段中移除掉所有路径满足 /**/RPC2 。

分析代码看到这里会先执行一个 requestPreHandlingAllowed 如果返回 false 的话直接放行。

跟进会先判断访问的是否为 JSP 页面,之后这里会调用 myPreHandlingDisabled 字段去做路径匹配,我们注意到了一个 /**/RPC2

这个路径是在类初始化的时候创建并添加到 myPreHandlingDisabled 字段中去的


有了这个路径,那么我们访问以前以 /RPC 作为后缀的 url 均不会被鉴权。
而恰好 jetbrains.buildServer.server.rest.request.UserRequest#createToken(java.lang.String, java.lang.String, java.lang.String) 可以通过 path 变量来创建 token (这里的作用也就是可以创建指定用户的 token,用户指定通过 userLocator ,对于 admin 默认为 id:1),我们直接 name 位置设置为 RPC2 便可以绕过鉴权。

  • 复现

JetBrains TeamCity RCE - CVE-2023-42793 (projectdiscovery.io) 提供了一种 RCE 的方式,也就是利用获取到的 admin token 开启 debug API,触发配置重载过程中 RCE

之后去掉 Content-Type 字段之后就可以 RCE 了

CVE-2024-27198

teamcity 业务请求的分发处位于 jetbrains.buildServer.controllers.BaseController 首先看如果发送一个需要授权的请求curl --noproxy '*' -v "http://127.0.0.1:8111/profile.html"
jetbrains.buildServer.controllers.BaseController#handleRequestInternal

可以看到在这里会跳转至 /unauthorized.html 未授权页面,同时无法获取到模型视图参数。这是因为 web.xml 当作配置了 error-page ,当 401 时就会跳转到 /unauthorized.html


但如果我们修改为不存在的 uri:curl --noproxy '*' -v "http://127.0.0.1:8111/xxx"
jetbrains.buildServer.controllers.BaseController#handleRequestInternal 可以看到这里面是可以获取到模型视图参数的


原因在于不存在的路由导致 error-code 404 会分发至 PageNotFoundController 处理,并为模型视图设置视图名为 404.jsp

跟入 updateViewIfRequestHasJspParameter ,里面有个获取外部参数的操作 getJspFromRequest

跟进 jetbrains.buildServer.controllers.BaseController#getJspFromRequest 这里会取出 jsp 参数,如果 .jsp 结尾或者内容不包含admin/ 则不会返回 null,同时如果和默认视图名 404.jsp 不同的话则会将其设置为视图作为最终返回的内容。

payload 修改为:curl --noproxy '*' -ik "http://127.0.0.1:8111/xxxx?jsp=/app/rest/server;.jsp"
随后视图渲染 org.springframework.web.servlet.DispatcherServlet#resolveViewName 得到 JstlView 类型的 view

对于 jsp 文件的视图渲染,根据官方文档可知会作 dispatch 处理,也就是会将当前的 viewName 当作 url 请求进行处理

随后在执行获取视图路径的 RequestDispatcher 过程中,会对 path 作如下处理(位于 org.apache.catalina.core.ApplicationContext#getRequestDispatcher)其中 stripPathParams 方法会将 uri 中出现 ; 及其之后的部分去除,因此最后请求 path 变为了 /app/rest/server ,进而绕过鉴权


补充:为什么后面的 dispatch 请求就可以绕过鉴权?
我们之前知道鉴权处是在 jetbrains.buildServer.controllers.interceptors.RequestInterceptors#preHandle 方法当中,可以看到这里会根据当前应用请求获取一个 Stack 类,根据 size 以及请求 uri 的变化次数可以大概猜到这里应该表示的是请求处理栈。

拦截器只会在栈空间为 1 的时候起作用,但我们当时请求的是个不存在的 uri,所以自然始终是返回 true 的

总结

可能是 Java 版本过高的缘故,分析代码过程中部分核心位置反编译失败导致无法理清逻辑(后续值得研究一下反编译的问题)。不过目前针对 CVE-2024-27198 漏洞利用思路的猜想是:spring 支持当出现 error code 时通过配置 location (渲染视图位置)来响应错误页面,这实际上是做了二次 dispatch 请求. 而 teamcity 的鉴权操作并未对这种类似嵌套的请求作处理,同时视图名又可控,可覆盖默认的配置,导致可以以未授权的方式请求授权页面。(CVE-2024-27199 应该就是另外通过目录穿越的方式来控制第二次 dispatch 的位置)

  • 20
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值