Przejdź do treści
Atak na BdThemes: zatrute API JSON przejmuje WordPressa
Katarzyna Nowak
Katarzyna Nowak 9 August 2026 · 9 min czytania

Atak na BdThemes: zatrute API JSON przejmuje WordPressa

Atak na ekosystem BdThemes nie wymagał podmiany plików wtyczki ani instalacji aktualizacji przez ofiarę. Wystarczyło przejąć zdalny strumień JSON, który wyświetlał bannery promocyjne w panelu WordPressa, aby uruchomić JavaScript w przeglądarce każdego zalogowanego administratora.

Dotknięte wtyczki są tymczasowo zamknięte w oficjalnym katalogu WordPressa na czas pełnej kontroli i trwającego dochodzenia zespołu WordPress Plugins. Problem ma CVSS 5.4 (średni), nie ma poprawki, a osoba, która go zgłosiła, pozostaje nieznana.

Pełne przejęcie witryny bez aktualizacji wtyczki

Jeśli używasz choć jednej z wymienionych dalej wtyczek BdThemes, potraktuj instalację jako potencjalnie przejętą. Atak mógł tworzyć konto administratora, instalować webshell i zapisywać trwałe backdoory, nie zmieniając plików oryginalnej wtyczki.

Jak doszło do kompromitacji łańcucha dostaw

Wtyczki BdThemes zawierają wewnętrzny komponent Biggopti. Pobiera on bannery promocyjne z API dostawcy i renderuje je w kokpicie administracyjnym WordPressa. API korzystało ze statycznych plików JSON w bucketcie DigitalOcean Spaces, chronionym przez Cloudflare, a nie z dynamicznego serwera aplikacyjnego.

Autorzy wtyczek wprowadzili podatność Cross-Site Scripting (XSS), czyli możliwość uruchomienia obcego JavaScriptu w kontekście zaufanej strony. Następnie nieuprawniony podmiot uzyskał zapis do bucketa i podmienił prawidłowe odpowiedzi JSON na spreparowane dane. Żaden plik kodu źródłowego w oficjalnym repozytorium WordPress.org nie został przy tym zmieniony.

Biggopti dodaje asset JavaScript po hooku admin_init, więc działa bezwarunkowo przy każdym załadowaniu dowolnej strony wp-admin. Skrypt pobiera dane bannera, a potem wstawia pole display_id z odpowiedzi JSON bez escapowania bezpośrednio do atrybutu HTML id.

// JavaScript - fragment podatnego składania atrybutu HTML
var f = t.display_id || t.id || "default";
var m = "bdt-admin-biggopti-api-biggopti-" + f;
A = 'id="' + m + '"';

W tym samym kodzie data-display-id był już escapowany przez funkcję zamieniającą znaki &, <, > i " na encje HTML. Konstrukcja tej funkcji była zawiła, prawdopodobnie na skutek minifikacji lub bundlowania, ale nie chroniła osobnego atrybutu id.

