Přeskočit na obsah
BdThemes: kompromitované API spouštělo XSS v administraci WordPressu
Jakub Dvořák
Jakub Dvořák 9. August 2026 · 10 min čtení

BdThemes: kompromitované API spouštělo XSS v administraci WordPressu

Útočníci kompromitovali ekosystém pluginů BdThemes, aniž by změnili jediný soubor v oficiálním repozitáři WordPressu. Stačilo jim upravit vzdálený JSON feed s propagačními bannery, který pluginy načítaly do každé stránky administrace, a zneužít neescapovaný atribut HTML.

Důsledek je kritický: škodlivý JavaScript běží v prohlížeči přihlášeného administrátora. Může vytvořit cizí účet správce, nahrát plugin s webshellu, nainstalovat perzistentní zadní vrátka a odeslat výsledky na C2 server. Napadený web přitom nepotřeboval aktualizaci pluginu a soubory pluginu na disku zůstaly beze změny.

Co udělat hned

Používáš-li některý z dotčených pluginů, okamžitě prověř seznam uživatelů, adresáře pluginů včetně mu-plugins a tabulku wp_options na uvedené indikátory kompromitace. Pouhá kontrola integrity souborů tento útok nemusí odhalit.

Pluginy byly 7. srpna 2026 v adresáři WordPressu dočasně uzavřeny, zatímco pokračovala jejich kontrola. K 8. srpnu obě kompromitovaná API vracela čistý JSON. Nelze ale předpokládat, že tím automaticky zmizely účty, webshell nebo MU-pluginy, které útok mohl na web už uložit.

Jak útok fungoval

Dotčené pluginy obsahují interní komponentu Biggopti. Ta při načtení administrace získává propagační bannery z API a vykresluje je v wp-admin. Zdroj dat netvořil dynamický aplikační server: šlo o statické JSON soubory v DigitalOcean Spaces, které chránil Cloudflare.

JavaScript Biggopti se registruje na hook admin_init, takže se spustí bezpodmínečně při každém načtení libovolné stránky wp-admin. Z odpovědi API převezme hodnotu display_id a bez escapování ji vloží do HTML atributu id. Právě tady vzniká XSS (cross-site scripting): útočník může do hodnoty vložit vlastní HTML atribut nebo JavaScript, který prohlížeč administrátora spustí.

// Hodnota z nedůvěryhodné vzdálené odpovědi
var f = t.display_id || t.id || "default";
var m = "bdt-admin-biggopti-api-biggopti-" + f;

// Chybí escapování f před vložením do HTML
A = 'id="' + m + '"';

Ve stejném kódu se atribut data-display-id escapoval funkcí, která nahrazuje &, <, >, " za HTML entity. Pro atribut id ale tato ochrana chyběla. Pozdější verze doplnily sanitizaci obsahu banneru přes DOMParser, která odstraňuje nebezpečné HTML tagy a inline handlery; neescapované display_id v id však zůstalo.

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

Chyba se objevila 1. března 2026 v Prime Slider 4.1.9, v SVN revizi r3471891, a následně se rozšířila do dalších pluginů. Neoprávněná osoba získala zápis do úložiště dodavatele a nahradila legitimní JSON připravenými záznamy. Hodnota display_id ukončila atribut id, přidala handler onanimationstart a CSS animaci; prohlížeč tak spustil payload přibližně do 10 ms od načtení administrační stránky.

Útočník v display_id skrýval přes String.fromCharCode() volání fetch() na externí skript. Ve zneužité odpovědi se banner tvářil jako letní nabídka „Build more. Pay less. This Summer“, ale ve skutečnosti se z něj stal spouštěč další fáze infekce.

Dotčené pluginy a zranitelnost

Zranitelnost v knihovně Biggopti má CVSS 5,4, tedy střední závažnost, a v okamžiku zveřejnění neměla opravu. Útočník však potřeboval kompromitovat server Sigmative API; za této podmínky mohl vložit libovolný skript do stránek, které otevře přihlášený uživatel.

