Start trial

Supply Chain Attack on OptinMonster, TrustPulse, and PushEngage: Tampered CDN Scripts Auto-Creating Rogue Admins

Published June 15, 2026, by the Patchstack Team A supply chain attack against three popular WordPress marketing plugins (OptinMonster, TrustPulse, and PushEngag

Published June 15, 2026
Dave Jong avatar
Dave Jong
Security Research Lead at Patchstack

Published June 15, 2026, by the Patchstack Team

A supply chain attack against three popular WordPress marketing plugins (OptinMonster, TrustPulse, and PushEngage) served tampered JavaScript from their vendors’ CDNs to live websites. The injected code did not exploit a plugin vulnerability in the traditional sense. Instead, it ran inside the browser of any logged-in administrator who loaded an affected page, and used that administrator’s own session to silently create hidden admin accounts and install a self-hiding backdoor plugin.

Because the payload runs client-side with valid credentials and a valid nonce, every malicious request looks, at the network layer, almost exactly like a real administrator adding a user. That is what makes this campaign interesting, and what made it tricky to stop without breaking legitimate functionality.

Patchstack deployed a mitigation rule for this campaign, and over roughly 36 hours it blocked 271 exploitation attempts across customer sites. We will walk through how the attack works, what our firewall logs revealed, and exactly what you should do if you ran any of these plugins.

This research builds on the public disclosures from OptinMonster and Sansec, combined with our own analysis of a captured payload and our firewall logs.

About the vendor

OptinMonster, TrustPulse, and PushEngage are conversion and engagement tools published under the Awesome Motive umbrella, collectively installed on a very large number of WordPress sites.

According to Sansec, over 1.2 million sites were potentially exposed during this incident. These plugins load a small JavaScript SDK from a vendor-controlled CDN on the front end. This is the standard pattern for this category of product, and it is the exact mechanism the attacker abused.

What happened

Per OptinMonster’s disclosure, an attacker exploited a vulnerability in a third-party plugin (UpdraftPlus) running on OptinMonster’s marketing website to gain access to that server. From there they retrieved a CDN API key and used it to modify the JavaScript files served to customers from the CDN edge.

The tampered files were the plugins’ front-end SDKs:

  • a.omappapi.com/app/js/api.min.js (OptinMonster)
  • a.opmnstr.com/app/js/api.min.js (OptinMonster)
  • a.optnmstr.com/app/js/api.min.js (OptinMonster)
  • a.trstplse.com/app/js/api.min.js (TrustPulse)
  • clientcdn.pushengage.com/sdks/pushengage-web-sdk.js (PushEngage)

The malicious code was appended to the legitimate, minified SDK, so the file kept working normally while the attacker’s logic ran alongside it. No update was pushed to the plugins themselves. The change happened entirely at the CDN, which is why a fully up-to-date site could still be served the malicious script.

Timeline

  • April 28, 2026: The command-and-control domain tidio.cc is registered and issued a TLS certificate, indicating the campaign was prepared well in advance.
  • June 12, 22:17 UTC: Malware first observed in the OptinMonster and TrustPulse api.min.js files.
  • June 12, 22:42 UTC: Last verified presence of the malware on the OptinMonster and TrustPulse CDNs.
  • June 13, 19:02 UTC: The PushEngage SDK is still serving injected code from certain CDN edges.
  • June 14, 2026: PushEngage malware removed; OptinMonster publishes its incident disclosure.
  • June 14–15, 2026: Patchstack’s mitigation rule actively blocks auto-exploitation attempts across protected sites.

The exposure window for OptinMonster and TrustPulse was short, but the tampered PushEngage SDK lingered on some CDN edges into June 13–14 as caches were purged.

How the attack works

We analyzed a captured copy of the injected payload. It is a self-contained script bolted onto the end of the genuine SDK, and it executes a careful, staged routine designed to run only where it will succeed and stay quiet everywhere else.

1. Environment and victim checks. Before doing anything, the script tries to avoid researchers and automated tooling. It checks for navigator.webdriver, headless markers like window._phantom and window.__nightmare, and zero-size browser windows. It then confirms it is running in a genuine WordPress admin context by looking for the admin bar, /wp-admin/ paths, and the wordpress_logged_in_ cookie. If it is not in front of a logged-in administrator, it simply stops. It also stamps localStorage so it will not re-run on the same browser for 24 hours.

2. Locating the site and harvesting a nonce. It determines the WordPress root and admin path, fingerprints the WordPress version from the generator tag and asset query strings, and then harvests a valid REST nonce from inline wpApiSettings, from admin-ajax.php?action=rest-nonce, or by scraping the new-user page. With a valid nonce plus the admin’s own cookies, the script can make authenticated requests as the administrator.

3. Creating a rogue administrator in four ways. This is the core of the attack. The payload tries multiple methods in sequence until one succeeds:

  • REST API: POST /wp-json/wp/v2/users (and the ?rest_route=/wp/v2/users form) with a JSON body requesting the administrator role.
  • Admin form: POST /wp-admin/user-new.php with action=createuser.
  • AJAX: POST /wp-admin/admin-ajax.php with the same createuser payload.
  • Hidden iframe: a 1×1 invisible iframe that loads user-new.php, fills the form, and submits it.

It plants both a fixed account and randomized accounts:

// Fixed identity baked into one observed sample
USER:  "developer_api1",
EMAIL: "customer1usx@gmail.com",

// Randomized variant seen overwhelmingly in the wild
// dev_xxxxxx / dev_xxxxxx@gmail.com  (administrator role)

The script even carries a multi-language dictionary of “user already exists” messages so it can tell, across localized WordPress installs, whether the account was created or already present.

4. Installing a self-hiding backdoor. Once it has admin access, the payload fetches a generated ZIP from its C2 and uploads it via POST /wp-admin/update.php?action=upload-plugin. The backdoor masquerades under rotating random names such as “Content Delivery Helper” (content-delivery-helper) or “Database Optimizer” (database-optimizer), and actively conceals itself from the plugin list, user list, updates screen, and activity logs.

Per Sansec, it exposes a web shell (“WPM File Manager & Shell”) via the ?developer_api1_fm parameter that runs system($_POST['cmd']), plus a developer_api1_eval endpoint that executes base64-decoded attacker input.

5. Exfiltration. Results are XOR-encrypted with the key jX9kM2nP4qR6sT8v, base64-encoded, and beaconed to tidio.cc/cdn-cgi/pe-* using a resilient chain of navigator.sendBeacon, fetch, XMLHttpRequest, and finally an image-pixel fallback.

The key takeaway: this is not a CSRF or a plugin bug. The malicious requests carry the administrator’s legitimate session and a legitimate nonce, because the administrator’s own browser is the one making them. The administrator is the victim, not the attacker.

What our firewall logs show

Because the requests are authenticated and well-formed, the only reliable signal at the HTTP layer is the attacker’s own indicators: the specific account identities and the backdoor’s parameter names. We built the mitigation rule around those values, and the telemetry confirms it caught the campaign cleanly.

Between June 14 and June 15, 2026, the rule blocked 271 requests across 13 distinct sites originating from 81 unique IP addresses:

EndpointBlocked requestsVector
/wp-json/wp/v2/users263REST API account creation
/wp-admin/user-new.php5Admin form / iframe
/wp-admin/admin-ajax.php3AJAX account creation
Total271

Every blocked request was a rogue-admin creation attempt matching this campaign. 267 used the randomized dev_xxxxxx / dev_xxxxxx@gmail.com identities and 4 used the fixed developer_api1 / customer1usx@gmail.com account. The version distributed at scale clearly favored randomized names, while the fixed identity appeared mainly through the form-based path.

A representative blocked REST request:

POST /wp-json/wp/v2/users
{
  "username": "dev_3m6nyp",
  "email": "dev_wvi65g@gmail.com",
  "password": "lSwI4oUwH%&uCZ@7NW2*",
  "roles": ["administrator"]
}

Two details stand out in the data:

  • The traffic is residential, not centralized. The blocked requests came from 81 different IPs and a wide mix of consumer browsers: roughly 60% mobile (Android/Samsung), with the rest Windows, macOS, and desktop Linux, including a couple of Googlebot user agents. This is exactly what you would expect when the malicious code executes inside the browsers of real site administrators rather than from a single attacker server.
  • Sites were hit in rapid bursts. Individual sites show clusters of account-creation attempts seconds apart, as the script worked through its fallback chain and sprayed multiple randomized administrators in one visit. Two sites alone accounted for nearly 200 of the blocked attempts.

Patchstack’s mitigation

A web application firewall cannot stop a browser from loading a compromised CDN script, and it cannot see the encrypted beacon to tidio.cc (that traffic goes to a third-party origin). What it can do is block the on-site actions the payload performs, by matching the attacker’s hardcoded indicators precisely enough to avoid touching legitimate admin work.

Our rule blocks:

  • Rogue administrator creation and login: any request that creates or authenticates the fixed developer_api1 / customer1usx@gmail.com identity, or the randomized dev_xxxxxx / dev_xxxxxx@gmail.com pattern, across the REST, form, and AJAX vectors.
  • Backdoor web shell access: any request carrying the developer_api1_fm or developer_api1_eval parameters used by the planted plugin.

Because the rule keys on attacker-specific values rather than on the act of creating a user, the false-positive risk for sites running these plugins is effectively zero. No legitimate administrator creates a user named developer_api1 with the email customer1usx@gmail.com, or logs in as one.

This is a mitigation, not a cleanup. It stops the auto-exploitation from succeeding on a still-exposed site, but it does not remove a rogue admin or backdoor that was created before protection was in place.

Indicators of Compromise (IOC)

Rogue accounts

  • developer_api1 / customer1usx@gmail.com
  • dev_xxxxxx / dev_xxxxxx@gmail.com (randomized, six characters)

Backdoor plugins (rotating disguises)

  • content-delivery-helper: “Content Delivery Helper” (v2.7.1)
  • database-optimizer: “Database Optimizer” (v2.9.4)
  • UI string: “WPM File Manager & Shell”

Backdoor parameters

  • ?developer_api1_fm (web shell), developer_api1_eval (code execution)

Command and control

  • Domain: tidio.cc (IP 84.201.6.54, Ultahost AS214036)
  • Paths: /cdn-cgi/p, /cdn-cgi/b, /cdn-cgi/l, /cdn-cgi/pe-p, /cdn-cgi/pe-b, /cdn-cgi/pe-l

Malware signature

  • XOR key: jX9kM2nP4qR6sT8v

Tampered CDN files

  • a.omappapi.com/app/js/api.min.js, a.opmnstr.com/app/js/api.min.js, a.optnmstr.com/app/js/api.min.js, a.trstplse.com/app/js/api.min.js, clientcdn.pushengage.com/sdks/pushengage-web-sdk.js

What you should do

If you ran OptinMonster, TrustPulse, or PushEngage and an administrator visited the site during the exposure window (around June 12–14, 2026), treat the site as potentially compromised and check it manually. Remediating the vendors’ systems does not clean a site that was already attacked.

  1. Audit administrator accounts. Delete developer_api1 and any dev_xxxxxx accounts. Review all admin users for anything you do not recognize.
  2. Check the filesystem, not just the dashboard. The backdoor hides from the admin UI. Inspect wp-content/plugins/ directly for content-delivery-helper, database-optimizer, or any unfamiliar plugin, and grep your codebase for developer_api1_fm, developer_api1_eval, and the XOR key jX9kM2nP4qR6sT8v.
  3. Run a server-side malware scan for comprehensive detection beyond the known names.
  4. Rotate everything if you find a compromise: administrator passwords, API keys, database credentials, and the WordPress security keys/salts in wp-config.php.
  5. Block the C2 tidio.cc (84.201.6.54) at the DNS or network layer.

Conclusion

This incident is a clear reminder that the software supply chain extends past the plugins you install to every third-party asset they load at runtime. A site that was fully patched and well configured could still be served malicious code, because the compromise happened on a vendor’s CDN, not on the customer’s site. And because the payload weaponized the administrator’s own authenticated session, the resulting requests were nearly indistinguishable from legitimate activity.

The defensible signal in cases like this is the attacker’s own fingerprints. By keying on the specific rogue identities and backdoor parameters, Patchstack’s rule blocked 271 real exploitation attempts in its first day and a half without disrupting legitimate site management. If you depend on third-party scripts, and almost every WordPress site does, assume that “trusted” assets can turn hostile, and make sure something is watching the requests they generate.

Like it? Share it.

Related articles