// JavaScript - funkcja użyta dla innego atrybutu
function p(t) {
  return (t || "")
    .replace(/&/g, "&amp;")
    .replace(/</g, "&lt;")
    .replace(/>/g, "&gt;")
    .replace(/"/g, "&quot;");
}

Historia commitów SVN wskazuje, że nieescapowane wstawianie do atrybutu pojawiło się 1 marca 2026 roku w Prime Sliderze 4.1.9 (bdthemes-prime-slider-lite, rewizja r3471891), a później trafiło do pozostałych wtyczek. Późniejsze wydania dodały oparty na DOMParser sanitizer dla pola treści bannera, który usuwał niebezpieczne tagi HTML i inline handlery, lecz nie naprawiły id.

Złośliwy display_id zamykał atrybut id i dopisywał handler zdarzenia onanimationstart. Animacja CSS uruchamiała go w ciągu 10 ms, po cichu, gdy administrator otwierał stronę kokpitu. W przechwyconej odpowiedzi API znajdował się rekord typu adminDashboard, z tytułem bannera „Build more. Pay less. This Summer”, którego display_id ładował zewnętrzny skrypt przez zakodowane wywołanie fetch.

Atak był sterowany wyłącznie przez API: ofiara nie musiała aktualizować wtyczki, a pliki na dysku pozostawały niezmienione. Dlatego skanery integralności oparte na plikach nie widzą tego mechanizmu, a firewall aplikacji webowej ma ograniczoną widoczność ataku wykonywanego w przeglądarce zalogowanego administratora.

Dwa warianty złośliwego payloadu

Handler zdarzenia rozpoczynał wieloetapową kompromitację i dobierał zewnętrzny skrypt zależnie od dostępności infrastruktury atakującego. Główny payload w2.js trafiał do wtyczek korzystających z endpointu api-data-all-records.

Po uruchomieniu w sesji zalogowanego administratora w2.js kontaktował się z serwerem C2 ia-cdn[.]com/fz/c, przekazując origin witryny i pobierając instrukcje targetowania. Jeśli C2 zwracał status skip albo done, skrypt kończył pracę.

Następnie wykorzystywał X-WP-Nonce obecny w aktywnej sesji administratora, by utworzyć przez WordPress REST API nowe konto z rolą administratora. Gdy ta droga zawodziła, przechodził na zwykłe wysyłanie formularzy WordPressa.

Kolejny etap pobierał z C2 fałszywe archiwum ZIP wtyczki i instalował je przez standardowy formularz uploadu wtyczek. Fałszywa wtyczka ukrywała się pod slugiem wp-smart-thumbnails albo podobną, neutralnie brzmiącą nazwą, zawierała webshell emer-run.php, a atakujący wywoływał ten plik bezpośrednio po URL.

Wywołanie emer-run.php instalowało w katalogu Must-Use plugins dwa moduły zapewniające trwałość. Obie przechwycone próbki miały timestamp cofnięty do września 2025 roku, aby wtopić się w legalne daty systemu plików; nazwy plików mogą różnić się między infekcjami.

Pierwszy moduł był backdoorem magic login: pozwalał wejść do panelu administratora bez uwierzytelnienia przez parametr URL ?_wplogin=<token> i kierował atak na najdłużej zarejestrowane konto administratora. Drugi moduł utrudniał analizę – podpinał się do zapytań bazy danych WordPressa, ukrywał utworzone konta na liście użytkowników administracyjnych i odpowiednio zmniejszał łączną liczbę użytkowników. Wyniki każdego etapu skrypt wysyłał do C2 przez navigator.sendBeacon.

Drugi skrypt, x.js, hostowano bezpośrednio w infrastrukturze BdThemes i podawano ofiarom korzystającym z endpointu api-data-records. Wyliczał on deterministyczne dane administratora na podstawie nazwy hosta witryny: obliczał hash, zamieniał jego wartość bezwzględną na base36 i brał pierwsze sześć znaków.

// JavaScript - schemat deterministycznych danych konta
var n = Math.abs(hash).toString(36).slice(0, 6);
var user = "bd_" + n;
var pass = "Bd@26!" + n + "x";

Powstawała nazwa użytkownika bd_ z sześcioma znakami hash base36 oraz hasło z prefiksem Bd@26!, tym samym hashem i końcowym x. Skrypt łączył te dane z adresem e-mail w domenie @wordpress.org, tworzył konto administratora przez /wp-admin/user-new.php, raportował rezultat do C2 i – jeśli przeglądarka udostępniała localStorage – zapisywał flagę wykonania lokalnie.

Deterministyczny algorytm zwalniał atakujących z utrzymywania centralnej listy przejętych stron. Zespół reagujący na incydent może za to obliczyć dokładną nazwę i hasło, których trzeba szukać dla podejrzanej domeny.

Zakres i powiązania kampanii

Domena C2 jest powiązana z tą samą grupą, która stała za atakami łańcucha dostaw na Advanced Responsive Video Embedder oraz OptinMonster w ciągu poprzednich dwóch miesięcy.

Celem kampanii było masowe, ciche utrzymanie uprawnień administracyjnych i zdalne wykonanie kodu w milionach środowisk WordPressa. Atakujący nie potrzebowali dostępu do konkretnych witryn: wystarczyło zatruć zaufany feed promocyjny i wykorzystać błąd w kodzie, który go wyświetlał.

Obecność złośliwych rekordów JSON oraz drugiego payloadu x.js bezpośrednio w bucketcie dostawcy wskazuje na poważne przejęcie poświadczeń do magazynu chmurowego BdThemes albo jego infrastruktury wewnętrznej.

Dotknięte wtyczki

Podatność Biggopti przez parametr display_id z API Sigmative dotyczy następujących wtyczek:

Pełny opis podatności znajduje się w bazie Wordfence Intelligence.

Wskaźniki kompromitacji

Sprawdź zasoby zewnętrzne, pliki, opcje bazy danych oraz konta użytkowników. Endpointy API dostawcy wymienione niżej zostały już oczyszczone, ale ich wcześniejsze użycie stanowi ważny ślad analityczny.

  • Główny payload: ia-cdn[.]com/fz/w2.js.
  • Endpoint beacon C2: ia-cdn[.]com/fz/c.
  • Zatruty endpoint dostawcy: api[.]sigmative[.]io/prod/store/api/biggopti/api-data-all-records.
  • Zatruty endpoint dostawcy: api[.]sigmative[.]io/prod/store/api/biggopti/api-data-records.
  • Zasób drugiego payloadu: api[.]sigmative[.]io/prod/store/api/biggopti/x.js.
  • Webshell emer-run.php, MD5: 1024732009983dd5e54b4cf5593f04d4.
  • Backdoor magic login MU-plugin: class-wp-token-validate.php, MD5: 7719cd98a35ffad2771f26d1ceab7d27.
  • Moduł ukrywania MU-plugin: class-wp-query-9d127ff3.php lub podobny plik pasujący do class-wp-query-*.php, MD5 próbki: 9aadc3e5c5242b273bd17c5bdc358845.
  • Moduł „Health Check” MU-plugin: wp-cache-optimizer.php, MD5: e450ae5bc4bfc0d960dded06a76bb8e9.
  • Opcja bazy danych fz_emer_login_tokens, która przechowuje tokeny magic login.
  • Opcja bazy danych fz_emer_done_v1, która oznacza zakończoną kompromitację.
  • Konta używające domen @wordpress.org albo @developer.wordpress.org.
  • Nazwy użytkowników pasujące do wzorca bd_ oraz sześć znaków alfanumerycznych.

Oś czasu

  • 1 marca 2026 – JavaScript API Biggopti trafia do Prime Slidera 4.1.9 w rewizji SVN r3471891. display_id nie jest escapowany w atrybucie id, a wtyczka korzysta z endpointu api-data-records.
  • 10 maja 2026 – endpoint zmienia się na api-data-all-records. Dodano sanitizer DOMParser dla pola treści, ale id nadal pozostaje nieescapowany.
  • 23 czerwca 2026start_date kampanii z zatrutym API dla komunikatu „Summer Sale”; to najwcześniejsza możliwa data aktywności XSS.
  • 6 sierpnia 2026, 22:40 UTC – czas Last-Modified zatrutego api-data-records.
  • 7 sierpnia 2026, 09:11 UTC – czas Last-Modified zatrutego api-data-all-records.
  • 7 sierpnia 2026 – zespół Threat Intelligence otrzymuje zgłoszenie i obserwuje atak w środowiskach produkcyjnych.
  • 7 sierpnia 2026 – wtyczki zostają zamknięte w katalogu WordPressa do czasu kontroli.
  • 7 sierpnia 2026, 16:56 UTC – przechwycono artefakty dochodzenia.
  • 8 sierpnia 2026 – oba endpointy API ponownie zwracają czysty JSON.

Co sprawdzić teraz

Przejrzyj listę użytkowników w bazie danych, katalogi wtyczek – w tym Must-Use plugins – oraz tabelę opcji pod kątem wszystkich wskaźników z tej listy. Nie ograniczaj się do kontroli integralności plików: ten atak tworzył trwałe elementy poza oryginalnymi plikami wtyczek.

Sygnatury malware i reguły WAF wykrywające tę kompromitację otrzymali 7 sierpnia 2026 użytkownicy Wordfence Premium, Care, Response oraz płatnego Wordfence CLI. Użytkownicy bezpłatnego Wordfence i bezpłatnego Wordfence CLI dostaną je po standardowym 30-dniowym opóźnieniu.

Dołącz do społeczności HelloWP!

Porozmawiaj z nami o WordPressie i tworzeniu stron oraz dziel się doświadczeniami z innymi deweloperami.

- członkowie
- online
Dołącz

Używamy plików cookie, aby poprawić Twoje doświadczenia. Kontynuując, zgadzasz się na naszą Politykę plików cookie.