Riziko se týká těchto pluginů a slugů:

  • 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)

Primární payload: účet správce, webshell a perzistence

Pluginy, které používaly endpoint api-data-all-records, načítaly primární payload w2.js. Skript nejdřív kontaktoval C2 endpoint ia-cdn[.]com/fz/c, kterému předal origin napadeného webu. Pokud server odpověděl stavem skip nebo done, skript akci ukončil; jinak dostal instrukce pro další postup.

V aktivní relaci administrátora pak zneužil X-WP-Nonce a přes WordPress REST API založil nový účet s rolí administrátora. Když REST API neuspělo, přešel na klasické odeslání formuláře. Následně z C2 stáhl ZIP falešného pluginu a nahrál ho přes standardní formulář pro instalaci pluginu.

Falešný plugin používal slug wp-smart-thumbnails nebo podobně neutrálně znějící název. Obsahoval webshell emer-run.php, který útočník volal přímo přes URL. Tento soubor dále ukládal do adresáře Must-Use pluginů dva moduly pro perzistenci; získané vzorky měly časové údaje zpětně nastavené na září 2025, aby lépe splynuly s existujícími soubory. Názvy souborů se mezi jednotlivými infekcemi mohou lišit.

  • První MU-plugin vytváří zadní vrátka pro magic login. Nepřihlášený útočník se s tokenem v parametru ?_wplogin=<token> dostane do administrace pod nejdéle registrovaným administrátorským účtem webu.
  • Druhý MU-plugin zakrývá stopy. Hookuje databázové dotazy WordPressu, skryje vytvořené cizí účty v seznamu uživatelů administrace a odpovídajícím způsobem sníží i celkový počet uživatelů.
  • Další zaznamenaný MU-plugin se vydává za modul „Health Check“.
  • V každé fázi skript odesílá výsledek na C2 přes navigator.sendBeacon.

Alternativní payload: předvídatelné přihlašovací údaje

Druhá větev útoku používala x.js, který ležel přímo v infrastruktuře BdThemes a obsluhoval pluginy s endpointem api-data-records. Skript nevolil údaje náhodně: vypočítal je deterministicky z hostname napadeného webu. Útočník proto nemusel centrálně uchovávat seznam kompromitovaných webů – přihlašovací údaje pro konkrétní doménu si dokázal kdykoli znovu odvodit.

function makeCreds(host) {
  var hash = 0;

  for (var i = 0; i < host.length; i++) {
    hash = ((hash << 5) - hash + host.charCodeAt(i)) | 0;
  }

  var suffix = Math.abs(hash).toString(36).slice(0, 6);

  return {
    user: "bd_" + suffix,
    pass: "Bd@26!" + suffix + "x"
  };
}

Tak vzniká uživatelské jméno bd_ následované šesti znaky base36 a heslo ve tvaru Bd@26! + stejný suffix + x. Skript účet spojí s e-mailem na doméně @wordpress.org, případně se při vyhledávání stop počítá také s @developer.wordpress.org. Přes načtení /wp-admin/user-new.php získá nonce a formulářem vytvoří administrátora. Výsledek – včetně uživatelského jména, hesla, originu webu, URL odhlášení a aktuální URL – pošle na C2.

Po pokusu o vytvoření účtu si payload ukládá příznak do localStorage, pokud je úložiště prohlížeče dostupné. Pro odeslání dat preferuje navigator.sendBeacon; když není k dispozici, používá fetch() s metodou POST, keepalive, režimem no-cors a bez cookies.

Indikátory kompromitace

