Zavolat Další kontakty >
E-mail Kontakty

Lynt na zimních konferencích: 16.2. Jakub Kašparů na PPC Masters v Berlíně, 18.2. Vláďa Smitka na WordCampu v Praze

Masivní únorová infekce WordPress webů 2017

Novinkou WordPress verze 4.7 je REST API, které je již součástí jádra a je v základu zapnuté. Tato nová funkcionalita, která může velmi podstatně změnit budoucnost WP, je však stále ještě čerstvá a objevují se v ní chyby. Nevhodnou kontrolou oprávnění tak ve verzích 4.7 a 4.7.1 mohl kdokoliv modifikovat obsah příspěvků.

Zdá se, že únor je pro WordPress nešťastný měsíc. V loňském roce se rozšířila infekce, která využila bezpečnostních problémů mnoha pluginů, letos zde máme velmi závažnou zranitelnost přímo v jádře WP - konkrétně v novince REST API.

REST API je speciální rozhraní, které umožňuje "strojově" komunikovat přímo s jádrem WP. To lze využít v několika případech. Je především možné vytvářet mnohem dynamičtější šablony, kde pro získání obsahu není třeba překreslovat stránku a web je tak mnohem rychlejší. Dále je mnohem jednodušší integrovat WP do jiných systémů, se kterými může obousměrně komunikovat. Je tak možné vytvářet například vlastní aplikace pro správu obsahu - jako je například Calypso na WP.com. Tato technologie přináší mnoho nových možností, nicméně v začátcích jsou s ním spojené i různé problémy.

REST API bylo dlouho  ve formě externího pluginu, od verze 4.7 se však dostalo přímo do jádra a je v základu aktivní - přesvědčit se o tom můžete pokud na svém webu navštívíte cestu /wp-json/wp/v2.

WordPress verze 4.7 a 4.7.1 obsahuje velmi závažnou chybu, kdy nedokonalou kontrolou oprávnění bylo možné přes REST API modifikovat příspěvky bez oprávnění - posláním speciálního POST požadavku tak mohl kdokoliv změnit obsah libovolného příspěvku/stránky. Chyba byla opravena ve verzi 4.7.2 a podrobné detaily byly zveřejněny až několik dní po opravě. Jádro WP v základním nastavení (a pokud jsou správně nastaveny oprávnění) samo instaluje minoritní patche, takže webů se automaticky aktualizovala na opravenou verzi sama ještě před masivními pokusy o zneužití této chyby. Automatické aktualizace však spouští WP-Cron, který potřebuje, aby web někdo navštívil - málo navštěvované weby tak mohou zůstat poměrně dlouhou dobu bez bezpečnostní opravy. K problémům s aktualizacemi jsou také náchylné weby, které jsou kompletně verzované a je u nich nutné ručně publikovat novou verzi - správci těchto webů by měli velmi obezřetně sledovat dění kolem bezpečnosti tohoto redakčního systému, aby mohli v případě problémů co nejdříve zakročit.

Chyba byla způsobena nevhodnou kontrolou oprávnění k modifikaci příspěvku - jednoduše řečeno, bylo možné přelstít ověřovací mechanismus GET parametrem s ID neexistujícího příspěvku, který tedy žádná omezení nemá - příspěvek s ID=123ABC neexistuje, omezení nejsou aplikována, ale po extrakci celého čísla v dalším kód zbylo již existující ID=123. Podrobně byl problém popsán na blogu Sucuri.

Aktuální útoky mají na svědomí především automatické skripty, které najdou na webu ID nejnovějšího příspěvku (buď opět z REST API, nebo třeba z RSS) a tento příspěvek pozmění. V logu tak naleznete většinou něco podobného:

http://example.com/index.php?rest_route=/wp/v2/posts/1&id=1XXX&content=TEXT
http://example.com/wp-json/wp/v2/posts/1&id=1XXX&content=TEXT

Díky tomu, že je útok prováděn standardní cestou, lze pozměněné příspěvky poměrně snadno dohledat - např. SQL dotazem, který najde všechny změny od vydání patche:

SELECT ID, post_title, post_content FROM wp_posts WHERE post_modified_gmt > '2017-01-26';

Pokud máte zapnuté revize, lze se tak jednoduše vrátit k původní verzi obsahu.

Útočníci v naprosté většině případů pouze vkládali svůj text "Hacked By ...", není však možné vyloučit mnohem závažnější dopady. Jedním z možných problémů může být vložení javascriptu a tím provedením XSS - naštěstí je obsah vkládaný přes REST API sanitizován jako v případě komentářů, ale z minulosti víme, že se tuto kontrolu občas podaří překonat. Případné vložení javascripotvého kódu může mít dalekosáhlé důsledky především v kombinaci s jinými zranitelnostmi. Napadnutelné jsou v určitých případech i shortkódy různých pluginů, přes které lze vkládat neočekávané atributy, nebo přímo vykonávat PHP kód (např.  Insert PHP, Exec-PHP), nicméně se opět jedná spíše o minoritní problém.

Můžeme si položit otázku, zda REST API potřebujeme. Oficiálně vypnout nelze, protože bude stále více využívané v administraci a začínají se objevovat první šablony, které jsou na něm založené. Pokud takovou šablonu však nepoužíváte, je možné API zpřístupnit pouze přihlášeným uživatelům. To lze učinit například následujícím snippetem kódu:

