Back to Blog
Insights

CVE-2026-18391: Unauthenticated RCE in WooCommerce Subscriptions

CVE-2026-18391: Unauthenticated RCE in WooCommerce Subscriptions

WooCommerce published a security release for WooCommerce Subscriptions on 5 August 2026 with unusually plain language: the most serious issue could allow "an unauthorized user to assume control of a site."

CVE-2026-18391 is a CVSS 9.8 unauthenticated PHP object injection leading to remote code execution in WooCommerce Subscriptions versions below 9.1.0, exploitable on stores running High-Performance Order Storage. If you sell recurring subscriptions on WooCommerce, this one is holding your customers' card-adjacent data, and it deserves an hour of your day.

What it is

FieldValue
CVECVE-2026-18391
CVSS9.8 Critical
ClassPHP object injection (CWE-502 / CWE-94 depending on source) → remote code execution
AuthenticationNone
PreconditionHigh-Performance Order Storage (HPOS) enabled
AffectedWooCommerce Subscriptions < 9.1.0
Fixed in9.1.0, released 5 August 2026
DiscoveryInternal security review by WooCommerce
Published12 August 2026
RecordNVD · WPScan

The HPOS precondition matters

High-Performance Order Storage is WooCommerce's newer way of storing orders. Instead of packing orders into the generic wp_posts and wp_postmeta tables that WordPress uses for everything, HPOS gives orders dedicated tables. It is faster, it is the recommended configuration for stores at any scale, and for new installations it has been the default.

So the precondition is not obscure. If your store is recent, or you followed WooCommerce's own performance advice, you very probably have HPOS on. You can check under WooCommerce → Settings → Advanced → Features.

There is a bitter irony worth naming: the sites most exposed here are the ones that took the platform's performance guidance. "We're on an older configuration" is, for once, the safer position.

How the bug works

The plugin passes user-supplied input to PHP's unserialize() without validating it first, on the HPOS code path.

unserialize() is not a parser for data. It is a constructor for objects of whatever class the input names. Rebuilding an object can execute code, because PHP fires magic methods automatically: __wakeup() when the object is reconstructed, __destruct() when it is disposed of, __toString() when it is used as a string. None of that requires the application to do anything meaningful with the object.

From there the attacker needs a gadget chain: classes already loaded in the process whose magic methods, given attacker-chosen properties, can be strung together into something useful. In this case the chain runs through gadgets in the plugin's bundled dependencies (third-party libraries shipped alongside the plugin).

unauthenticated request  ──►  serialized payload
        │
        ▼
unserialize()  ── no validation of the input
        │
        ▼
object of an attacker-named class is constructed
        │
        ▼
__wakeup() / __destruct() fires
        │
        ▼
gadget chain through bundled dependencies
        │
        ▼
remote code execution

The dependency detail is the part with the widest lesson. The vulnerable code and the exploit's building blocks were in different codebases. WooCommerce Subscriptions supplied the unsafe unserialize(); the machinery that turns it into code execution came from libraries the plugin bundles. Auditing the plugin's own source and finding no dangerous function calls would not have found this.

Why this one is worse than a brochure-site bug

The compromise is the same technically. The consequences are not.

A subscriptions store holds a recurring-billing relationship with every customer: names, addresses, emails, order history, and payment tokens or gateway references that authorise future charges. Code execution on that host means:

  • Customer personal data at rest in the orders tables
  • Payment tokens or gateway credentials, depending on your gateway configuration
  • The ability to modify checkout to skim card details going forward, which is the classic Magecart pattern and can run undetected for months
  • Under PCI DSS, a compromise of the environment handling cardholder data, with the notification and forensic obligations that follow
  • Under PDPL, GDPR and equivalents, a personal-data breach with statutory notification clocks

WooCommerce stated they had "no evidence that any store was compromised or that any customer data was accessed." That is a meaningful and welcome statement about their visibility. It is not a statement about your store, and it is not a substitute for checking your own logs. Whether an incident is notifiable is a question about your evidence, not the vendor's, which is exactly the kind of determination our governance and compliance work exists to make defensible.

What to do today

  1. Update WooCommerce Subscriptions to 9.1.0 or later. WooCommerce's own advice is to verify the version manually even if automatic updates are enabled. Do not assume.
  2. Check whether HPOS is on (WooCommerce → Settings → Advanced → Features). If it is, treat any pre-9.1.0 exposure window as a period requiring investigation, not just patching.
  3. Audit for the standard post-compromise markers before you close this out: administrator accounts you did not create, modified files, unexpected scheduled tasks, and outbound connections from the web host.
  4. Rotate secrets if you find anything at all: database credentials, payment gateway API keys, administrator and application passwords.

How to detect exploitation attempts

Serialized PHP has an unmistakable shape. Look for object markers in request data hitting your store:

grep -aE 'O:[0-9]+:"' access.log | grep -iE 'wc-ajax|admin-ajax|subscriptions'

Then look for what a successful chain usually leaves behind: a file:

find wp-content -type f -name '*.php' -newermt '2026-07-01' \
  -not -path '*/plugins/*' -not -path '*/themes/*' -printf '%TY-%Tm-%Td %p\n' | sort

And check the checkout path specifically for injected JavaScript, because that is where the money is. Compare your active theme's checkout templates and any custom scripts against a known-good copy.

The wider point about bundled dependencies

This bug's exploitability came from libraries the plugin ships. Most WordPress plugin audits stop at the plugin's own code, and this is precisely why that is not enough. Your effective attack surface is every class loaded into the PHP process, from core, from every active plugin, and from every vendored dependency inside them.

That is not a WordPress-specific problem; it is the standard software supply-chain question wearing a different hat. Knowing what your store actually loads, and having a way to be told when any of it becomes dangerous, is the work. If you would like that established properly (or a penetration test of the store before someone else performs one), talk to us.

Get Started

Want a security-first build?

Get a free security review. We'll look at where you stand today and tell you what to fix first, no strings attached.

Talk to an Expert
CVE-2026-18391: Unauthenticated RCE in WooCommerce Subscriptions - IKZERO