Skip to main content

Command Palette

Search for a command to run...

BdThemes: A WordPress Supply Chain Attack That Changed No Code At All

Updated
17 min readView as Markdown
BdThemes: A WordPress Supply Chain Attack That Changed No Code At All

Summary

Not a single source code file in the WordPress.org repository was modified. No plugin update was released. No file on disk changed.

Yet rogue administrator accounts were created on victim sites, a PHP web shell was installed, and two backdoor modules were planted in the mu-plugins directory.

Attackers obtained write access to BdThemes' own DigitalOcean Spaces bucket — where the vendor stores the JSON data feeding a promotional banner system rendered in the WordPress admin dashboard — and replaced the legitimate content with payloads exploiting an existing XSS flaw in Biggopti, an internal library shipped with the plugins.

Seven plugins are affected. Element Pack alone shows more than 100,000 active installations on WordPress.org, while BdThemes advertises a portfolio of over 350,000 active installs.

The point that matters most for defenders: the WordPress security industry has spent fifteen years building file integrity tooling — hash comparison, unfamiliar file detection, plugin version diffing. That entire layer is blind here, because there is nothing to compare.

Priority action: if your website runs any BdThemes plugin, treat it as compromised and immediately audit your user list for accounts with @wordpress.org email addresses or usernames beginning with bd_.


The Vulnerability: When CVSS 5.4 Becomes Full Site Takeover

BdThemes attack overview

Overview of the BdThemes supply chain campaign (source: Cyber Security News, based on Wordfence analysis).

Biggopti is an internal component bundled with BdThemes plugins. Its job is entirely mundane: pull promotional banners from the vendor's API server — specifically JSON files stored in a DigitalOcean Spaces bucket sitting behind Cloudflare — and render them in the WordPress admin dashboard.

The flaw sits in the JSON response parsing: Biggopti fails to safely escape the display_id value before inserting it into an HTML id attribute. Notably, a neighbouring field in the same response is sanitized correctly — only this one was missed.

Item Detail
Component Biggopti (internal, bundled with plugins)
Data source Sigmative API, JSON on a DigitalOcean Spaces bucket
Vulnerable field display_id — insufficient client-side escaping
Type Cross-site scripting (XSS)
CVSS (Wordfence) 5.4 — medium
First introduced 1 March 2026, in bdthemes-prime-slider-lite v4.1.9
Trigger mechanism onanimationstart handler, firing within 10 milliseconds of page load
Trigger scope Every wp-admin page load by any logged-in administrator

This deserves separate attention. 5.4 is a methodologically correct score, and read on its own it would sit near the bottom of most patch queues. But that score is calculated on an implicit assumption: that the data source — the vendor's own API — is trustworthy.

When that assumption collapses, the same flaw permits full site takeover. The CVSS scale has no field for "if upstream is compromised." That is a gap in the current scoring model, and it appears more often as modern software increasingly pulls dynamic data from external infrastructure.

Timeline: Three Victims, One Actor

What makes this more than an isolated incident is Wordfence's assessment that the C2 infrastructure used here is related to two other WordPress supply chain attacks in the preceding two months.

Date Event
1 Mar 2026 The display_id XSS flaw is introduced in Prime Slider v4.1.9, then propagated to other BdThemes plugins
14 Jun 2026 OptinMonster and TrustPulse disclose an incident: a CDN credential was compromised and a tampered JavaScript file served from their own CDN
23 Jun 2026 Earliest possible start of the BdThemes campaign, per Wordfence's analysis of available records
28 Jul 2026 Advanced Responsive Video Embedder (ARVE) 10.8.7 is backdoored; Wordfence's PRISM system detects it in under two hours; CVE-2026-18072, CVSS 9.8
7 Aug 2026 Wordfence observes attacks through its WAF; signatures and firewall rules shipped to Premium, Care, Response and paid CLI customers
7–8 Aug 2026 The WordPress Plugins team closes all seven BdThemes plugins pending a full review
8 Aug 2026 Both poisoned API endpoints serve clean JSON again; Wordfence publishes its analysis
11 Aug 2026 Broad press coverage; BdThemes has still issued no official statement

The Shared Pattern

Placed side by side, the pattern across all three incidents is what belongs in an internal briefing:

OptinMonster / TrustPulse ARVE BdThemes
Point of compromise CDN API credential Developer account commit access Cloud storage credentials
Repository source modified? No Yes (v10.8.7) No
Trigger condition Logged-in administrator A single HTTP request Logged-in administrator
Outcome Hidden admin account + concealed backdoor plugin Unauthenticated admin access Rogue admin + web shell + mu-plugins backdoors

Two of three never touched source code. All three attacked the delivery layer rather than the code layer: a CDN key, a developer account, cloud storage credentials. And in two of them the payload only activated for logged-in administrators — a choice that avoids detection by ordinary visitors and automated scanners entirely.

Wordfence assesses the campaign's end goal as establishing covert administrative persistence and remote code execution across WordPress environments at scale.


The Attack Chain

  1. Attackers obtain BdThemes' cloud credentials and gain write access to the vendor's DigitalOcean Spaces bucket.

  2. They replace legitimate JSON records with crafted payloads, planting malicious content in the display_id field.

  3. A logged-in WordPress administrator opens any wp-admin page on a site running a BdThemes plugin.

  4. Biggopti automatically fetches banner data from the API endpoint.

  5. The poisoned display_id is inserted unescaped into an HTML id attribute; an onanimationstart handler fires within 10 milliseconds.

  6. The main payload — a JavaScript file named w2.js, delivered via the api-data-all-records endpoint — begins executing inside the administrator's authenticated browser.

  7. w2.js contacts the C2 server at ia-cdn[.]com/fz/c, sending the victim site's origin to fetch targeting instructions. If the C2 returns a skip or done status, execution aborts.

  8. Using the active administrative session's REST API nonce, the script creates a rogue administrator account through the WordPress REST API.

  9. It downloads a fake plugin ZIP from the C2 — with an innocuous slug such as wp-smart-thumbnails — and installs it through WordPress's standard plugin upload form.

  10. That plugin contains a PHP web shell named emer-run.php.

  11. The web shell is invoked to install two persistence modules into the mu-plugins directory (Must-Use plugins, which are always active and cannot be disabled from the admin interface), with file timestamps backdated to September 2025.

  12. The first module is a magic-login backdoor: it grants unauthenticated administrative entry via the URL parameter ?_wplogin=<token>, targeting the site's longest-registered administrator account.

  13. The second is an anti-analysis stealth module: it hooks WordPress database queries to conceal the rogue accounts from the admin user list and excludes them from the displayed total user count.

  14. Results of the whole operation are exfiltrated back to the C2 via navigator.sendBeacon. Steps 7 and 11 warrant separate comment.

The skip/done mechanism in step 7 shows the attackers maintain a target list server-side and avoid re-running against already-processed sites. For incident responders this means not observing activity does not mean the site was never targeted — the C2 may simply have returned done.

The file backdating in step 11 is a small but effective anti-forensic touch. An administrator reviewing the mu-plugins directory by modification time would see files that appear to predate even the introduction of the vulnerability itself, and would most likely skip past them.

The Second Payload: Deterministic Credentials

An alternate payload named x.js, found hosted on the plugin developer's own infrastructure and served through the api-data-records endpoint, works differently.

Rather than creating accounts with random details, it generates administrative credentials mathematically derived from the victim website's hostname:

Username    bd_ + a 6-character base36 hash
Password    Bd@26! + hash + x
Email       an @wordpress.org address

Wordfence highlights the two-way consequence of this design, and both directions are worth remembering.

For the attacker: because the credentials are deterministic, they do not need to store a centralised list of compromised sites. Knowing the hostname is enough to recompute the login at any time. There is no victim database to be seized or leaked.

For defenders: incident responders can compute the exact username and password to hunt for on a suspected domain. That is a rare condition in incident response — you know precisely what you are looking for rather than guessing.

The choice of an @wordpress.org email address is also a calculated social detail: to an administrator glancing down a user list, the account reads as an official system account rather than an intruder.


Affected Plugins

Slug Plugin name Active installs
bdthemes-element-pack-lite Element Pack Addons for Elementor 100,000+
live-copy-paste Live Copy Paste for Elementor 6,000+
ultimate-store-kit Ultimate Store Kit – Addon For WooCommerce, EDD and Elementor 6,000+
bdthemes-prime-slider-lite Prime Slider Addons for Elementor Not published
pixel-gallery Pixel Gallery Addons for Elementor Not published
ultimate-post-kit Ultimate Post Kit Addons for Elementor Not published
smart-admin-assistant Smart Admin Assistant – Dashboard and Site Enhancements Not published