function lynt_disable_public_rest_api( $allow ) {
    if( !is_user_logged_in() ) {
        return new WP_Error( 'rest_cannot_access', 'Not allowed' , array( 'status' => 403 ) );
    }
    return $allow;
}
add_filter( 'rest_authentication_errors', 'lynt_disable_public_rest_api' );

Drastičtější metodou je jeho zakázání pomocí .htaccess, to však může mít dopady na funkce webu. 

RewriteCond %{QUERY_STRING} wp-json/wp [NC,OR]
RewriteCond %{REQUEST_URI} wp-json/wp [NC]
RewriteRule .* - [F]

REST API obsahuje i další nástrahu, se kterou je třeba počítat. Poskytuje jednoduchý způsob, jak získat uživatelská jména a md5 hashe e-mailů uživatelů (je možné je hrubou silou dekódovat), kteří na webu vytvořili nějaký obsah. Tyto informace se skrývají pod endpointem /wp-json/wp/v2/users. Ve výstupu naleznete uživatelská jména (hodnoty slug a v rámci odkazu na profil link) a v základním nastavení i cestu ke gravataru (avatar_urls), která obsahuje hash e-mailu. Zjištění těchto informací nemusí představovat přímé riziko a je možné se k nim často dostat i jiným způsobem, nicméně mohou případnému útočníkovi pomoci zasadit další dílek do skládanky, jak váš na web funguje.

Pokud tyto informace nechcete veřejně zobrazovat, je možné použít následující snippet kódu pro jejich odstranění z odpovědi v REST API:

function lynt_remove_sensitive_data_from_rest( $response ) {
   if(!current_user_can('list_users')){
     //get WP_REST_Response
     $data = $response->get_data();
     //unset sensitive fields
     unset($data['link']);
     unset($data['slug']);
     unset($data['avatar_urls']);
     //set data back
     $response->set_data($data);
   }
   return $response;
}

add_filter( 'rest_prepare_user', 'lynt_remove_sensitive_data_from_rest');

Vraťme se k aktuálnímu problému. Bylo napadeno odhadem několik desítek tisíc webů, které nestihly aplikovat patch s opravou. Průzkum ukázal, že šlo především o menší méně navštěvované weby a na straně druhé o větší weby, které jsou kompletně ve verzovacím systému. U nás WP weby neverzujeme celé, ale pouze šablonu a případné pluginy - celý web je tak složen z několika repozitářů. Je to maličko komplikovanější, ale můžeme tak nechat samotné jádro žít vlastním životem, díky čemu je stále aktualizované. Tento postup má i nevýhodu, že případný automatický update může něco rozbít. Pokud se tak stane, tak se o tom však často velmi brzo dozvíme, protože své weby stále monitorujeme a v případě problémů nám přijde notifikace. Monitorování má i další pozitivní efekt - na web je stále přistupováno, takže dochází k pravidelným spouštěním WP-Cron, nestane se tedy, že aktualizace nebude aplikována proto, že na web zrovna nikdo nepřišel. Pro monitorování používáme komplexní systém Zabbix, mohu však vřele doporučit i službu Uptime Robot, která je do 50 stránek a intervalem kontroly 5 minut zdarma. Pro správu aktualizací využíváme Main WP, díky kterému je i aktualizace pluginů hračkou. Podobný systém bych doporučil každému, kdo spravuje více než 2 WP weby.

Na verzi 4.7 jsme z důvodů nejistot s REST API zatím s našimi zákazníky zatím kompletně nepřešli.

Zajímalo mě, jak problém ve verzích 4.7 a 4.7.1 ovlivňuje české prostředí. V roce 2015 jsem prováděl velký výzkum českých WordPress webů a od té doby mám vybudovanou databázi téměř 80 000 WordPressů. Za 2 roky jich přestalo zhruba 15 tisíc fungovat, nicméně nové stále doplňuji.

Proskenoval jsem tyto weby a hledal na nich známky nákazy. Kontrolou prošlo celkem 62 999 webů, u kterých bylo možné s jistotou rozpoznat, že se jedná o WordPress.

Evidentně napadených bylo 311, to je 0,5 % českých WordPress webů.

Potenciálně napadnutelných je však 9x více - téměř 2800 (4,5 %) webů používá WP se zranitelností v REST API.

Celkový přehled jednotlivých verzí WP:

Podrobnější přehled minor verzí 4.7:

Z podrobnějšího zkoumání jednotlivých verzí vychází, že 1/3 českých WP webů nemá aplikovány poslední bezpečnostní záplaty (a pro 1/6 již záplaty ani nevycházejí).

Výsledky ukazují, že aktuální verze WP jsou nasazeny na většině webů, nicméně velké o velkému množství stránek chybí zodpovědný správce.

Pokud byl váš web napaden, tak se pro opravu stačí ujistit, že používáte aktuální verzi 4.7.2 a změněné příspěvky vrátit do původního stavu z revizí nebo ze záloh. Preventivně však můžete podniknout obecné kroky po útocích - ideálně smazat celý web a vytvořit čerstvý ze zálohy, vše aktualizovat, změnit šifrovací klíče ve wp-config.php, změnit hesla uživatelů a heslo do databáze.

Pokud vás bezpečnost WP zajímá, podívejte se na mé prezentace na toto téma a určitě přijďte 18.2. na WordCamp Praha! I já zde budu mít svou přednášku ;-)

Komentáře

Přidat komentář

K článku prozatím nebyly přidány žádné komentáře.