Při incident response hledej soubory, databázové volby i podezřelé účty. Kontroluj zejména následující hodnoty:

  • Primární payload: ia-cdn[.]com/fz/w2.js
  • C2 beacon endpoint: ia-cdn[.]com/fz/c
  • Dříve kompromitované endpointy: api[.]sigmative[.]io/prod/store/api/biggopti/api-data-all-records, api[.]sigmative[.]io/prod/store/api/biggopti/api-data-records a api[.]sigmative[.]io/prod/store/api/biggopti/x.js
  • Webshell emer-run.php s MD5 1024732009983dd5e54b4cf5593f04d4
  • MU-plugin pro magic login class-wp-token-validate.php s MD5 7719cd98a35ffad2771f26d1ceab7d27
  • MU-plugin pro skrývání stop class-wp-query-9d127ff3.php nebo podobný soubor podle vzoru class-wp-query-*.php; zachycený vzorek má MD5 9aadc3e5c5242b273bd17c5bdc358845
  • MU-plugin maskovaný jako kontrola zdraví wp-cache-optimizer.php s MD5 e450ae5bc4bfc0d960dded06a76bb8e9
  • Volba databáze fz_emer_login_tokens, která ukládá tokeny magic loginu
  • Volba databáze fz_emer_done_v1, která označuje dokončenou kompromitaci
  • Účty s e-mailovou adresou na @wordpress.org nebo @developer.wordpress.org
  • Uživatelská jména odpovídající vzoru bd_ a šesti alfanumerickým znakům

Časová osa

  1. 1. března 2026: Prime Slider 4.1.9 dostal Biggopti API JavaScript. Revize SVN r3471891 vložila display_id bez escapování do atributu id; plugin používal endpoint api-data-records.
  2. 10. května 2026: endpoint se změnil na api-data-all-records. Do obsahu banneru přibyla sanitizace přes DOMParser, ale atribut id zůstal zranitelný.
  3. 23. června 2026: v kompromitované kampani začínalo datum banneru „Summer Sale“. Jde o nejdřívější možný okamžik aktivního XSS.
  4. 6. srpna 2026, 22:40 UTC: kompromitovaný api-data-records měl poslední změnu.
  5. 7. srpna 2026, 09:11 UTC: kompromitovaný api-data-all-records měl poslední změnu.
  6. 7. srpna 2026: útok se objevil v provozu a dotčené pluginy se uzavřely v adresáři WordPressu.
  7. 7. srpna 2026, 16:56 UTC: vznikly forenzní artefakty pro vyšetřování.
  8. 8. srpna 2026: oba API endpointy začaly vracet čistý JSON.

Proč je tento útok obtížné zachytit

Útočníci nepozměnili zdrojové soubory v repozitáři WordPress.org ani soubory nainstalovaného pluginu. Zneužili důvěru v datový kanál dodavatele a administrátorský prohlížeč. Kontrola hashů souborů proto před samotným spuštěním payloadu neměla co najít a webový aplikační firewall útok obvykle viděl jen omezeně.

C2 infrastruktura souvisí také s nedávnými supply-chain útoky na Advanced Responsive Video Embedder a OptinMonster. Nahrání škodlivých JSON záznamů i x.js přímo do bucketu dodavatele ukazuje na kompromitované přístupy ke cloudovému úložišti BdThemes nebo k jeho interní infrastruktuře.

Pokud na webu najdeš byť jediný indikátor, ber ho jako incident s možným vzdáleným spuštěním kódu. Zruš podezřelé účty, odstraň nalezené škodlivé soubory a databázové volby, prověř všechny MU-pluginy, změň přístupy administrátorů i hostingu a obnov důvěryhodný stav z ověřené zálohy. Signatury malwaru a pravidla WAF získali 7. srpna 2026 uživatelé Wordfence Premium, Care, Response a placeného Wordfence CLI; bezplatné varianty je dostanou po standardní 30denní prodlevě.

Připojte se ke komunitě HelloWP!

Povídejte si s námi o WordPressu, webovém vývoji a sdílejte zkušenosti s ostatními vývojáři.

- členové
- online
Připojit se

Používáme cookies ke zlepšení vašeho zážitku. Pokračováním souhlasíte s našimi Zásadami používání cookies.