All seven were closed on the official repository by the WordPress Plugins team on 7 or 8 August 2026, with a notice that they are unavailable pending a "full review."


Indicators of Compromise

Compiled from the Wordfence Threat Intelligence advisory of 8 August 2026 and public reporting reproducing its technical detail. Domains are defanged.

C2 infrastructure and payloads

ia-cdn[.]com/fz/c            # C2 endpoint receiving site origin, returning targeting instructions
                             # note the naming, designed to pass for a legitimate CDN service
 
w2.js                        # main payload, served via the api-data-all-records endpoint
x.js                         # alternate payload, hosted on BdThemes' own infrastructure,
                             # served via the api-data-records endpoint
 
api-data-all-records         # poisoned API endpoint (Sigmative API)
api-data-records             # poisoned API endpoint (Sigmative API)

On-site artifacts

# Web shell
emer-run.php                 # PHP web shell, installed via the fake plugin ZIP
 
# Fake plugin slug (observed example)
wp-smart-thumbnails          # neutral name, installed via the standard plugin upload form
 
# mu-plugins directory
Two unfamiliar files with timestamps backdated to September 2025:
  - magic-login backdoor module
  - anti-analysis module hiding users from the admin list
 
# Backdoor URL parameter
?_wplogin=<token>            # unauthenticated administrative entry,
                             # targeting the longest-registered admin account
 
# WordPress database options
fz_emer_login_tokens
fz_emer_done_v1

Rogue administrator account pattern

Username    bd_<6-character base36 hash>
Password    Bd@26!<hash>x
Email       <anything>@wordpress.org
 
# Because the credentials are deterministic per hostname, responders can
# precompute the exact values to hunt for on a given domain

Behavioural indicators

- Administrator accounts appearing outside your provisioning process, especially with @wordpress.org emails
- Plugins installed that nobody in the organisation installed
- A discrepancy between the displayed total user count and the actual row count in the database
- Files in mu-plugins whose timestamps do not match the site's deployment history
- Outbound requests to ia-cdn[.]com from an administrator's browser

Related campaign activity (context)

CVE-2026-18072    Advanced Responsive Video Embedder 10.8.7
                  CVSS 9.8 — Authentication Bypass via hardcoded backdoor
                  File: php/fn-update-check.php, function _arve_uc_init()
                  Parameters: _wplogin, _wpm
                  Detected by Wordfence PRISM in under 2 hours (28 July 2026)
                  ~20,000 active installations
 
OptinMonster / TrustPulse   CDN incident, disclosed 14 June 2026
                  CDN credential compromised, tampered JavaScript served from the CDN
                  Activated only for logged-in administrators

MITRE ATT&CK Mapping

Tactic Technique ID Technique Name Observed in campaign
Initial Access T1195.002 Supply Chain Compromise: Software Supply Chain Poisoning the vendor's JSON data stream
Resource Development T1584 Compromise Infrastructure Write access obtained to BdThemes' own storage bucket
Resource Development T1583.001 Acquire Infrastructure: Domains ia-cdn[.]com registered as C2 infrastructure
Execution T1059.007 Command and Scripting Interpreter: JavaScript w2.js and x.js running in the administrator's browser
Defense Evasion T1036.005 Masquerading: Match Legitimate Name or Location wp-smart-thumbnails; C2 domain posing as a CDN service
Credential Access T1185 Browser Session Hijacking Abusing the active admin session's REST API nonce
Persistence T1136 Create Account Rogue administrator created via the REST API
Persistence T1505.003 Server Software Component: Web Shell emer-run.php
Persistence T1505 Server Software Component Two backdoor modules in mu-plugins
Persistence T1098 Account Manipulation Targeting and abusing the longest-registered admin account
Defense Evasion T1070.006 Indicator Removal: Timestomp mu-plugins files backdated to September 2025
Defense Evasion T1564 Hide Artifacts Hooking database queries to conceal rogue accounts from the user list
Defense Evasion T1027 Obfuscated Files or Information JavaScript payload embedded in a JSON response
Exfiltration T1041 Exfiltration Over C2 Channel Results sent to C2 via navigator.sendBeacon
Impact T1078 Valid Accounts Maintaining persistent administrative access

