Sari la conținut
Atac supply chain în pluginurile BdThemes: XSS prin API JSON compromis
Elena Popescu
Elena Popescu 9 August 2026 · 9 min de citit

Atac supply chain în pluginurile BdThemes: XSS prin API JSON compromis

O compromitere a lanțului de aprovizionare a afectat ecosistemul BdThemes fără ca atacatorii să modifice fișierele din repository-ul WordPress.org. Ei au alterat răspunsuri JSON servite de API-ul furnizorului, iar codul vulnerabil din pluginuri a executat JavaScript în browserul fiecărui administrator autentificat care deschidea orice pagină din wp-admin.

Pluginurile afectate au fost închise temporar în directorul WordPress în timpul inspecției. Dacă administrezi un site care le-a folosit, tratează incidentul ca pe o posibilă compromitere completă: simpla actualizare sau verificare a integrității fișierelor nu este suficientă, deoarece atacul nu trebuia să schimbe fișierele pluginului pe disc.

Acțiune imediată

Atacul putea crea conturi de administrator neautorizate, instala un plugin cu webshell și adăuga backdoor-uri persistente în directorul de Must-Use plugins. Verifică imediat utilizatorii, pluginurile, directorul mu-plugins și opțiunile din baza de date.

Pluginurile afectate și severitatea problemei

Problema afectează biblioteca internă Biggopti în mai multe versiuni și are scorul CVSS 5.4, de severitate medie, fără patch disponibil la momentul raportării. În practică, impactul este mult mai grav atunci când cineva controlează fluxul JSON de la distanță: XSS-ul rulează în contextul unui administrator WordPress autentificat și poate prelua site-ul.

Biggopti există în următoarele pluginuri BdThemes:

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

Cum a funcționat compromiterea prin API

Biggopti încarcă bannere promoționale în panoul WordPress. Componenta citește datele dintr-un API care este susținut de un bucket static DigitalOcean Spaces, protejat de Cloudflare, nu de un server de aplicație dinamic. Atacatorii au obținut acces de scriere la acel bucket și au înlocuit răspunsurile JSON legitime cu răspunsuri malițioase.

Fiecare plugin care folosește Biggopti adaugă un asset JavaScript client-side pe hook-ul admin_init. Scriptul rulează astfel necondiționat la fiecare încărcare a unei pagini din wp-admin, cere datele bannerului și construiește elemente HTML în DOM (Document Object Model, structura de obiecte a paginii).

Vulnerabilitatea XSS (Cross-Site Scripting) apare deoarece codul preia display_id din JSON și îl concatenează direct în atributul HTML id, fără escaping pe partea de client:

// `display_id` vine din răspunsul JSON de la API
var displayId = banner.display_id || banner.id || "default";
var elementId = "bdt-admin-biggopti-api-biggopti-" + displayId;
var attributes = 'id="' + elementId + '"'; // valoare neescapată

În aceeași zonă de cod, atributul data-display-id primește escaping corect pentru &, <, > și ghilimele duble. Diferența arată că atributul id a rămas neprotejat, chiar dacă versiunile ulterioare au introdus un sanitizer bazat pe DOMParser pentru câmpul de conținut al bannerului. Acest sanitizer elimina tagurile HTML periculoase și handler-ele inline, dar nu rezolva injectarea în atributul id.

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

Un display_id construit special închidea atributul id, introducea un handler onanimationstart și pornea o animație CSS după aproximativ 10 milisecunde. Rezultatul: browserul administratorului executa silențios codul injectat la încărcarea oricărei pagini din dashboard. Nu era necesară o actualizare a pluginului, iar fișierele locale ale pluginului nu se modificau; de aceea scanerele care verifică numai integritatea fișierelor și WAF-urile observau greu compromiterea.

Evoluția atacului după executarea XSS

Handler-ul injectat încărca scripturi externe în etape, în funcție de disponibilitatea infrastructurii atacatorilor. Campania a folosit două payload-uri principale, asociate cu endpoint-uri API diferite.

Payload-ul principal: w2.js

