[recovery mode] Сказ о том, как я с гидрой боролся

Проблемы третьего мира

Вообще на этом фриланс-проекте я занимался вёрсткой — простенький дизайн в Figma, где ничто не предвещало беды. И вот, когда проект сдан, заказчик внезапно присылает скриншоты с Mac и iPhone — и там какие-то белые квадраты, дизайном ни разу не предусмотренные.

Вариант «а у меня всё нормально показывется» не прокатил. Пришлось поднять виртуалку с MacOS и убедиться: квадраты есть. Консоль? Есть консоль! В консоли — ошибки, какие-то бредовые скрипты, пытающиеся (неудачно) показать рекламу. Заказчик клянётся и божится, что ничего такого не ставил… Смотрю исходники, вижу прекрасный /index.php вот такого содержания:

Приятно обнаружить такое в главном файле сайтаПриятно обнаружить такое в главном файле сайта

И тут вспоминаю, что когда возился с настройкой главного меню (да, в Битриксе с этим приходится возиться), видел странную строку: include($_SERVER["DOCUMENT_ROOT"]."/upload/dr/content/inc.php");

Для Битрикса она довольно нормальная, антивирусы тоже спокойно пропускают. А мне как-то странно — ну кто в здравом уме подключает файлы из upload?

Небольшая справка для тех, кто не знаком с Битриксом и верит в здравый ум:

У него есть несколько проблем:
  • Сайт — это примерно 12.000 мелких php-файлов, раскиданных по сотням директорий разных уровней, каждая страница — отдельный php-файл, каждый компонент — отдельная папка с кучей файлов. Единой точки входа нет, разделения M/V/C нет, растительности нет, жизни нет, населена роботами.

  • CSS и JS раскиданы по всему сайту, и на лету собираются в единую свалку. Управлять этим невозможно, поэтому практикуется встраивание inline-картинок в css, css в html, html в php, php подключаются как попало (include, include_once, require …) и куда попало.

  • шаблоны вёрстки — php-файлы, в которых код перемешан с html, да ещё и по задумке разработчиков раскиданы по такой структуре директорий (3 раза bitrix — это не предел!):
    /bitrix/components/bitrix/iblock.element.add/templates/.default/bitrix/iblock.element.add.form/.default/template.php

    Но если вы думаете, что мучения по поиску нужного компонента заканчиваются на файле /bitrix/components/bitrix/news/templates/.default/bitrix/catalog.filter/.default/template.php то вы ошибаетесь — нужный вам файл может переопределяться вот этим: /local/templates/mystyle/components/bitrix/news/certificates/bitrix/catalog.filter/.default/template.php

  • Разработчики часто бывают не очень грамотные и путаются в этих путях. Плюс официальные обновления иногда ломают совместимость. Поэтому при обновлении движка часто ломается весь сайт, и то, что работает пару лет без обновлений, трогать крайне рискованно. Естественно, дыры в безопасности копятся годами.

Со всем этим безобразием всё равно приходится работать, потому что на предложение «снести сайт к чертям и сделать нормально» малый бизнес реагирует довольно болезненно.

Археология

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

По идее, для таких вещей есть антивирусы типа VirusDie, но на практике они не работают — тот самый include из папки upload он, например, подозрительным не считает. А от беглого просмотра создатели вируса предусмотрели забавную, но тупую защиту:

Защита от беглого взгляда — 3 Мб
37de7e26ab0dad809fc1bcfe44c72026.gif

Как известно, глупые и ленивые всякую работу делают дважды. Поэтому сначала я попытался вычистить всё это вручную, в надежде, что там всего пара «закладок» и сейчас я всё починю. Но гидра не сдавалась, новые головы отрастали на месте отрубленных и приводили меня в отчаяние.

На сервере появлялись новые файлы с stupidcode shell, why bro shell и прочими говорящими названиями. Любопытными при этом представлялись два факта:

  • самые старые из найденных файлов вируса датировались 2016-м годом, а попытки лечить его через VirusDie предпринимались с 2015-го!

  • файлы появлялись непонятным образом, всё, что можно было вычистить, было вычищено

Снежное чучело

На этом моменте (только тогда!) получилось добыть у заказчика ssh-доступ. И тут выяснилось самое интересное. Будет уместна цитата из «Отеля «У погибшего альпиниста», полностью описывающая моё состояние:

… Он не пошевелился. Тогда я подбежал к нему и потряс за плечо. Я обалдел. Хинкус вдруг как-то странно осел, мягко подавшись у меня под рукой. — Хинкус! — растерянно закричал я, непроизвольно подхватывая его.  Шуба раскрылась, из нее вывалилось несколько комьев снега, свалилась меховая шапка, и только тогда я понял, что Хинкуса нет, а есть только снежное чучело, облаченное в его шубу…

