Добрый день, уважаемые Хабраюзеры!
Данная статья является логическим продолжением этой статьи. В одном из комментариев к ней, оставленном юзером Agel_Nash была указана новая сигнатура вируса. Файлы подверженные атаке — *.js.
Сразу несколько сайтов наших клиентов, которые не последовали нашим рекомендациям сменить доступы фтп, почистить свои машины от зловредов и сменить фтп клиенты, подверглись заражению данным вирусом. В файл *.js прописывается следующий код: pastebin.com/2PWJycAd. Размещается он в одну строку и строго в конец файла.
С помощью нехитрых манипуляций код был деобфусцирован. Для тех кому интересно, выкладываю читабельный вид: данного кода (подобный код уже указывал MrMYSTIC).
Деобфускацию проводил простешим способом. Зашёл на jsfiddle и вместо eval вывел на страницу генерируемый код. Делает он несложные манипуляции, а именно следующее: Генерирует путь до файла *.js используя url = 305yoy.bdcfwpndqm.is-a-therapist.com/g/, после чего собирает элемент script с полученным путём и добавляет его в head страницы, на которой отработал.
Для очистки сайтов я использовал сканер, про который я писал в прошлой статье. Однако решил немного его «причесать», учитывая замечания в комментариях. Отдельное спасибо за конструктивную критику charon и pro100tak (поставил бы + если бы мог голосовать).
Алгоритм старый, работает построчно в файлах. Поэтому нужно было выявить, за что зацепиться сканеру.
Взял за основу указанный Agel_Nash, однако сканер показал неожиданно небольшое количество заражённых файлов. Странно, ведь пусть и не тысячи, но сотни *.js файлов в проекте точно есть. Стал копать и верно, доверяй но проверяй. Выбранная сигнатура встречалась не везде в виду того, что она не конечна. Выборочно проверив несколько файлов в различной удалённости друг от друга дирректориях, была выявлена пара вариантов:
и
Разница небольшая и состоит в двух словах:
Предположительно, таким образом могут перебираться все типы данных: даты, числа, строки и прочее. Хорошо, значит берём оставшуюся часть строки, а именно:
На сей раз сканер показал все файлы, которые были заражены данным вирусом. То что нужно! Осталось проверить Jquery, fancybox и ещё несколько используемых библиотек. Чисто… Вхождений в их исходном коде не обнаружено. Отлично, можно использовать.
Немного переработал сканер и запустил. Он вновь отработал как и задумано, не подвёл.
Изменения:
— начал собирать список сигнатур (на данный момент их две, можно запускать сканер без параметра искомой строки);
— указывает какую из сигнатур обнаружил в файле;
— добавлена функция удаления бэкапов (delete_backups());
— вызов функций можно осуществлять без параметров (в таком случае используются заданные по умолчанию):
— добавлен функционал смены владельца директории на время работы с ней сканером, но этот участок я закомментировал. Не довелось протестировать:
— добавлена функция dir_content() которая собирает все файлы в массив, который в последствии используеся всеми остальными функциями;
— при сканировании игнорирует самого себя.
Привожу код обновлённого сканера, с подробными комментариями:
Ну и пример использования с базовыми параметрами поиска.
Вот собственно и всё, чем я хотел с Вами поделиться.
Чистых серверов Вам и стабильной их работы.
Бест регардс!
Данная статья является логическим продолжением этой статьи. В одном из комментариев к ней, оставленном юзером Agel_Nash была указана новая сигнатура вируса. Файлы подверженные атаке — *.js.
Сразу несколько сайтов наших клиентов, которые не последовали нашим рекомендациям сменить доступы фтп, почистить свои машины от зловредов и сменить фтп клиенты, подверглись заражению данным вирусом. В файл *.js прописывается следующий код: pastebin.com/2PWJycAd. Размещается он в одну строку и строго в конец файла.
С помощью нехитрых манипуляций код был деобфусцирован. Для тех кому интересно, выкладываю читабельный вид: данного кода (подобный код уже указывал MrMYSTIC).
Деобфускацию проводил простешим способом. Зашёл на jsfiddle и вместо eval вывел на страницу генерируемый код. Делает он несложные манипуляции, а именно следующее: Генерирует путь до файла *.js используя url = 305yoy.bdcfwpndqm.is-a-therapist.com/g/, после чего собирает элемент script с полученным путём и добавляет его в head страницы, на которой отработал.
Для очистки сайтов я использовал сканер, про который я писал в прошлой статье. Однако решил немного его «причесать», учитывая замечания в комментариях. Отдельное спасибо за конструктивную критику charon и pro100tak (поставил бы + если бы мог голосовать).
Алгоритм старый, работает построчно в файлах. Поэтому нужно было выявить, за что зацепиться сканеру.
Взял за основу указанный Agel_Nash, однако сканер показал неожиданно небольшое количество заражённых файлов. Странно, ведь пусть и не тысячи, но сотни *.js файлов в проекте точно есть. Стал копать и верно, доверяй но проверяй. Выбранная сигнатура встречалась не везде в виду того, что она не конечна. Выборочно проверив несколько файлов в различной удалённости друг от друга дирректориях, была выявлена пара вариантов:
)try{Boolean().prototype.q}catch(egewgsd){f=[
и
)try{Date().prototype.q}catch(egewgsd){f=[
Разница небольшая и состоит в двух словах:
Date() и Boolean()
Предположительно, таким образом могут перебираться все типы данных: даты, числа, строки и прочее. Хорошо, значит берём оставшуюся часть строки, а именно:
().prototype.q}catch(egewgsd){f=[
На сей раз сканер показал все файлы, которые были заражены данным вирусом. То что нужно! Осталось проверить Jquery, fancybox и ещё несколько используемых библиотек. Чисто… Вхождений в их исходном коде не обнаружено. Отлично, можно использовать.
Немного переработал сканер и запустил. Он вновь отработал как и задумано, не подвёл.
Изменения:
— начал собирать список сигнатур (на данный момент их две, можно запускать сканер без параметра искомой строки);
— указывает какую из сигнатур обнаружил в файле;
— добавлена функция удаления бэкапов (delete_backups());
— вызов функций можно осуществлять без параметров (в таком случае используются заданные по умолчанию):
$dron->find(); $dron->scan(); $dron->restore_backups(); $dron->delete_backups();
— добавлен функционал смены владельца директории на время работы с ней сканером, но этот участок я закомментировал. Не довелось протестировать:
/* // получаем имя владельца директории $unformated_path_stat = stat($path); $path_stat = posix_getpwuid($unformated_path_stat['uid']); $path_user_name = $path_stat['name']; // меняем имя владельца директории chown($path, 'www'); */ ... // восстанавливаем имя владельца директории // chown($path, $path_user_name);
— добавлена функция dir_content() которая собирает все файлы в массив, который в последствии используеся всеми остальными функциями;
— при сканировании игнорирует самого себя.
Привожу код обновлённого сканера, с подробными комментариями:
<? /* ---------------------------------------------------------------------------------- dScaner Class - START ---------------------------------------------------------------------------------- */ /** * @param Имя: dScaner * @param Описание: Класс для сканирования директорий на наличие вредоносного кода в * указанных типах файлов * * @param Разработчик: Денис Ушаков * @param Версия разработки: 0.0.5 (13-04-2012) * */ Class dScaner { // список файлов private $arr_files = array(); // список сигнатур для поиска public $signatures = array( '=Array.prototype.slice.call(arguments).join(""),', '().prototype.q}catch(egewgsd){f=[' ); /** * Преобразуем входной параметр в массив * * @param string $get_str Список параметров * @param string $separator Разделитель параметров в списке * @return array - массив параметров или FALSE */ function request($get_str, $separator) { if (!empty($get_str)) { // эксплоадим строку в массив и возвращаем его $obj = explode($separator, $get_str); return $obj; } else { return false; } } /** * Функция получения списка файлов указанного расширения * * @param string $path - путь до директории, от которой отталкиваться при сканировании * @param string $files_allowed - список файлов, которые подвергаются сканированию */ function dir_content($path = './', $files_allowed = '.') { // исключаемые ссылки на директории и файлы, которые будут игнорироваться $dir_disallow = array('.', '..', '.htaccess', '.git', 'zlordwaters'); if(is_dir($path)) { $temp = opendir($path); while (false !== ($dir = readdir($temp))) { if ((is_dir($path . $dir)) && (!in_array($dir, $dir_disallow)) ) { // если директория - сканируем её $sub_dir = $path . $dir . '/'; $this->dir_content($sub_dir, $files_allowed); } elseif ((is_file($path . $dir)) && (!in_array($dir, $dir_disallow)) && (strpos($dir, $files_allowed) == true) && (strpos($dir, '_BACKUP') == false) && (strpos($dir, trim($_SERVER['SCRIPT_NAME'], '/')) === false) ) { // Если файл, то собираем массив файлов $this->arr_files[] = $path . $dir; } } closedir($temp); } } /** * Функция поиска в файлах вхождения заданной строки: * * @param string $path - путь до директории, от которой отталкиваться при сканировании * @param string $files_allowed - список файлов, которые подвергаются сканированию * @param string $requested_string - строка поиска */ function find($path = './', $files_allowed = '.', $requested_string = '().prototype.q}catch(egewgsd){f=[') { // получаем список файлов нужного расширения $this->dir_content($path, $files_allowed); foreach($this->arr_files AS $in_dir_file) { // считываем файл в строку $temporary_file = file_get_contents($in_dir_file); // флаг найденного вхождения искомой строки $file_founded = false; // разбиваем файл на строки $tf_strings = explode("\n", $temporary_file); // обрабатываем каждую отдельно foreach ($tf_strings AS $item) { // проверяем на заданную строку $item = strval($item); if (strpos($item, $requested_string) !== false) { $file_founded = true; $founded_str = $requested_string; } // проверяем по имеющимся сигнатурам foreach ($this->signatures AS $signa) { $signa = strval($signa); if (strpos($item, $signa) !== false) { $file_founded = true; $founded_str = $signa; } } } // если в файле найдена строка if ($file_founded) { // выводим путь до файла в котором найдено вхождение print "<span style='display:block; padding:5px; border:1px solid #1f4f18; background-color:#d5f5ce; font-size:12px; line-height:16px; font-family:tahoma, sans-serif; margin-bottom:-15px;'><h3>" . $in_dir_file . "</h3> В файле обнаружена искомая строка. <br> Cигнатура: <b>" . $founded_str . "</b> </span><br>"; } } } /** * Функция сканирования вредоносного кода: * * @param string $path - путь до директории, от которой отталкиваться при сканировании * @param string $files_allowed - список файлов, которые подвергаются сканированию * @param string $requested_string - строка, по которой определяется наличие вредоносного кода */ function scan($path = './', $files_allowed = '.', $requested_string = '().prototype.q}catch(egewgsd){f=[') { // получаем список файлов нужного расширения $this->dir_content($path, $files_allowed); foreach($this->arr_files AS $in_dir_file) { // считываем файл в строку $temporary_file = file_get_contents($in_dir_file); // флаг бекапа файла $create_backup = false; // разбиваем файл на строки и считываем каждую отдельно $tf_strings = explode("\n", $temporary_file); // индекс строки файла $str_index = 0; // каждую строку обрабатываем отдельно foreach ($tf_strings AS $item) { // проверяем на заданную строку $item = strval($item); if (strpos($item, $requested_string) !== false) { // если в строке есть вхождения искомого запроса // флаг бекапа файла, в котором найден вредоносный код $create_backup = true; // удаляем всю строку с вредоносным кодом unset($tf_strings[$str_index]); $founded_str = $requested_string; } // проверяем по имеющимся сигнатурам foreach ($this->signatures AS $signa) { $signa = strval($signa); if (strpos($item, $signa) !== false) { // если в строке есть вхождения искомого запроса // флаг бекапа файла, в котором найден вредоносный код $create_backup = true; // удаляем всю строку с вредоносным кодом unset($tf_strings[$str_index]); $founded_str = $signa; } } // переходим на следующую строку $str_index++; } // создаём бэкап if ($create_backup) { /* // получаем имя владельца директории $unformated_path_stat = stat($path); $path_stat = posix_getpwuid($unformated_path_stat['uid']); $path_user_name = $path_stat['name']; // меняем имя владельца директории chown($path, 'www'); */ // меняем права в папке в которой находимся чтобы иметь возможность писать в неё chmod($path, 0777); // формируем имя БЭКАПа файла $temp_file_backup = $in_dir_file.'_BACKUP'; // сохраняем БЭКАП файла рядом с исходным file_put_contents($temp_file_backup, $temporary_file); // собираем очищенный файл в строку $scanned_file = implode("\n", $tf_strings); // сохраняем очищенный файл if (file_put_contents($in_dir_file, $scanned_file)) { // перезаписали удачно print "<span style='display:block; padding:5px; border:1px solid #1f4f18; background-color:#d5f5ce; font-size:12px; line-height:16px; font-family:tahoma, sans-serif; margin-bottom:-15px;'><h3>" . $in_dir_file . "</h3> Файл очищен. (+ BACKUP) <br> Cигнатура: <b>" . $founded_str . "</b> </span><br>"; } else { // перезапись не удалась print "<span style='display:block; padding:5px; border:1px solid #822121; background-color:#ea7575; font-size:12px; line-height:16px; font-family:tahoma, sans-serif; margin-bottom:-15px;'><h3>" . $in_dir_file . "</h3> Файл НЕ очищен. Cигнатура: <b>" . $founded_str . "</b> </span><br>"; } // меняем права в папке в которой находимся обратно на 755 chmod($path, 0755); // восстанавливаем имя владельца директории // chown($path, $path_user_name); } } } /** * Функция восстановления БЭКАПОВ файлов * * @param string $path - путь до директории, от которой отталкиваться при восстановлении * @param string $files_allowed - список файлов, которые подвергаются восстановлению */ function restore_backups($path = './', $files_allowed = '.') { // получаем список файлов нужного расширения $this->dir_content($path, $files_allowed); foreach($this->arr_files AS $in_dir_file) { if (is_file($in_dir_file.'_BACKUP')) { // БЭКАП существует, получаем его содержимое $temporary_file_from_backup = file_get_contents($in_dir_file.'_BACKUP'); // восстанавливаем бэкап файла if (file_put_contents($in_dir_file, $temporary_file_from_backup)) { // удаляем бэкап unlink($_SERVER['DOCUMENT_ROOT'].'/'.$in_dir_file.'_BACKUP'); // бэкап восстановили print "<span style='display:block; padding:5px; border:1px solid #1f4f18; background-color:#d5f5ce; font-size:12px; line-height:16px; font-family:tahoma, sans-serif; margin-bottom:-15px;'><h3>".$in_dir_file ."</h3> Восстановлен. </span><br>"; } else { // бэкап НЕ восстановили print "<span style='display:block; padding:5px; border:1px solid #822121; background-color:#ea7575; font-size:12px; line-height:16px; font-family:tahoma, sans-serif; margin-bottom:-15px;'><h3>".$in_dir_file ."</h3> НЕ восстановлен. </span><br>"; } } } } /** * Функция удаления БЭКАПОВ файлов * * @param string $path - путь до директории, от которой отталкиваться при удалении бэкапов * @param string $files_allowed - список файлов, которые подвергаются удалению */ function delete_backups($path = './', $files_allowed = '.') { // получаем список файлов нужного расширения $this->dir_content($path, $files_allowed); foreach($this->arr_files AS $in_dir_file) { if (is_file($in_dir_file.'_BACKUP')) { // БЭКАП существует, удаляем его if (unlink($_SERVER['DOCUMENT_ROOT'].'/'.$in_dir_file.'_BACKUP')) { print " <span style='display:block; padding:5px; border:1px solid #1f4f18; background-color:#d5f5ce; font-size:12px; line-height:16px; font-family:tahoma, sans-serif; margin-bottom:-15px;'><h3>".$in_dir_file ."_BACKUP</h3> Удалён. </span><br>"; } else { // бэкап НЕ удалили print "<span style='display:block; padding:5px; border:1px solid #822121; background-color:#ea7575; font-size:12px; line-height:16px; font-family:tahoma, sans-serif; margin-bottom:-15px;'><h3>".$in_dir_file ."_BACKUP</h3> НЕ удалён. </span><br>"; } } } } } /* ---------------------------------------------------------------------------------- dScaner Class - END ---------------------------------------------------------------------------------- */ ?>
Ну и пример использования с базовыми параметрами поиска.
<? // создаём экземпляр сканера - Дрон $dron = new dScaner; // поиск по файлам из текущей директории $dron->find('./', '.'); // очистка файлов // $dron->scan('./', '.'); // восстановление бэкапов файлов // $dron->restore_backups('./', '.'); // удаление бэкапов файлов // $dron->delete_backups('./', '.'); ?>
Вот собственно и всё, чем я хотел с Вами поделиться.
Чистых серверов Вам и стабильной их работы.
Бест регардс!