Scriptul principal era livrat pluginurilor care apelau endpoint-ul api-data-all-records. Rulat în sesiunea unui administrator, parcurgea următorii pași:

  1. Contacta serverul C2 la ia-cdn[.]com/fz/c și trimitea originea site-ului pentru a primi instrucțiuni de țintire. Oprea execuția dacă serverul răspundea cu starea skip sau done.
  2. Folosea valoarea X-WP-Nonce disponibilă în sesiunea activă pentru a crea un administrator prin WordPress REST API. Dacă metoda eșua, încerca formularele standard WordPress.
  3. Descărca de la C2 o arhivă ZIP care pretindea că este plugin. O instala prin formularul standard de încărcare a pluginurilor. Pluginul fals folosea slug-uri aparent neutre, precum wp-smart-thumbnails, și includea webshell-ul emer-run.php, apelat direct prin URL.
  4. Apela emer-run.php, care instala două module persistente în directorul Must-Use plugins. Mostrele analizate aveau timestamp-uri schimbate în septembrie 2025 pentru a se amesteca printre fișierele legitime; numele fișierelor puteau varia între infecții.
  5. Primul modul MU era un backdoor de autentificare magică: permitea intrarea administrativă neautentificată cu parametrul URL ?_wplogin=<token> și viza administratorul înregistrat cel mai devreme pe site.
  6. Al doilea modul MU ascundea urmele analizei: se lega de interogările WordPress către baza de date, ascundea conturile neautorizate din lista de utilizatori din administrare și micșora corespunzător totalul de utilizatori.
  7. Trimitea rezultatele fiecărei etape către C2 prin navigator.sendBeacon.

Payload-ul alternativ: x.js

Scriptul alternativ era găzduit direct în infrastructura BdThemes și era servit victimelor ale căror pluginuri apelau api-data-records. El calcula credențiale deterministe pornind de la hostname-ul site-ului: genera un hash base36 de șase caractere, un nume de utilizator cu prefixul bd_ și o parolă în forma Bd@26!<hash>x. Contul primea o adresă de e-mail @wordpress.org.

Caracterul determinist elimina nevoia atacatorilor de a păstra central o listă cu site-urile compromise. Un responder la incidente putea calcula, pentru un domeniu suspect, numele de utilizator și parola pe care scriptul încerca să le creeze.

Apoi x.js încărca pagina /wp-admin/user-new.php, extrăgea nonce-ul formularului și trimitea formularul de creare a unui utilizator cu rolul administrator. Raporta către C2 starea operației, credențialele, originea site-ului, URL-ul paginii și linkul de logout, prin sendBeacon; când API-ul nu era disponibil, folosea fetch cu keepalive. Dacă putea accesa localStorage, marca browserul ca procesat pentru a evita repetarea operației.

Indicatori de compromitere

Caută indicatorii de mai jos în loguri, în fișiere și în baza de date. Endpoint-urile API BdThemes listate au revenit ulterior la JSON curat, dar accesările istorice și artefactele lăsate pe site rămân relevante pentru investigație.

Domenii și endpoint-uri externe

  • Payload principal: ia-cdn[.]com/fz/w2.js
  • Endpoint C2 pentru beacon: ia-cdn[.]com/fz/c
  • Endpoint API compromis: api[.]sigmative[.]io/prod/store/api/biggopti/api-data-all-records
  • Endpoint API compromis: api[.]sigmative[.]io/prod/store/api/biggopti/api-data-records
  • Payload alternativ găzduit în infrastructura furnizorului: api[.]sigmative[.]io/prod/store/api/biggopti/x.js

Fișiere, module MU și opțiuni WordPress

  • Webshell: emer-run.php, MD5 1024732009983dd5e54b4cf5593f04d4.
  • Backdoor MU pentru magic login: class-wp-token-validate.php, MD5 7719cd98a35ffad2771f26d1ceab7d27.
  • Modul MU de ascundere: class-wp-query-9d127ff3.php sau un nume asemănător cu modelul class-wp-query-*.php, MD5 9aadc3e5c5242b273bd17c5bdc358845.
  • Modul MU denumit aparent „Health Check”: wp-cache-optimizer.php, MD5 e450ae5bc4bfc0d960dded06a76bb8e9.
  • Opțiunea fz_emer_login_tokens, care stochează tokenuri pentru magic login.
  • Opțiunea fz_emer_done_v1, care marchează compromiterea ca finalizată.
  • Conturi cu domeniile de e-mail @wordpress.org sau @developer.wordpress.org.
  • Nume de utilizator care respectă modelul bd_ urmat de șase caractere alfanumerice.