Я тоже обалдел. На хостинге был 21 сайт, а у скриптов не было никаких ограничений по доступу между ними — то есть любой скрипт спокойно читал любой файл из директорий выше уровнем (логи, бекапы…) и писал что угодно в другие папки. Фактически, поймав вирус на одном дырявом сайте, ты ловил его сразу на всех! Снеговик, облачённый в меховую шубу — прекрасная метафора уровня безопасности этого проекта, не правда ли?

21 сайт … 250.000 скриптов … за что обижать бедный верстальщик, мистер? я когда-то жить город и ходить школа, мистер, но я не уметь читать так mucho mucho rapido!

Единственное, что спасало — это то, что вирусы явно писали не особо заморачиваясь, видимо, рассчитывая на то, что их никто лечить и не будет. Но мне стало любопытно, смогу ли я с этим справиться.

Плюс автоматизация всей страны

Для начала я с особым цинизмом заблокировал доступ к сайту со стороны Amazon AWS, откуда по логам шла основная коммуникация с вирусными файлами. Кстати, если посоветуете удобную бесплатную программу под Windows для анализа логов — буду рад, платить за http Logs Viewer мне не захотелось, но удобнее ничего не нашёл.

Для получения списка адресов AWS пришлось скачать и установить AWSPowerShell и запустить в PS волшебную команду Get-AWSPublicIpAddressRange | where {$_.IpAddressFormat -eq "Ipv4"} | select IpPrefix

Команда выдаёт список из 3000+ адресов, дополнил его парой литовских серверов, которые уж слишком упорно лезли обращаться к вирусным файлам и закинул в .htaccess (наверное, нормальные админы приготовили для меня отдельный котёл в аду за такое, но ничего умнее на shared хостинге мне в голову не пришло). php_value open_basedir отправился туда же.

php_value open_basedir "/path/to/site"


Require all granted
Require not ip 100.20.0.0
Require not ip 100.24.0.0
#... 3000+ адресов....

А дальше пришлось писать утилиту, которая бы помогла бы мне обуздать эту гидру. Хотелось сделать быстро, одним файлом, без использования внешних зависимостей, чтобы его было легко просто скопировать в нужное место и запустить, из командной строки или через веб.

Кто первый VSCode запустит, тот и программист

В общем, эта поделка вряд ли порадует кого-то хорошим исходником или правильным программированием, зато она работает и делает то, для чего предназначена — сканирует все файлы на предмет 20+ факторов опасности, взвешивает и выдаёт общую оценку опасности файла.

Признаки разные:

  • использования более-менее безобидных, но опасных функций eval() и shell_execute()

  • характерных паттерны обфускации кода вроде $WUGh64382 = irmcjaowlfxc($gbVclGM7976[39]);

  • длинные цепочки вроде \144\151\x73\160\154\x61\157\x72\x73

  • прочие странные имена переменных и функций — например, длиной 40+ символов

  • список директорий, обращение к которым через inlclude считается криминальным.

Каждому признаку присваивается вес (веса подшаманивал вручную по ходу написания). Можно через параметр задать минимальный вес, при котором объект попадёт в отчёт.

Пример отчётаПример отчёта

Ложных срабатываний не так много, хотя Битрикс порадовал функциями с именами рекордной длины вроде GetNumberOfWorkflowTemplatesForDocumentType(), GetInheritedPropertyTemplateSectionMenuItems() или языковой константы ProgressDialogWaitingForResponseFromServerText. А я ещё удивлялся длинным путям в структуре директорий! Создатели Битрикс явно не ленятся печатать! Ну и встроить картинку в css, css в html, html в php — для них нормально.

Чтобы их исключить, предусмотрен список исключений по имени и пути файла — да, очень ненадёжно, но от идеи сравнивать crc32 я пока отказался, хотя это было бы сильно лучше. Потому что количество использования подозрительных факторов зашкаливает:

Исключения всё равно выводятся в списке - на всякий случай их тоже надо просматриватьИсключения всё равно выводятся в списке — на всякий случай их тоже надо просматривать

Результаты

Потратил день, вычищая всё найденное сканером. Вирусная активность с тех пор прекратилась, сайты снова живы. Google и Яндекс успели проиндексировать выдаваемый вирусом контент — какую-то рекламу курток на японском языке, но сейчас всё возвращается обратно.

Сканер я запустил ещё и у себя на хостинге, и тоже нашёл там куски вируса в паре файлов — благо, в давно неактивных сайтах 2016 года. Но сам факт удивил. Попробуйте у себя, если у вас есть что-то из legacy-сайтов на Битрикс / Joomla / WordPress, любопытно, найдется ли что-то!

Прикинул, сколько времени и нервов может сэкономить эта поделка, и решил поделиться ей с общественностью. Хотя когда представляю, сколько помидоров может прилететь за качество кода на нашем серьёзном Хабре — карма съёживается. А и пусть :-)

GitHub

https://github.com/mSnus/simple-virus-scanner

Дополнения и исправления приветствуются. В планах — сделать нормальную сортировку и фильтрацию результатов на JS, улучшить анализ исключений. Ну и, конечно, пригодятся новые «факторы риска» — делитесь, если придумаете таковые.

© Habrahabr.ru