Debian.pro/

Про Debian


Большой мануал: часть 24. Боремся с вирусней на сайтах.

Эта статья — часть Большого Мануала по настройке lamp-сервера на debian.

Предыдущая часть цикла — Делаем бэкапы.

Следующая часть цикла — Логгируем вызовы mail() из php.


Дисклеймер: статья очень нужная, но мне пришлось буквально выдавливать её из себя, вышло совсем не интересно. У меня не очень много опыта в чистке от вирусни (обычно есть бэкапы/CVS или проще раскатить сайт из дистрибутива заново), поэтому про собственно чистку ничего не написано — только про поиск.
Поэтому буду весьма рад дополнениям в комментариях, а тем более и какому-то описанию «по горячим следам».

Речь пойдет, само собой, не про типичную виндовую вирусню в .exe-файлах (или как оно у вас там деплоится теперь), а про те скрипты, которые заливают на взломанные PHP-сайты.
Скрипты эти обычно занимаются какой-то гадостью вида рассылки спама, всяческих сканирований и брутфорсов и так далее. Есть и те, которые вставляют всяческие спам-ссылки на страницах самого сайта. Есть php-шеллы (они, конечно, редко становятся единственным последствием взлома, через них ещё чего-то заливают частенько). Ну и вообще там много всего можно придумать. Общее у всех этих «вирусов» одно — обычный антивирусник ничего с ними делать не будет. Ну а что антивируснику делать со скриптом, который просто шлет почту по списку ящиков?
Вообще работа по чистке взломанного сайта — она не то, чтобы хорошая тема для мануала. Нельзя в одной небольшой статье объяснить, как почистить любую CMS (а уже тем более ещё и всякое самописное), что делать если то, что делать если это. Я постараюсь рассказать, как можно искать взломанные файлы (само собой, все способы рассказать не получится). Что с ними делать — вопрос отдельный, решается в каждом случае индивидуально. Опять же — дальше я перечисляю далеко не все варианты. Есть бэкап до взлома — отлично, залили бэкап, долили новые файлы, работаем. Сайт сделан на популярной CMS с минимальными изменениями? Отлично, взяли дамп, развернули CMS с нуля из дистрибутива, доставили плагинов, защитили сайт от взлома получше (почитали про уязвимости, обновили CMS, удалили уязвимые плагины — как минимум) и работаем дальше. Самописный сайт, бэкапов нет, CVS нет, ничего нет — печаль, работаем с каждым взломанным файлом персонально.

Многие, конечно, сейчас сразу закричат «что ж ты за админ такой, что у тебя сайты взламывают!». Первые несколько взломов я тоже охуевал и пытался понять, что со мной не так. Расслабьтесь. Редко когда причина будет в настройках сервера. Если вы сами занимаетесь каждым сайтом, редактируете все файлики сами, доступов к админке CMS ни у кого нет, файлового доступа к сайту ни у кого нет — это ещё можно рассуждать о том, что во взломе виноваты вы. Можно и анально обложить всяческими библиотеками и запретами (об этом может будет ещё статья), но оно подойдет только для какого-то простенького блога вроде этого, где только текст, хранящийся в базе (если обратите внимание — у меня даже все картинки залиты по sftp, а не штатной админкой WP). Можно, да.

Но из всех взломов сайтов, с которыми я сталкивался (как на своих системах, так и просто «по горячим следам») только один был «на уровне системы». Через proftpd с дыркой CVE-2015-3306. Откуда там взялся proftpd — отдельный вопрос, я то был уверен, что там vsftpd, ну да ладно. Так вот. В wordpress была интересная уязвимость, которую разработчики уязвимостью не считали. Если не вдаваться в подробности, то инсталлер wordpress позволял залить любой файл от имени вебсервера в докрут уже установленного wordpress’a. Когда счет взломов сайтов через эту дырку перевалил через сотню (!) я утомился и перестал их считать. Что тут какой-то один взлом через proftpd =). А ещё был уязвимость в tinymce, много уязвимостей в популярных темах (купленных, кстати, за деньги, у компании-автора). И это wordpress, вообще-то одна из лучших CMS в соотношении популярность/безопасность. Чего уж там говорить про joomla какую-то.

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

Раз уж заговорил про меры безопасности, напомню основные (помимо настроек веб-сервера):

  • правильные права на файлы — разрешайте CMS писать только в те каталоги, куда действительно необходимо писать. Например, в upload-директорию.
  • Не используйте сомнительные функции CMS, которые касаются изменений файлов, особенно если вы не пользуетесь ими постоянно. Например, установка плагинов через вебморду или редактирование шаблона CMS из админки. Наверняка плагины вы ставите раз в пятилетку — в остальное время запретите запись в каталог с плагинами
  • Запретите выполнение php-кода в upload-каталогах
  • закройте админку от доступов по ip или basic-auth (писал в статье про phpmyadmin об этом), если CMS позволяет (WP не позволяет закрыть /wp-admin, например).
  • следите за обновлениями CMS/плагинов, а лучше сразу за уязвимостями (не для всех сразу появляется фикс, чаще сначала появляется какой-нибудь хак, типа поменять пару строчек, сломав какую-нибудь функцию).

Ну и так далее. А теперь о том, с чего начинать, если всё же есть признаки взлома. Начать я предлагаю с запуска утилиты maldet. Она проста как табуретка в плане запуска. Из минусов — работает по базе сигнатур, эвристического анализа нет. Может, в принципе, подцепить при сканировании ещё и clamav. Установка в лучших практиках девопс — curl … | bash, и вот это вот всё. Но — и правда что-то находит. По крайней мере, достаточно быстрый и простой способ выяснить, был ли взлом на файловом уровне — дальше уже можно руками разбираться.