Assessment

The WordPress security industry built its entire defensive posture around one assumption: malicious code lives in files.

That was a reasonable assumption and held true for years. A backdoored plugin means changed files. A compromised site means unfamiliar files appear. So the tooling was built accordingly: compare plugin file hashes against the official repository, scan directories for unknown files, alert on anomalous updates. WordPress.org provides the known-good baseline, and the model works.

This campaign put its payload in exactly the place nothing scans: a JSON response. No file to hash. No diff to compare. No update to review. The plugin on disk is byte-for-byte identical to the official repository copy — and still executes the attacker's code every time an administrator opens the dashboard.

But there is a notable paradox here, and it favours defenders. Precisely because the technique is unusual, it leaves very specific and easily verified traces: one URL parameter name, two database option names, a computable username pattern. Hard to catch with automated scanners, but verifiable by hand in fifteen minutes if you know where to look. This is not the kind of incident that requires expensive tooling to investigate.

On the severity scoring gap. The XSS flaw scores 5.4 — medium. In most organisations' patch queue, that number sits below the priority threshold. But the score reflects danger assuming the data source is trustworthy. When the vendor itself is compromised, the same flaw enables full site takeover.

This is an increasingly common problem rather than an edge case. Modern software constantly pulls dynamic data from external infrastructure: promotional banners, update notices, remote configuration, telemetry. Every such feed is a channel your code trusts by default, and the current scoring model has nowhere to record that risk.

The awkward position site owners are in right now. The plugins are closed on WordPress.org, meaning there is no update path through the official channel. The vulnerability was unpatched when the report was published, and the vendor has issued no statement. That leaves site owners with an uncomfortable choice: keep the plugins and carry an unpatched vulnerability, or remove them and break much of the site's Elementor layout.

That the API endpoints have served clean data since 8 August is positive but not a fix — it depends on the attackers not regaining write access to the bucket, which is entirely outside your control.

Relevance for Vietnam

The WordPress plus Elementor combination is extremely common domestically. It is the default platform for small and medium business websites, online shops, landing pages and company profile sites in Vietnam — low cost, easy to find people who can build it, and no technical capability required to run it. Element Pack is among the most widely used Elementor addon suites in that ecosystem.

Two market characteristics make the risk worse here.

The first is the build-and-hand-over agency model. A great many business websites are built by an outside firm, handed over, and the contract ends. After that nobody tracks which plugins are running, at what version, or whether patches exist. The site keeps working normally for years — and that is exactly the condition this campaign needs.

The second is shared hosting. With the low-cost hosting packages common domestically, multiple websites frequently sit under one account or on one server. A site where an attacker gained administrative access and installed a web shell can become a stepping stone to the others in the same scope.

A very simple operational question to test yourself against: who in your organisation knows which plugins your company website runs, and at which versions? For most small and medium businesses we encounter, the answer is nobody — which means that even when this advisory reaches the right person, they still cannot answer "are we affected?"

If your organisation lacks a plugin inventory for the websites it operates, building one is worth doing before addressing this specific incident.


Recommendations

  • If a website runs any BdThemes plugin, treat it as compromised until proven otherwise — particularly sites where administrators logged into the dashboard regularly between 23 June and 8 August 2026.

  • Hunt four specific artifacts: accounts with @wordpress.org emails or usernames beginning bd_; unfamiliar files in mu-plugins (ignore timestamps, which are backdated); the emer-run.php web shell; and the fz_emer_login_tokens and fz_emer_done_v1 entries in the database options table.

  • Rotate all administrative credentials and invalidate active sessions — the administrator's authenticated session is what was abused to carry out the attack, so deleting the rogue accounts alone is not enough.

  • Compare the displayed user count in the interface against the actual row count in wp_users — the attacker's stealth module hides accounts from both, so a discrepancy between database and interface is a direct indicator.

  • Build and maintain a plugin inventory for every website you operate, with versions and an accountable owner — without it you cannot answer the most basic question when an advisory lands.

  • For dynamic external data feeds generally: review which components in your systems automatically fetch and render third-party content in a privileged context, and treat those feeds as untrusted input rather than a vouched-for source.


References

More from this blog

F

FPT IS Security

945 posts

Dedicated to providing insightful articles on cybersecurity threat intelligence, aimed at empowering individuals and organizations to navigate the digital landscape safely.