Cronologia incidentului

  • 1 martie 2026: Biggopti API JS intră în Prime Slider 4.1.9, în SVN r3471891. Câmpul display_id ajunge neescapat în atributul id; pluginul folosește endpoint-ul api-data-records.
  • 10 mai 2026: endpoint-ul se schimbă în api-data-all-records. Apare sanitizerul DOMParser pentru câmpul de conținut, dar atributul id rămâne neescapat.
  • 23 iunie 2026: câmpul start_date din campania API compromisă, asociat notificării „Summer Sale”, indică cea mai timpurie dată posibilă la care XSS-ul putea fi activ.
  • 6 august 2026, 22:40 UTC: ultima modificare observată pentru api-data-records compromis.
  • 7 august 2026, 09:11 UTC: ultima modificare observată pentru api-data-all-records compromis.
  • 7 august 2026: atacul este observat în practică, iar pluginurile sunt închise în directorul WordPress în așteptarea verificării.
  • 7 august 2026, 16:56 UTC: sunt capturate artefacte pentru investigație.
  • 8 august 2026: ambele endpoint-uri API livrează din nou JSON curat.

Ce verifici acum pe site

Începe cu toate site-urile care au avut instalat unul dintre cele șapte pluginuri, inclusiv site-urile pe care pluginul nu mai este activ astăzi. Inspectează lista de utilizatori direct în WordPress și în baza de date, deoarece modulul de stealth putea ascunde conturile din interfața de administrare.

  1. Caută utilizatori administratori necunoscuți, mai ales conturi bd_ urmate de șase caractere și adrese cu @wordpress.org sau @developer.wordpress.org.
  2. Verifică directorul pluginurilor pentru emer-run.php, pluginuri încărcate recent și arhive ori directoare cu nume care imită pluginuri legitime, inclusiv wp-smart-thumbnails.
  3. Inspectează wp-content/mu-plugins pentru fișierele și modelele de nume din lista de indicatori, inclusiv module cu timestamp-uri suspect de vechi.
  4. Caută opțiunile fz_emer_login_tokens și fz_emer_done_v1 în tabela de opțiuni WordPress.
  5. Verifică logurile pentru cereri către domeniile și endpoint-urile IoC și compară fișierele găsite cu hash-urile MD5 furnizate.
  6. Presupune că un site pe care găsești indicatori a permis execuție de cod la distanță. Elimină persistența, conturile neautorizate și webshell-urile, apoi investighează în profunzime ce alte modificări au apărut.

Regulile WAF și semnăturile pentru această compromitere au fost puse la dispoziția utilizatorilor Wordfence Premium, Care și Response, precum și clienților plătiți Wordfence CLI, la 7 august 2026. Utilizatorii versiunilor gratuite Wordfence și Wordfence CLI le primesc după întârzierea standard de 30 de zile.

Incidentul arată un risc pe care verificarea strictă a fișierelor nu îl acoperă: date considerate de încredere, încărcate de la distanță în interfața de administrare, sunt parte din suprafața de atac. Pentru componentele proprii, tratează orice câmp primit prin API ca date neîncredere, escapează-l pentru contextul exact în care îl inserezi și limitează încărcarea scripturilor administrative la ecranele unde ai nevoie de ele.

Elena Popescu

Elena Popescu

Redactor-șef al echipei române, dezvoltator web full-stack și mentor tehnic. .NET și C# sunt domeniile mele principale, dar nici JavaScript modern nu-mi este străin.

Toate articolele

Alătură-te comunității HelloWP!

Discută cu noi despre WordPress, dezvoltare web și împărtășește experiențe cu alți dezvoltatori.

- membri
- online
Alătură-te

Folosim cookie-uri pentru a vă îmbunătăți experiența. Continuând, sunteți de acord cu Politica noastră privind cookie-urile.