Ставится так. Сначала скачиваем-распаковываем-cd.

root@server:~# wget http://www.rfxn.com/downloads/maldetect-current.tar.gz; tar -zxvf maldetect-current.tar.gz; cd $(find . -iname "maldetect-*" -type d)

Потом запускаем «инсталлер»:

root@server:~# bash install.sh

Обновим базы и сам скрипт (сразу после установки бестолку, но на будущее пригодится):

root@server:~# maldet -u; maldet -d

Со всякими кронячками советую не заморачиваться — гонять такую вещь в фоне по крону не очень полезно, особенно если у вас в железке только одно ядро и сотня-другая тысяч файлов.
Начинаем сканирование каталога с сайтом. Пока только *.php (понятное дело, так намного быстрее, чем все файлы подряд). Из-за бага в maldet —include-regex скорее не работает, чем да, поэтому сделаем это через список файлов:

root@server:~# find /home -iname "*.php" > /tmp/filelist.txt

Запускаем сканирование по этому списку файлов:

root@server:~# maldet -f /tmp/filelist.txt

По результату сканирования maldet напишет что-то такое:

maldet(16823): {scan} scan report saved, to view run: maldet --report 180119-1943.16823

Предлагаю не заниматься чушью (то есть запуском команды maldet —report), а посмотреть в файл /usr/local/maldetect/sess/session.180119-1943.16823 (цифры у вас будут другие) любым удобным способом. Там будет написано, какие файлы показались maldet-у зараженными.
Само собой, вторым этапом уже можно запустить сканирование на все файлы (php-код можно положить в любой файл, просто вызвать напрямую не получится). Только делайте это лучше в скрине или вроде того. Путь до каталога с сайтами угадайте сами =)

root@server:~# maldet -a /home

Ну и есть смысл удалить кронячку maldet, если не собираетесь ею пользоваться (так-то вообще вещь полезная):

root@server:~# rm /etc/cron.daily/maldet

Следующий — clamav. Так как нам на «позырить один раз» — то clamav-daemon не очень пригодится, ставим только clamscan:

root@server:~# apt-get install clamav

Базы будут обновляться в фоне процессом freshclam (ну не могут они без этого!). Предлагаю убить его (всё равно ничего не обновляет, сволочь), и запустить для начала руками:

root@server:~# killall freshclam; freshclam

Запускаем сканирование:

root@server:~# clamscan --recursive=yes --infected /home

Сканирует долго, опять же — лучше в скрине. Потом на консоль напишет, что нашлось.
Можно интегрировать его с maldet, но мне всегда лень. Всё равно разное находят.

Ещё можно поговорить о полезных командах, которые позволяют анализировать ситуацию вручную.
Во-первых, это рекурсивный grep (и egrep по регуляркам если):

root@server:~# grep -rni what /where/dir/

Искать можно eval, base64. Можно поискать какие-то готовые конструкции в интернетах (в принципе, maldet как раз на этом и основан).
Во-вторых, команда stat. Пишем:

root@server:~# stat /path/to/file

Запоминаем, сколько дней назад файл менялся.
И ищем find-ом файлы, которые менялись примерно тогда же :

root@server:~# find /home -iname "*.php" -mtime +5 -mtime -10

Здесь, например, больше 5 дней (важно — именно больше, то есть 6 или больше) и меньше 10 дней (аналогично — 9 или меньше, а не 10 или меньше).
Есть опция -mmin (то же самое, но в минутах), с ней можно оперировать в пределах дня (можно и дальше, но умножать упаритесь).

Вот. С использованием всего этого уже можно получить информацию о том, взломан ли сайт. И как примерно взломан. А там уже лапками-лапками, к сожалению. А лучше пожелаю вам, чтобы вас не ломали =)
Ну а если заморочитесь и настроить работу maldet с clamav по крону — вообще молодцы.

19.01.2018 byinkvizitor68sl|big-manual

Комментарии (2):

  1. yukra :

    Добавлю 3 момента:
    1) mtime может быть легко изменен пользователем (вирусов), желательно смотреть на ctime. При этом изменение mtime влечет за собой изменение ctime, а ctime без рута (перевода часов или размонтирования ФС)
    2) У php есть параметры mail.add_x_header и mail.log, я предпочитаю держать их включенными, как минимум помогут оценить «масштаб трагедии» если поймаете рассылку спама.
    3) Иногда бывает такое, что какой-нить спамскрипт вы нашли, удалили, а он снова появляется через некоторое время. То есть изначальную причину вы не устарнили, в этом случае помогают логи (особенно логи POST-запросов) и/или демон auditd.
    Приведу пример POST-лога для nginx. В контекст http добавляем новый логформат и мапу:

    log_format post '$remote_addr|$remote_port|$remote_user|$time_local|$http_host|$request_length|$request|$status|$body_bytes_sent|$http_referer|$http_user_agent|$http_x_forwarded_for|$http_cookie|$request_body';

    map $request_method $postloggable {
    POST 1;
    default 0;
    }

    А потом в контексте нужного сервера:

    access_log /var/log/nginx/servername_post.log post if=$postloggable;

    Только не забывайте что в POST могут быть приватные данные, и кому попало доступ к этому логу давать не стоит.

    PS надеюсь парсер меня поймет.

  2. Сделал покрасивше комментарий.
    Спасибо!

Написать комментарий