Skip to content
BdThemes Plugin Supply Chain Compromise: What to Hunt For
Emma Richardson
Emma Richardson August 9, 2026 · 9 min read

BdThemes Plugin Supply Chain Compromise: What to Hunt For

A poisoned promotional JSON feed was enough to compromise WordPress administrator sessions without changing a single plugin file. Seven BdThemes plugins loaded the affected client-side code on every wp-admin request, so an administrator merely opening the dashboard could trigger the attack.

The affected plugins were closed in the WordPress plugin directory on August 7, 2026, while the WordPress Plugins team began its inspection. If any of these plugins ran on your site, treat the incident as a potential full-site compromise and investigate the database as well as the filesystem.

What happened: a remote API supply chain compromise

BdThemes plugins include Biggopti, an internal component that retrieves promotional banners and displays them in the WordPress admin dashboard. Its API uses static JSON stored in a DigitalOcean Spaces bucket behind Cloudflare, rather than a dynamic application server. An unauthorized party gained write access to that storage and replaced legitimate records with hostile JSON.

This was a supply chain compromise driven by remote data, not a malicious plugin update. No WordPress.org repository source files were altered, no plugin update was required, and the victim site’s files initially remained untouched. File-integrity scanners therefore had little to inspect, while a Web Application Firewall had limited visibility into the browser-side execution.

The issue is tracked as Biggopti Library (Various Versions) – Cross-Site Scripting via display_id from Sigmative API with CVSS 5.4 (Medium) and an Unpatched status. Insufficient output escaping allowed a party that controlled the Sigmative API to inject scripts that execute when an injected page is opened.

Why every administrator page load was exposed

Each plugin using Biggopti enqueues its JavaScript asset on admin_init. That makes the banner-fetching script run unconditionally on every wp-admin page load. It read display_id from the remote JSON and concatenated it into an HTML id attribute without escaping the value first.

// `display_id` is remote data and was not escaped.
var f = t.display_id || t.id || "default";
var m = "bdt-admin-biggopti-api-biggopti-" + f;
A = 'id="' + m + '"';

The same codebase did escape the data-display-id attribute. Its bundled escape helper converts ampersands, angle brackets, and double quotes, which shows that the vulnerable id construction followed a different and unsafe path.

