{"id":279,"date":"2026-08-09T10:40:27","date_gmt":"2026-08-09T08:40:27","guid":{"rendered":"https:\/\/helloblog.io\/pt\/ataque-cadeia-fornecimento-bdthemes-json-contaminado\/"},"modified":"2026-08-09T10:40:27","modified_gmt":"2026-08-09T08:40:27","slug":"ataque-cadeia-fornecimento-bdthemes-json-contaminado","status":"publish","type":"post","link":"https:\/\/helloblog.io\/pt\/ataque-cadeia-fornecimento-bdthemes-json-contaminado\/","title":{"rendered":"Ataque \u00e0 cadeia de fornecimento BdThemes atrav\u00e9s de JSON contaminado"},"content":{"rendered":"\n<p>Uma cadeia de fornecimento comprometida no ecossistema BdThemes permitiu executar JavaScript malicioso nas sess\u00f5es autenticadas de administradores do WordPress. O ataque est\u00e1 a explorar respostas JSON remotas destinadas a banners promocionais, sem alterar qualquer ficheiro do plugin no reposit\u00f3rio oficial ou no disco do site; por isso, pode n\u00e3o detetar-se com verifica\u00e7\u00f5es de integridade baseadas em ficheiros.<\/p>\n\n\n\n<p>Os sete plugins afetados est\u00e3o temporariamente fechados no diret\u00f3rio de plugins do WordPress enquanto decorre a inspe\u00e7\u00e3o. Se tens algum instalado, trata o incidente como uma potencial comprometimento completo do site: o c\u00f3digo est\u00e1 a criar administradores indevidos, a instalar uma webshell e a estabelecer persist\u00eancia no servidor.<\/p>\n\n\n\n<div class=\"wp-block-group callout callout-danger is-style-danger is-layout-flow wp-block-group-is-layout-flow\" style=\"border-width:1px;border-radius:8px;padding-top:1rem;padding-right:1.5rem;padding-bottom:1rem;padding-left:1.5rem\">\n\n<h4 class=\"wp-block-heading callout-title\">Risco imediato<\/h4>\n\n\n<p>A vers\u00e3o vulner\u00e1vel do componente n\u00e3o tem corre\u00e7\u00e3o indicada. Os endpoints remotos passaram a devolver JSON limpo em 8 de agosto de 2026, mas isso n\u00e3o remove contas, plugins, ficheiros MU nem op\u00e7\u00f5es da base de dados que j\u00e1 tenham sido criados.<\/p>\n\n<\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Como funciona a cadeia de ataque<\/h2>\n\n\n\n<p>Os plugins BdThemes incluem o <em>Biggopti<\/em>, um componente interno que est\u00e1 a obter banners promocionais de uma API e a mostr\u00e1-los no painel de administra\u00e7\u00e3o. A API serve ficheiros JSON est\u00e1ticos a partir de um bucket DigitalOcean Spaces protegido por Cloudflare, n\u00e3o de um servidor de aplica\u00e7\u00f5es din\u00e2mico; o acesso de escrita ao bucket foi comprometido e as respostas leg\u00edtimas foram substitu\u00eddas por registos preparados para explorar XSS.<\/p>\n\n\n\n<p>O script do Biggopti \u00e9 colocado em fila durante <code>admin_init<\/code>, pelo que est\u00e1 a correr em todos os carregamentos de p\u00e1ginas <code>wp-admin<\/code> para cada plugin que o usa. Ao construir o contentor HTML no DOM, o valor remoto <code>display_id<\/code> concatena-se diretamente no atributo <code>id<\/code>, sem escape no lado do cliente; o campo <code>data-display-id<\/code> do mesmo c\u00f3digo \u00e9 escapado, o que confirma que a falha se limita \u00e0 inje\u00e7\u00e3o do atributo <code>id<\/code>.<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#24292e\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" style=\"color:#e1e4e8;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>\/\/ display_id vem da resposta JSON remota e n\u00e3o \u00e9 escapado\nvar f = t.display_id || t.id || &quot;default&quot;;\nvar m = &quot;bdt-admin-biggopti-api-biggopti-&quot; + f;\nA = 'id=&quot;' + m + '&quot;'; \/\/ valor injetado no DOM\n\n\/\/ Noutro atributo, o c\u00f3digo j\u00e1 escapava caracteres HTML\nfunction escapeAttribute(value) {\n  return (value || &quot;&quot;)\n    .replace(\/&amp;\/g, &quot;&amp;amp;&quot;)\n    .replace(\/&lt;\/g, &quot;&amp;lt;&quot;)\n    .replace(\/&gt;\/g, &quot;&amp;gt;&quot;)\n    .replace(\/&quot;\/g, &quot;&amp;quot;&quot;);\n}\n<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki github-dark\" style=\"background-color:#24292e;color:#e1e4e8\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#6A737D\">\/\/ display_id vem da resposta JSON remota e n\u00e3o \u00e9 escapado<\/span><\/span>\n<span class=\"line\"><span style=\"color:#F97583\">var<\/span><span style=\"color:#E1E4E8\"> f <\/span><span style=\"color:#F97583\">=<\/span><span style=\"color:#E1E4E8\"> t.display_id <\/span><span style=\"color:#F97583\">||<\/span><span style=\"color:#E1E4E8\"> t.id <\/span><span style=\"color:#F97583\">||<\/span><span style=\"color:#9ECBFF\"> \"default\"<\/span><span style=\"color:#E1E4E8\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color:#F97583\">var<\/span><span style=\"color:#E1E4E8\"> m <\/span><span style=\"color:#F97583\">=<\/span><span style=\"color:#9ECBFF\"> \"bdt-admin-biggopti-api-biggopti-\"<\/span><span style=\"color:#F97583\"> +<\/span><span style=\"color:#E1E4E8\"> f;<\/span><\/span>\n<span class=\"line\"><span style=\"color:#79B8FF\">A<\/span><span style=\"color:#F97583\"> =<\/span><span style=\"color:#9ECBFF\"> 'id=\"'<\/span><span style=\"color:#F97583\"> +<\/span><span style=\"color:#E1E4E8\"> m <\/span><span style=\"color:#F97583\">+<\/span><span style=\"color:#9ECBFF\"> '\"'<\/span><span style=\"color:#E1E4E8\">; <\/span><span style=\"color:#6A737D\">\/\/ valor injetado no DOM<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color:#6A737D\">\/\/ Noutro atributo, o c\u00f3digo j\u00e1 escapava caracteres HTML<\/span><\/span>\n<span class=\"line\"><span style=\"color:#F97583\">function<\/span><span style=\"color:#B392F0\"> escapeAttribute<\/span><span style=\"color:#E1E4E8\">(<\/span><span style=\"color:#FFAB70\">value<\/span><span style=\"color:#E1E4E8\">) {<\/span><\/span>\n<span class=\"line\"><span style=\"color:#F97583\">  return<\/span><span style=\"color:#E1E4E8\"> (value <\/span><span style=\"color:#F97583\">||<\/span><span style=\"color:#9ECBFF\"> \"\"<\/span><span style=\"color:#E1E4E8\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">    .<\/span><span style=\"color:#B392F0\">replace<\/span><span style=\"color:#E1E4E8\">(<\/span><span style=\"color:#9ECBFF\">\/<\/span><span style=\"color:#DBEDFF\">&#x26;<\/span><span style=\"color:#9ECBFF\">\/<\/span><span style=\"color:#F97583\">g<\/span><span style=\"color:#E1E4E8\">, <\/span><span style=\"color:#9ECBFF\">\"&#x26;amp;\"<\/span><span style=\"color:#E1E4E8\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">    .<\/span><span style=\"color:#B392F0\">replace<\/span><span style=\"color:#E1E4E8\">(<\/span><span style=\"color:#9ECBFF\">\/<\/span><span style=\"color:#DBEDFF\">&#x3C;<\/span><span style=\"color:#9ECBFF\">\/<\/span><span style=\"color:#F97583\">g<\/span><span style=\"color:#E1E4E8\">, <\/span><span style=\"color:#9ECBFF\">\"&#x26;lt;\"<\/span><span style=\"color:#E1E4E8\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">    .<\/span><span style=\"color:#B392F0\">replace<\/span><span style=\"color:#E1E4E8\">(<\/span><span style=\"color:#9ECBFF\">\/<\/span><span style=\"color:#DBEDFF\">><\/span><span style=\"color:#9ECBFF\">\/<\/span><span style=\"color:#F97583\">g<\/span><span style=\"color:#E1E4E8\">, <\/span><span style=\"color:#9ECBFF\">\"&#x26;gt;\"<\/span><span style=\"color:#E1E4E8\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">    .<\/span><span style=\"color:#B392F0\">replace<\/span><span style=\"color:#E1E4E8\">(<\/span><span style=\"color:#9ECBFF\">\/<\/span><span style=\"color:#DBEDFF\">\"<\/span><span style=\"color:#9ECBFF\">\/<\/span><span style=\"color:#F97583\">g<\/span><span style=\"color:#E1E4E8\">, <\/span><span style=\"color:#9ECBFF\">\"&#x26;quot;\"<\/span><span style=\"color:#E1E4E8\">);<\/span><\/span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">}<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>A inje\u00e7\u00e3o de <code>display_id<\/code> est\u00e1 a sair do atributo <code>id<\/code> e a acrescentar um handler <code>onanimationstart<\/code>. Uma anima\u00e7\u00e3o CSS dispara-o em cerca de 10 milissegundos, silenciosamente, assim que um administrador abre qualquer p\u00e1gina do painel. Embora vers\u00f5es posteriores tenham acrescentado um sanitizador com <code>DOMParser<\/code> para o campo de conte\u00fado do banner, removendo tags e handlers perigosos, o atributo <code>id<\/code> continuou sem escape.<\/p>\n\n\n\n<p>A falha foi introduzida em 1 de mar\u00e7o de 2026, no Prime Slider 4.1.9, e acabou por aplicar-se a outros plugins. N\u00e3o \u00e9 necess\u00e1ria uma atualiza\u00e7\u00e3o do plugin para o site ficar exposto: basta que um administrador esteja a carregar o painel enquanto a resposta JSON maliciosa est\u00e1 dispon\u00edvel. A execu\u00e7\u00e3o est\u00e1 a acontecer no navegador autenticado, o que d\u00e1 ao script os nonces e as permiss\u00f5es dessa sess\u00e3o.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Plugins e vulnerabilidade afetados<\/h2>\n\n\n\n<p>A vulnerabilidade \u00e9 uma XSS atrav\u00e9s do par\u00e2metro <code>display_id<\/code> da API Sigmative na biblioteca Biggopti, classificada com CVSS 5,4, de gravidade m\u00e9dia e sem corre\u00e7\u00e3o. Um atacante que controle o servidor da API consegue injetar scripts arbitr\u00e1rios nas p\u00e1ginas que mostram o registo contaminado; no cen\u00e1rio observado, est\u00e1 a visar especificamente administradores autenticados.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n\n<li><a href=\"https:\/\/www.wordfence.com\/threat-intel\/vulnerabilities\/wordpress-plugins\/bdthemes-element-pack-lite\">Element Pack Addons for Elementor &#8211; Elementor Widgets, Elementor Templates, Elementor Addons<\/a> &#8211; <code>bdthemes-element-pack-lite<\/code><\/li>\n\n\n<li><a href=\"https:\/\/www.wordfence.com\/threat-intel\/vulnerabilities\/wordpress-plugins\/live-copy-paste\">Live Copy Paste for Elementor &#8211; Cross Domain Copy Paste &#038; Page Duplicator<\/a> &#8211; <code>live-copy-paste<\/code><\/li>\n\n\n<li><a href=\"https:\/\/www.wordfence.com\/threat-intel\/vulnerabilities\/wordpress-plugins\/pixel-gallery\">Pixel Gallery Addons for Elementor &#8211; Easy Grid, Creative Gallery, Drag and Drop Grid, Custom Grid Layout, Portfolio Gallery<\/a> &#8211; <code>pixel-gallery<\/code><\/li>\n\n\n<li><a href=\"https:\/\/www.wordfence.com\/threat-intel\/vulnerabilities\/wordpress-plugins\/bdthemes-prime-slider-lite\">Prime Slider Addons for Elementor &#8211; Widgets, Templates &#038; Elementor Addons<\/a> &#8211; <code>bdthemes-prime-slider-lite<\/code><\/li>\n\n\n<li><a href=\"https:\/\/www.wordfence.com\/threat-intel\/vulnerabilities\/wordpress-plugins\/smart-admin-assistant\">Smart Admin Assistant &#8211; Dashboard and Site Enhancements<\/a> &#8211; <code>smart-admin-assistant<\/code><\/li>\n\n\n<li><a href=\"https:\/\/www.wordfence.com\/threat-intel\/vulnerabilities\/wordpress-plugins\/ultimate-post-kit\">Ultimate Post Kit Addons for Elementor<\/a> &#8211; <code>ultimate-post-kit<\/code><\/li>\n\n\n<li><a href=\"https:\/\/www.wordfence.com\/threat-intel\/vulnerabilities\/wordpress-plugins\/ultimate-store-kit\">Ultimate Store Kit &#8211; Addon For WooCommerce, EDD and Elementor<\/a> &#8211; <code>ultimate-store-kit<\/code><\/li>\n\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Payloads observados e impacto<\/h2>\n\n\n\n<p>O handler injetado est\u00e1 a descarregar scripts externos conforme o endpoint utilizado pelo plugin. O payload principal, <code>w2.js<\/code>, \u00e9 servido aos plugins que usam <code>api-data-all-records<\/code>; o alternativo, <code>x.js<\/code>, surge alojado diretamente na infraestrutura BdThemes para v\u00edtimas que usam <code>api-data-records<\/code>. Ambos procuram criar acesso administrativo persistente e comunicar os resultados a uma infraestrutura de comando e controlo (C2).<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Payload principal: w2.js<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n\n<li><strong>Valida\u00e7\u00e3o do C2:<\/strong> contacta <code>ia-cdn[.]com\/fz\/c<\/code>, enviando a origem do site para receber instru\u00e7\u00f5es de sele\u00e7\u00e3o. O script termina se o C2 responder com o estado <code>skip<\/code> ou <code>done<\/code>.<\/li>\n\n\n<li><strong>Cria\u00e7\u00e3o de administrador indevido:<\/strong> usa o <code>X-WP-Nonce<\/code> presente na sess\u00e3o administrativa para criar um administrador atrav\u00e9s da REST API do WordPress. Se falhar, recorre a submiss\u00f5es de formul\u00e1rios normais.<\/li>\n\n\n<li><strong>Instala\u00e7\u00e3o de webshell:<\/strong> descarrega do C2 um ZIP que se faz passar por plugin e instala-o pelo formul\u00e1rio padr\u00e3o de carregamento de plugins. Usa o slug <code>wp-smart-thumbnails<\/code>, ou outro nome neutro semelhante, e cont\u00e9m a webshell <code>emer-run.php<\/code>, chamada diretamente por URL.<\/li>\n\n\n<li><strong>Persist\u00eancia em MU-plugins:<\/strong> ao chamar <code>emer-run.php<\/code>, instala dois m\u00f3dulos na diretoria de plugins obrigat\u00f3rios. As amostras observadas tinham datas retroativas para setembro de 2025, procurando misturar-se com timestamps leg\u00edtimos; os nomes dos ficheiros podem variar entre infe\u00e7\u00f5es.<\/li>\n\n\n<li><strong>Backdoor de magic login:<\/strong> o primeiro m\u00f3dulo permite entrar como administrador sem autentica\u00e7\u00e3o atrav\u00e9s de <code>?_wplogin=&lt;token&gt;<\/code> e visa o administrador registado h\u00e1 mais tempo no site.<\/li>\n\n\n<li><strong>Oculta\u00e7\u00e3o anti-an\u00e1lise:<\/strong> o segundo m\u00f3dulo usa hooks nas queries da base de dados do WordPress para esconder as contas indevidas da lista administrativa de utilizadores e reduzir os totais de utilizadores em conformidade.<\/li>\n\n\n<li><strong>Exfiltra\u00e7\u00e3o:<\/strong> envia os resultados de cada fase para o C2 atrav\u00e9s de <code>navigator.sendBeacon<\/code>.<\/li>\n\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\">Payload alternativo: x.js<\/h3>\n\n\n\n<p>O <code>x.js<\/code> est\u00e1 a calcular credenciais administrativas determin\u00edsticas a partir do hostname da v\u00edtima. Calcula um hash inteiro do hostname, converte o valor absoluto para base 36 e usa os primeiros seis caracteres: o utilizador segue o padr\u00e3o <code>bd_<\/code> seguido desse hash e a palavra-passe segue <code>Bd@26!<\/code> mais o hash e o sufixo <code>x<\/code>. A conta associa-se a um endere\u00e7o com dom\u00ednio <code>@wordpress.org<\/code>.<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#24292e\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" style=\"color:#e1e4e8;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>\/\/ L\u00f3gica defensivamente reproduzida para identificar o padr\u00e3o de credenciais\nfunction makeCreds(host) {\n  let hash = 0;\n  for (let i = 0; i &lt; host.length; i++) {\n    hash = ((hash &lt;&lt; 5) - hash + host.charCodeAt(i)) | 0;\n  }\n  const suffix = Math.abs(hash).toString(36).slice(0, 6);\n  return {\n    user: 'bd_' + suffix,\n    pass: 'Bd@26!' + suffix + 'x'\n  };\n}\n<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki github-dark\" style=\"background-color:#24292e;color:#e1e4e8\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#6A737D\">\/\/ L\u00f3gica defensivamente reproduzida para identificar o padr\u00e3o de credenciais<\/span><\/span>\n<span class=\"line\"><span style=\"color:#F97583\">function<\/span><span style=\"color:#B392F0\"> makeCreds<\/span><span style=\"color:#E1E4E8\">(<\/span><span style=\"color:#FFAB70\">host<\/span><span style=\"color:#E1E4E8\">) {<\/span><\/span>\n<span class=\"line\"><span style=\"color:#F97583\">  let<\/span><span style=\"color:#E1E4E8\"> hash <\/span><span style=\"color:#F97583\">=<\/span><span style=\"color:#79B8FF\"> 0<\/span><span style=\"color:#E1E4E8\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color:#F97583\">  for<\/span><span style=\"color:#E1E4E8\"> (<\/span><span style=\"color:#F97583\">let<\/span><span style=\"color:#E1E4E8\"> i <\/span><span style=\"color:#F97583\">=<\/span><span style=\"color:#79B8FF\"> 0<\/span><span style=\"color:#E1E4E8\">; i <\/span><span style=\"color:#F97583\">&#x3C;<\/span><span style=\"color:#E1E4E8\"> host.<\/span><span style=\"color:#79B8FF\">length<\/span><span style=\"color:#E1E4E8\">; i<\/span><span style=\"color:#F97583\">++<\/span><span style=\"color:#E1E4E8\">) {<\/span><\/span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">    hash <\/span><span style=\"color:#F97583\">=<\/span><span style=\"color:#E1E4E8\"> ((hash <\/span><span style=\"color:#F97583\">&#x3C;&#x3C;<\/span><span style=\"color:#79B8FF\"> 5<\/span><span style=\"color:#E1E4E8\">) <\/span><span style=\"color:#F97583\">-<\/span><span style=\"color:#E1E4E8\"> hash <\/span><span style=\"color:#F97583\">+<\/span><span style=\"color:#E1E4E8\"> host.<\/span><span style=\"color:#B392F0\">charCodeAt<\/span><span style=\"color:#E1E4E8\">(i)) <\/span><span style=\"color:#F97583\">|<\/span><span style=\"color:#79B8FF\"> 0<\/span><span style=\"color:#E1E4E8\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">  }<\/span><\/span>\n<span class=\"line\"><span style=\"color:#F97583\">  const<\/span><span style=\"color:#79B8FF\"> suffix<\/span><span style=\"color:#F97583\"> =<\/span><span style=\"color:#E1E4E8\"> Math.<\/span><span style=\"color:#B392F0\">abs<\/span><span style=\"color:#E1E4E8\">(hash).<\/span><span style=\"color:#B392F0\">toString<\/span><span style=\"color:#E1E4E8\">(<\/span><span style=\"color:#79B8FF\">36<\/span><span style=\"color:#E1E4E8\">).<\/span><span style=\"color:#B392F0\">slice<\/span><span style=\"color:#E1E4E8\">(<\/span><span style=\"color:#79B8FF\">0<\/span><span style=\"color:#E1E4E8\">, <\/span><span style=\"color:#79B8FF\">6<\/span><span style=\"color:#E1E4E8\">);<\/span><\/span>\n<span class=\"line\"><span style=\"color:#F97583\">  return<\/span><span style=\"color:#E1E4E8\"> {<\/span><\/span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">    user: <\/span><span style=\"color:#9ECBFF\">'bd_'<\/span><span style=\"color:#F97583\"> +<\/span><span style=\"color:#E1E4E8\"> suffix,<\/span><\/span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">    pass: <\/span><span style=\"color:#9ECBFF\">'Bd@26!'<\/span><span style=\"color:#F97583\"> +<\/span><span style=\"color:#E1E4E8\"> suffix <\/span><span style=\"color:#F97583\">+<\/span><span style=\"color:#9ECBFF\"> 'x'<\/span><\/span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">  };<\/span><\/span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">}<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>Como as credenciais s\u00e3o previs\u00edveis, os atacantes n\u00e3o precisam de manter uma lista central de sites comprometidos: podem calcul\u00e1-las quando conhecem o dom\u00ednio. O script est\u00e1 a criar a conta pela p\u00e1gina <code>\/wp-admin\/user-new.php<\/code>, extrai o nonce da p\u00e1gina, usa a fun\u00e7\u00e3o de logout encontrada no HTML e guarda uma flag em <code>localStorage<\/code> quando consegue concluir a tentativa. Depois est\u00e1 a enviar para o C2 o estado, o utilizador, a palavra-passe, a origem, o URL de logout e o URL da p\u00e1gina, recorrendo a <code>sendBeacon<\/code> ou a <code>fetch<\/code> com <code>keepalive<\/code> como alternativa.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Indicadores de comprometimento<\/h2>\n\n\n\n<p>A investiga\u00e7\u00e3o deve cruzar os indicadores de rede, sistema de ficheiros e base de dados. Os endpoints Sigmative contaminados est\u00e3o a devolver JSON limpo, mas os artefactos que o payload instalou podem continuar presentes e devem procurar-se ativamente.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Recursos externos<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n\n<li>Payload principal: <code>ia-cdn[.]com\/fz\/w2.js<\/code><\/li>\n\n\n<li>Endpoint de beacon C2: <code>ia-cdn[.]com\/fz\/c<\/code><\/li>\n\n\n<li>Endpoint contaminado, j\u00e1 limpo: <code>api[.]sigmative[.]io\/prod\/store\/api\/biggopti\/api-data-all-records<\/code><\/li>\n\n\n<li>Endpoint contaminado, j\u00e1 limpo: <code>api[.]sigmative[.]io\/prod\/store\/api\/biggopti\/api-data-records<\/code><\/li>\n\n\n<li>Payload alternativo, j\u00e1 limpo: <code>api[.]sigmative[.]io\/prod\/store\/api\/biggopti\/x.js<\/code><\/li>\n\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Ficheiros e op\u00e7\u00f5es da base de dados<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n\n<li>Webshell <code>emer-run.php<\/code>, MD5 <code>1024732009983dd5e54b4cf5593f04d4<\/code>.<\/li>\n\n\n<li>Backdoor MU <code>class-wp-token-validate.php<\/code>, MD5 <code>7719cd98a35ffad2771f26d1ceab7d27<\/code>.<\/li>\n\n\n<li>M\u00f3dulo furtivo MU <code>class-wp-query-9d127ff3.php<\/code>, ou nome semelhante no padr\u00e3o <code>class-wp-query-*.php<\/code>, MD5 <code>9aadc3e5c5242b273bd17c5bdc358845<\/code>.<\/li>\n\n\n<li>M\u00f3dulo MU apresentado como verifica\u00e7\u00e3o de sa\u00fade, <code>wp-cache-optimizer.php<\/code>, MD5 <code>e450ae5bc4bfc0d960dded06a76bb8e9<\/code>.<\/li>\n\n\n<li>Op\u00e7\u00e3o <code>fz_emer_login_tokens<\/code>, onde se guardam tokens de magic login.<\/li>\n\n\n<li>Op\u00e7\u00e3o <code>fz_emer_done_v1<\/code>, que assinala a conclus\u00e3o do comprometimento.<\/li>\n\n\n<li>Contas com endere\u00e7os <code>@wordpress.org<\/code> ou <code>@developer.wordpress.org<\/code>.<\/li>\n\n\n<li>Nomes de utilizador que correspondam a <code>bd_<\/code> mais seis caracteres alfanum\u00e9ricos.<\/li>\n\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Cronologia do incidente<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n\n<li><strong>1 de mar\u00e7o de 2026:<\/strong> o JavaScript da API Biggopti entra no Prime Slider 4.1.9, no SVN <code>r3471891<\/code>. O <code>display_id<\/code> fica sem escape no atributo <code>id<\/code> e o endpoint usado \u00e9 <code>api-data-records<\/code>.<\/li>\n\n\n<li><strong>10 de maio de 2026:<\/strong> o endpoint muda para <code>api-data-all-records<\/code>. Adiciona-se um sanitizador <code>DOMParser<\/code> ao campo de conte\u00fado, mas o atributo <code>id<\/code> continua vulner\u00e1vel.<\/li>\n\n\n<li><strong>23 de junho de 2026:<\/strong> data <code>start_date<\/code> da campanha contaminada, num aviso \u201cSummer Sale\u201d; \u00e9 a data mais antiga em que a XSS pode ter estado ativa.<\/li>\n\n\n<li><strong>6 de agosto de 2026, 22:40 UTC:<\/strong> \u00faltimo <code>Last-Modified<\/code> observado em <code>api-data-records<\/code> contaminado.<\/li>\n\n\n<li><strong>7 de agosto de 2026, 09:11 UTC:<\/strong> \u00faltimo <code>Last-Modified<\/code> observado em <code>api-data-all-records<\/code> contaminado.<\/li>\n\n\n<li><strong>7 de agosto de 2026:<\/strong> o ataque \u00e9 observado em ambiente real e os plugins s\u00e3o fechados no diret\u00f3rio do WordPress enquanto aguardam revis\u00e3o.<\/li>\n\n\n<li><strong>7 de agosto de 2026, 16:56 UTC:<\/strong> s\u00e3o recolhidos artefactos da investiga\u00e7\u00e3o.<\/li>\n\n\n<li><strong>8 de agosto de 2026:<\/strong> ambos os endpoints da API passam a devolver JSON limpo.<\/li>\n\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">O que deves verificar e fazer agora<\/h2>\n\n\n\n<p>Desativa os plugins afetados e impede novos carregamentos do painel por utilizadores n\u00e3o estritamente necess\u00e1rios enquanto estiveres a analisar o ambiente. A limpeza est\u00e1 a exigir mais do que reinstalar plugins, porque a cadeia deixa altera\u00e7\u00f5es na base de dados, em contas de utilizadores e na diretoria de MU-plugins; verifica tamb\u00e9m os registos HTTP e DNS para liga\u00e7\u00f5es aos indicadores externos.<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n\n<li>Audita manualmente a lista de utilizadores, incluindo diretamente na base de dados, para encontrar administradores <code>bd_<\/code> com seis caracteres, contas com os dom\u00ednios indicados e contas inesperadas que a interface possa ocultar.<\/li>\n\n\n<li>Inspeciona <code>wp-content\/mu-plugins\/<\/code>, as diretorias de plugins e os ficheiros acess\u00edveis por URL para os nomes, padr\u00f5es e hashes indicados. Remove os artefactos indevidos apenas depois de preservar evid\u00eancia e de garantir que tens uma c\u00f3pia de seguran\u00e7a.<\/li>\n\n\n<li>Consulta a tabela de op\u00e7\u00f5es para <code>fz_emer_login_tokens<\/code> e <code>fz_emer_done_v1<\/code>, bem como outros dados inesperados relacionados com o incidente.<\/li>\n\n\n<li>Revoga sess\u00f5es, nonces e palavras-passe potencialmente expostos; redefine as credenciais de todos os administradores e substitui as chaves e salts do WordPress para invalidar cookies autenticados.<\/li>\n\n\n<li>Roda uma an\u00e1lise de malware que cubra o sistema de ficheiros, a base de dados e os indicadores de rede. Uma verifica\u00e7\u00e3o que compare apenas ficheiros dos plugins pode n\u00e3o encontrar esta infe\u00e7\u00e3o.<\/li>\n\n\n<li>Rev\u00ea as credenciais e os controlos de acesso da infraestrutura associada aos plugins se gerires uma instala\u00e7\u00e3o BdThemes, porque o incidente indica comprometimento grave de credenciais de armazenamento cloud ou da infraestrutura interna.<\/li>\n\n<\/ol>\n\n\n\n<p>Esta campanha est\u00e1 a demonstrar que um feed remoto aparentemente inofensivo pode tornar-se um vetor de execu\u00e7\u00e3o remota quando o c\u00f3digo de apresenta\u00e7\u00e3o confia nos dados recebidos. Depois de eliminar os indicadores, mant\u00e9m a monitoriza\u00e7\u00e3o de contas administrativas, pedidos a endpoints suspeitos e altera\u00e7\u00f5es em MU-plugins, pois \u00e9 essa persist\u00eancia que pode devolver o controlo do site ao atacante.<\/p>\n\n\n\n<div class=\"wp-block-group callout callout-info is-style-info is-layout-flow wp-block-group-is-layout-flow\" style=\"border-width:1px;border-radius:8px;padding-top:1rem;padding-right:1.5rem;padding-bottom:1rem;padding-left:1.5rem\">\n\n<h4 class=\"wp-block-heading callout-title\">Cobertura de dete\u00e7\u00e3o<\/h4>\n\n\n<p>As assinaturas de malware e regras de WAF para este comprometimento foram disponibilizadas em 7 de agosto de 2026 aos utilizadores Wordfence Premium, Care e Response e a clientes pagos do Wordfence CLI. As edi\u00e7\u00f5es gratuitas do Wordfence e Wordfence CLI recebem-nas ap\u00f3s o atraso habitual de 30 dias.<\/p>\n\n<\/div>\n\n\n<div class=\"references-section\">\n                <h2>Refer\u00eancias \/ Fontes<\/h2>\n                <ul class=\"references-list\"><li><a href=\"https:\/\/www.wordfence.com\/blog\/2026\/08\/psa-supply-chain-compromise-in-bdthemes-ecosystem-via-poisoned-api-response\/\" target=\"_blank\" rel=\"noopener noreferrer\">PSA: Supply Chain Compromise in BdThemes Ecosystem via Poisoned API Response<\/a><\/li><li><a href=\"https:\/\/www.wordfence.com\/threat-intel\/vulnerabilities\/id\/7bf1d6f9-afe2-41c2-968b-20dbc474cd73\" target=\"_blank\" rel=\"noopener noreferrer\">Biggopti Library (Various Versions) &#8211; Cross-Site Scripting via display_id from Sigmative API<\/a><\/li><li><a href=\"https:\/\/www.wordfence.com\/blog\/2026\/07\/wordfence-prism-detected-backdoored-wordpress-plugin-within-two-hours-of-it-being-introduced\/\" target=\"_blank\" rel=\"noopener noreferrer\">Wordfence PRISM Detected Backdoored WordPress Plugin Within Two Hours of It Being Introduced<\/a><\/li><li><a href=\"https:\/\/optinmonster.com\/security-incident-tampered-script-served-via-optinmonster-and-trustpulse\/#what-to-check-and-do\" target=\"_blank\" rel=\"noopener noreferrer\">Security Incident: Tampered Script Served via OptinMonster and TrustPulse<\/a><\/li><\/ul>\n            <\/div>","protected":false},"excerpt":{"rendered":"<p>Um feed JSON de banners promocionais foi suficiente para comprometer administradores WordPress que usavam plugins BdThemes. V\u00ea os plugins afetados, os indicadores e os passos de resposta.<\/p>\n","protected":false},"author":29,"featured_media":278,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[9],"tags":[128,48,11,10,119],"class_list":["post-279","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-seguranca","tag-cadeia-de-fornecimento","tag-malware","tag-seguranca","tag-wordpress","tag-xss"],"_links":{"self":[{"href":"https:\/\/helloblog.io\/pt\/wp-json\/wp\/v2\/posts\/279","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/helloblog.io\/pt\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/helloblog.io\/pt\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/helloblog.io\/pt\/wp-json\/wp\/v2\/users\/29"}],"replies":[{"embeddable":true,"href":"https:\/\/helloblog.io\/pt\/wp-json\/wp\/v2\/comments?post=279"}],"version-history":[{"count":0,"href":"https:\/\/helloblog.io\/pt\/wp-json\/wp\/v2\/posts\/279\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/helloblog.io\/pt\/wp-json\/wp\/v2\/media\/278"}],"wp:attachment":[{"href":"https:\/\/helloblog.io\/pt\/wp-json\/wp\/v2\/media?parent=279"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/helloblog.io\/pt\/wp-json\/wp\/v2\/categories?post=279"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/helloblog.io\/pt\/wp-json\/wp\/v2\/tags?post=279"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}