1. 前言
1-1. 简介
TeamCity是一款由JetBrains开发的持续集成和持续交付(CI/CD)服务器。它提供了一个强大的平台,用于自动化构建、测试和部署软件项目。TeamCity支持多种编程语言和开发环境,并提供了丰富的功能和工具,帮助开发团队构建和交付高质量的软件。
本文简单分析了三个身份验证绕过的漏洞CVE-2024-23917、CVE-2024-27198、CVE-2024-27199。
1-2. 环境搭建
下载地址:https://blog.jetbrains.com/teamcity/2024/02/critical-security-issue-affecting-teamcity-on-premises-cve-2024-23917/
下载对应版本的 TeamCity,按引导进行安装。
创建管理员账号。
2. CVE-2024-23917
https://blog.jetbrains.com/teamcity/2024/02/critical-security-issue-affecting-teamcity-on-premises-cve-2024-23917/
漏洞描述
该漏洞会使未经身份验证的攻击者能够通过 HTTP(S) 访问 TeamCity 服务器来绕过身份验证检查并获得对该 TeamCity 服务器的管理控制。
影响版本
2023.11.2 及之前版本
2-1. 漏洞复现
构造 URL 为/app/rest/server;.jsp?jsp_precompile=1,能绕过身份验证,成功获取到目标内容。
2-2. 漏洞分析
jetbrains.buildServer.server.rest.APIController 中声明了/app/rest/路径下的”/builds//statusIcon“, “/builds/aggregated//statusIcon“, “/server/version”, “/version”, “/apiVersion”, “/swagger**”, “/server/nodes”这些不需要身份认证。
声明了 MVC 用到的拦截器,其中 AuthorizationInterceptorImpl 是进行身份校验的拦截器。
进入 RequestInterceptors 拦截器,会进行一个 if 判断,若判断为 false 则会进入 else 遍历它下面的其他拦截器,进而调用到 AuthorizationInterceptorImpl。
若要 if 判断为真,则this.requestPreHandlingAllowed(request)需返回 false。
看一下 requestPreHandlingAllowed() 方法,if 中调用了 WebUtil.isJspPrecompilationRequest() ,返回结果是由 URI 决定的。从请求中获取 URI ,若是以.jsp或.jspf结尾的,并且传入的jsp_precompile参数不为 null,就返回 true,进而 requestPreHandlingAllowed() 返回 false。
或者进入 else 分支,满足请求路径与this.myPreHandlingDisabled中的相匹配,也会返回 false,不过这里访问路径有限制,不如 if 条件的选择自由。
requestPreHandlingAllowed() 返回 false,不用遍历子拦截器,也就避免了进行身份验证。
TeamCity 提供 REST API,用于集成外部应用程序并创建与 TeamCity 服务器的脚本交互。它允许通过 URL 路径访问资源(实体)。
访问/app/rest/swagger.json可以获取到端点列表与格式,以及功能描述。
TeamCity REST API中有较为详细的介绍,/app/rest/users用来进行用户相关的操作,这是需要进行身份验证才能访问的API,创建一个用户实体,需要的参数如下:
构造请求包,满足 URI 以.jsp或.jspf结尾,并且传入jsp_precompile参数不为 null 两个条件,创建一个用户名为user1,密码为user1user1,角色权限为SYSTEM_ADMIN的用户。
{
"username":"user1",
"password":"user1user1",
"email": "[email protected]",
"roles": {"role": [{"roleId": "SYSTEM_ADMIN", "scope": "g"}]}
}
开启调试,在 RequestInterceptors.preHandle() 断点开始,
跟进 requestPreHandlingAllowed() -> WebUtil.isJspPrecompilationRequest(),此时获取到的 URI 是/app/rest/user;.jsp,jsp_precompile参数为1,满足条件,返回 true。
于是,结束 RequestInterceptors 拦截器的检查,其他拦截器不会进行拦截。
后续在路由处理中,由于 Tomcat的特性,会忽略掉;.jsp,解析到/app/rest/users对应的功能代码进行创建用户。
简单看一下 AuthorizationInterceptorImpl 的认证过程吧,进入该拦截器后,会判断当前是否有用户登录或者是否有 RememberMe 记录;
没有登录则调用isAuthenticationRequired()判断当前访问路径是否是this.myAuthorizationPaths中不需要身份验证的路径,不是就返回 false,后面进行拦截处理。
补丁下载:https://download-cdn.jetbrains.com/teamcity/plugins/internal/fix_CVE_2024_23917.zip
在 2023.11.3 版本中,this.interceptorList中增加了一个 Proxy 拦截器,会对当前请求进行拦截,导致请求失败。
3. CVE-2024-27198
官方公告
2023.11.3及之前版本
3-1. 漏洞复现
直接访问/app/rest/server会跳转到登录页面;
构造 URL 为/hax?jsp=/app/rest/server;.jsp,即可绕过身份验证访问到目标内容。
3-2. 漏洞分析
PageNotFoundController 是继承 BaseController 的,返回到 BaseController.handleRequestInternal(),调用 updateViewIfRequestHasJspParameter() 方法;
满足了 if 中的三个条件,就会重新将视图设置为 jsp 参数的值。
整理一下走到这一步需要的条件:
•请求时传入 jsp 参数,参数值以 .jsp 结尾,且不含admin/字符串
此时视图名称变为了/app/rest/server;.jsp,jsp 参数是可控的,可以通过这种方法来访问任意需要身份验证的路径。
处理器执行完后再次进入拦截器校验,但是 AuthorizationInterceptorImpl 并没有重写 postHandle() 方法,所以不会进行拦截。
后续会像 CVE-2024-23197 一样的过程解析/app/rest/server;.jsp,在 RequestInterceptors 中,这里没有传入jsp_precompile参数,所以if (!this.requestPreHandlingAllowed(var1))为 false,进入后面的代码;
或者同样在 URL 后面拼接传入&?jsp_precompile=1,这里就能直接返回 true 了。
继续看下面,此时__tc_requestStack值为2,所以不会再遍历this.myInterceptors,略过了 AuthorizationInterceptorImpl 的校验,RequestInterceptors.preHandle() 方法返回 true。
关于新增的 Proxy 拦截器,不知道其具体内容,这里也不会拦截。于是通过所有拦截器校验,返回 true。
后续 Tomcat 会自动忽略掉;.jsp,就会解析返回/app/rest/server对应的内容。
4. CVE-2024-27199
2023.11.3及之前版本
4-1. 漏洞复现
/admin/diagnostic.jsp是一个需要身份验证才能访问的路径,直接请求会跳转到登录页面;
构造 URL 为/update/../admin/diagnostic.jsp,即可成功访问。
能利用的不需要身份验证的路径有:
•/.well-known/acme-challenge/
能通过拼接绕过身份验证进行访问的路口有:
•/app/oauth/space/createBuild.html
4-2. 漏洞分析
在 RequestInterceptors 拦截器中,和之前的逻辑一样,判断请求路径是否与this.myPreHandlingDisabled中的路径相匹配;
在遍历this.myMatchingPaths时,判断请求路径是否与已定义的路径相符,匹配到了其中的/update/**,返回 true,这些路径都是不需要身份验证的路径。
于是 requestPreHandlingAllowed() 返回 false,不遍历 RequestInterceptors 的子拦截器,即不用进行身份验证。
在后续处理中,根据路径穿越符号,/update/../admin/diagnostic.jsp会处理为/admin/diagnostic.jsp,解析到视图/admin/diagnostic.html,输出对应内容。
同理,this.myMatchingPaths中的其他含有通配符的路径也可以用于绕过身份验证。
•/update/tools/content/../../../admin/diagnostic.jsp
/app/agents/**不可用是因为获取到的处理器不是 NodeDiagnosticsController,视图返回的就不是/admin/diagnostic.jsp。
还有一处不需要身份验证的路径列表,就是this.myPathsNotRequiringAuthentication,当进入 AuthorizationInterceptorImpl 时会进行遍历匹配;
当请求/res/../admin/diagnostic.jsp时,会匹配到/res/**,从而顺利通过拦截器校验。
同样,this.myPathsNotRequiringAuthentication其他含有通配符,且处理器为 NodeDiagnosticsController 的路径也能进行利用。
•/.well-known/acme-challenge/../../admin/diagnostic.jsp
补丁下载:
•TeamCity 2018.2 及更高版本
•TeamCity 2018.1 及更早版本
更多利用方式可参考以下链接:
https://www.rapid7.com/blog/post/2024/03/04/etr-cve-2024-27198-and-cve-2024-27199-jetbrains-teamcity-multiple-authentication-bypass-vulnerabilities-fixed/
https://forum.butian.net/share/2801
原文始发于微信公众号(中孚安全技术研究):TeamCity身份验证绕过漏洞分析(CVE-2024-23917、CVE-2024-27198、CVE-2024-27199)