function p(t) {
  return (t || "")
    .replace(/&/g, "&")
    .replace(/</g, "&lt;")
    .replace(/>/g, "&gt;")
    .replace(/"/g, "&quot;");
}

SVN history places the unsafe attribute injection in Prime Slider version 4.1.9 on March 1, 2026. Later releases added a DOMParser sanitizer for the banner content field to remove dangerous tags and inline event handlers, but they left the id attribute vulnerable.

The malicious display_id value escaped the id attribute and added an onanimationstart handler. A CSS animation ran that handler in about 10 milliseconds, silently executing code each time an authenticated administrator opened a dashboard page. At the time of analysis, the poisoned record used a Summer Sale notice and fetched an external JavaScript payload from the injected handler.

The compromise chain

The injected handler selected one of two payload paths according to the Biggopti endpoint in use. Both paths operated within the current administrator’s authenticated browser session, which gave the scripts access to WordPress nonces and same-origin administrative requests.

Primary payload: w2.js

Plugins using api-data-all-records received the primary w2.js payload. It first contacted ia-cdn[.]com/fz/c, sending the site’s origin for targeting instructions. The script stopped when the command-and-control (C2) server returned skip or done.

When instructed to proceed, the payload used the active session’s X-WP-Nonce to create a new WordPress administrator through the REST API, with ordinary form submissions as a fallback. It then downloaded a counterfeit plugin ZIP from the C2 server and installed it through the standard plugin upload form.

The fake plugin used the slug wp-smart-thumbnails or another neutral-looking slug. It contained the emer-run.php webshell, which the attacker invoked directly by URL. That webshell installed two persistence modules under the Must-Use (MU) plugins directory, where an MU plugin is automatically loaded by WordPress and cannot be disabled from the normal Plugins screen.

The first observed MU plugin was a magic-login backdoor. It permitted unauthenticated administrator access with ?_wplogin=<token> and targeted the site’s longest-registered administrator. The second was an anti-analysis module that hooked WordPress database queries to conceal rogue users from the admin user list and reduce displayed user totals. Both collected samples were backdated to September 2025 to resemble older filesystem content, and filenames can vary between infections.

Finally, the payload reported each execution-stage result to the C2 using navigator.sendBeacon.

Alternate payload: x.js

Victims whose plugins used api-data-records received x.js, hosted directly in BdThemes infrastructure. This script derived a deterministic administrator username and password from the victim site’s hostname: it calculated a signed 32-bit rolling hash, converted its absolute value to base36, and retained the first six characters.

function makeCreds() {
  var host = siteKey();
  var hash = 0;
  for (var i = 0; i < host.length; i++) {
    hash = ((hash << 5) - hash + host.charCodeAt(i)) | 0;
  }
  var n = Math.abs(hash).toString(36).slice(0, 6);
  return {
    user: 'bd_' + n,
    pass: 'Bd@26!' + n + 'x'
  };
}

The generated account used an @wordpress.org email address. Because the credentials were derived from the hostname, the attackers did not need a central inventory of compromised sites. For incident response, the same algorithm lets you calculate the expected account pattern for a suspected domain.

x.js submitted a same-origin request to /wp-admin/user-new.php, extracted the user-creation nonce, and posted a new administrator account. It sent the result, generated credentials, site origin, current page URL, and logout URL to the C2 using navigator.sendBeacon; if that failed, it attempted a fetch() POST with credentials: 'omit', mode: 'no-cors', and keepalive: true. It also set a localStorage completion flag where localStorage was available.

Affected plugins

The following plugins contain the affected Biggopti implementation:

  • Element Pack Addons for Elementor (bdthemes-element-pack-lite)
  • Prime Slider Addons for Elementor (bdthemes-prime-slider-lite)
  • Pixel Gallery Addons for Elementor (pixel-gallery)
  • Ultimate Post Kit Addons for Elementor (ultimate-post-kit)
  • Ultimate Store Kit — Addon For WooCommerce, EDD and Elementor (ultimate-store-kit)
  • Live Copy Paste for Elementor (live-copy-paste)
  • Smart Admin Assistant (smart-admin-assistant)

Indicators to hunt for

Start with administrator accounts and MU plugins, but don’t stop at visible WordPress screens. The stealth module can hide accounts and alter displayed totals, so inspect the database directly and examine wp-content/mu-plugins/ at the filesystem level.

  • Primary payload URL: ia-cdn[.]com/fz/w2.js
  • C2 beacon endpoint: ia-cdn[.]com/fz/c
  • Poisoned vendor API endpoint, now cleaned: api[.]sigmative[.]io/prod/store/api/biggopti/api-data-all-records
  • Poisoned vendor API endpoint, now cleaned: api[.]sigmative[.]io/prod/store/api/biggopti/api-data-records
  • Poisoned vendor payload URL, now cleaned: api[.]sigmative[.]io/prod/store/api/biggopti/x.js
  • Webshell: emer-run.php, MD5 1024732009983dd5e54b4cf5593f04d4
  • MU-plugin magic-login backdoor: class-wp-token-validate.php, MD5 7719cd98a35ffad2771f26d1ceab7d27
  • MU-plugin stealth module: class-wp-query-9d127ff3.php or a similar class-wp-query-*.php name, MD5 9aadc3e5c5242b273bd17c5bdc358845
  • MU-plugin Health Check module: wp-cache-optimizer.php, MD5 e450ae5bc4bfc0d960dded06a76bb8e9
  • Database option fz_emer_login_tokens, which stores magic-login tokens
  • Database option fz_emer_done_v1, a completed-compromise flag
  • Accounts using @wordpress.org or @developer.wordpress.org email domains
  • Usernames matching bd_ followed by six alphanumeric characters

Timeline of the exposed data path

  • 2026-03-01: Biggopti API JavaScript was added to Prime Slider 4.1.9 in SVN revision r3471891. display_id was inserted unescaped into an id attribute, and the API used api-data-records.
  • 2026-05-10: The endpoint changed to api-data-all-records. A DOMParser sanitizer was added for the content field, while the id attribute remained unescaped.
  • 2026-06-23: The poisoned campaign’s Summer Sale notice had this start_date. This is the earliest possible date for active XSS.
  • 2026-08-06 22:40 UTC: The poisoned api-data-records object was last modified.
  • 2026-08-07 09:11 UTC: The poisoned api-data-all-records object was last modified.
  • 2026-08-07: The attack was observed in the wild, and the affected plugins were closed in the WordPress directory pending review.
  • 2026-08-07 16:56 UTC: Investigation artifacts were captured.
  • 2026-08-08: Both API endpoints returned clean JSON.

Containment and recovery actions

Immediately audit user records, plugin directories, the MU-plugin directory, and the WordPress options table for every indicator above. Remove rogue accounts and malicious files only after preserving evidence and identifying persistence mechanisms; otherwise, a magic-login backdoor or hidden account can restore access. Reset administrator credentials, invalidate sessions, rotate relevant hosting and cloud-storage credentials, and review access logs for the listed endpoints and C2 infrastructure.

The C2 infrastructure overlaps with the actors associated with the recent Advanced Responsive Video Embedder and OptinMonster supply chain attacks. The presence of malicious JSON and x.js in the vendor bucket points to compromise of BdThemes cloud-storage credentials or internal infrastructure, rather than a defect limited to an individual WordPress installation.

Wordfence Premium, Care, Response, and paid Wordfence CLI customers received malware signatures and Web Application Firewall rules for this compromise on August 7, 2026. Free Wordfence and Wordfence CLI users receive those signatures and rules after the standard 30-day delay. Wordfence’s Threat Intelligence database contains more than 4.4 million unique malicious samples, and its premium signature set detects more than 99% of those samples and indicators of compromise; Wordfence CLI can scan a server even when WordPress is not functioning.

Remote data is executable input when your admin-side JavaScript inserts it into the DOM. For plugin authors, escape every remote value in its final output context and do not treat a sanitizer for one field as protection for another. For site owners, complete the database and filesystem hunt now, because this campaign was designed to leave persistent access after the poisoned API response disappeared.

Emma Richardson

Emma Richardson

UI/UX designer and frontend developer. React and the modern JavaScript ecosystem are my expertise. Passionate about user experience and accessibility.

All posts

Join the HelloWP community!

Chat with us about WordPress, web development and share experiences with other developers.

- members
- online
Join

We use cookies to improve your experience. By continuing, you agree to our Cookie Policy.