Все собаки попадают в рай, а все владельцы интернет магазинов на Opencart, рано или поздно на Opencartforum. Когда проходит эйфория от первой установки движка на хостинг и начинается суровая реальность, типичному владельцу магазина всегда начинает чего-то не хватать и он начинает сложный путь поиска квалифицированных подрядчиков и качественных дополнений для своего магазина.
Самый крупный ресурс в рунете, на котором можно найти и то и другое — это opencartforum.com, на котором на сегодня зарегистрировано более 140к пользователей. Из этих 140к регистраций, пусть половина — это живые люди с живыми магазинами, которые так или иначе пользуются шаблонами, модулями, которые можно тут же приобрести на площадке. И все эти люди, даже не догадываются, что вместе с дополнениями они приобретают себе чудесные бекдоры и уязвимости, о которых любой мамкин хакир может только мечтать.
Хотя в декларативной форме в правилах размещения дополнений в магазине форума черным по белому написано. У нас работает qadepartment, специально обученные люди проверяют дополнения на уязвимости.
И ни форум, как площадка, являющаяся посредником между автором и конечным пользователем дополнения, ни авторы дополнений, как оказалось, не несут никакой ответственности.
Как же так? А вот так! Видимо исторически сложилось.
Моделируя для себя гипотетические последствия от инцидентов, о которых пойдет речь далее, у меня становятся волосы на голове дыбом.
Вот просто представьте. Вы успешный владелец магазина, не спали пару лет ночами, писали тексты, грызли черное, белое, серое seo, боролись с пандами, минусинсками, не доедали не допивали, привлекли 100 000 покупателей, вложились в позиции. Ваши дети ходят в хороший детский сад, и тут вдруг у вас продажи упали вполовину, просто потому что ваша база со всеми контактами клиентам ушла к конкурентам. Или вдруг вы ее лишились совсем, потому что уже месяц как ваш магазин работал на чужом mysql сервере, а вы даже не заметили, смену кофигов, и все бекапы на хостинге «протухли».
Представили свои ощущения в подобной ситуации, работали работали несколько лет и в один день это все коту под хвост. Вы скажете, — так не может быть.
А вот может и очень легко. Достаточно просто купить дополнение с недекларирумой негласной защитой “защитой” от несанкционированого использования, и ваши данные очень быстро могут стать чужими!
Первый инцидент
Полгода назад производили поверхностную профилактику одного магазина, когда репорт сканера Ай-болит показал странное предупреждение на странный код в в модуле SeoCms (более 10.000 активных установок).
Начали смотреть ближе и оказалось что эта абракадабра, просто напросто выводит версию дополнения:
никак не фильтруется и если нечаянно вдруг с сервера клиента придет вместо версии какой нибудь скрипт вроде приведенного ниже, получить админский доступ к любому магазину, где установлен модуль — дело времени и техники.
Подробности ситуации описаны здесь. Уведомление администрации здесь.
Наверное с пятого раза, смоделировав очень наглядно этот процесс для администрации форума, удалось донести критичность ситуации.
И администрация в свою очередь официально уведомила пользователей о наличии данной уязвимости и вроде даже сделала рассылку покупателям. Но это не точно.
Оставим за кадром угрозы и увиливания автора дополнения и перенесемся на два месяца далее…
Второй инцидент
Приходит на осмотр очередной магазин, у которого возникли дикие фризы до 3-4сек на каждой странице. Начинаем препарировать и видим в папке с кешем 20к+ файлов с расширением .php. Файлы кеша с расширением php Карл, это ж неспроста!
Смотрим в структуру данных этих файлов, а там опять кусочки нашего прекрасного модуля SEOCMS.
Это пример классической уязвимости — Local File Include.
Достаточно в base64 закодировать ../....../config.php и здравствуйте — require($ajax_file); И пожалуйста: вот вам пароли от базы, а вот системный лог ошибок, и если сильно надо даже /etc/pwd, хотя давно неактуально, ну а вдруг?
А сколько хостеров и серверов с настройками по дефолту светят в мир phpmyadmin? Половина? Больше?
Начинаем смотреть дальше и что мы находим? Чудесный код загрузки аватарок:
if (!$json) {
if (is_uploaded_file($this->request->files['file']['tmp_name']) &&
file_exists($this->request->files['file']['tmp_name'])) {
$file = basename($filename) . '.' .
md5(substr(sha1(uniqid(mt_rand(), true)), 0, 10)); $file_original = basename($filename);
// Hide the uploaded file name so people can not link to it directly. //$json['file'] = $this->encryption->encrypt($file);
// To remove highload from the file system, when large number of buyers $avatar_dir = 'data/avatars/'.(ceil ($this->data['customer_id'] / 300)) * 300;
move_uploaded_file($this->request->files['file']['tmp_name'],
DIR_IMAGE . $file); $new_filename =
$avatar_dir.'/'.$this->data['customer_id']."_".utf8_strtolower($file_original);
if (isset($this->data['thislist']['avatar_width']) && $this->data['thislist']['avatar_width']!='') {
$width = $this->data['thislist']['avatar_width']; } else {
if (isset($this->data['generallist']['avatar_width']) && $this->data['generallist']['avatar_width']!='') {
$width = $this->data['generallist']['avatar_width']; } else {
$width = '100'; }
}
if (isset($this->data['thislist']['avatar_height']) && $this->data['thislist']['avatar_height']!='') {
$height = $this->data['thislist']['avatar_height']; } else {
if (isset($this->data['generallist']['avatar_height']) && $this->data['generallist']['avatar_height']!='') {
$height = $this->data['generallist']['avatar_height']; } else {
$height = '100'; }
} $this->load->model('tool/image');
$json['file'] = $avatar_thumb = $this->model_tool_image->resizeavatar($file, $new_filename , $width, $height, true, false);
if (trim($avatar_thumb)=='') {
$json['error'] = $this->language->get('error_upload');
}
if ($file!='' && file_exists(DIR_IMAGE . $file)) { unlink (DIR_IMAGE . $file);
}
}
}
Который отлично вываливается в ошибку при попытке ресайза (и показывает нам полный путь к загружаемому файлу), после загрузки какого-нибудь shell.php.png, а потом также отлично выполняется предыдущим методом.
Видео эксплуатации уязвимостей (с включенным выводом системных ошибок php):
Видео эксплуатации уязвимостей (с выключенным выводом системных ошибок php):
Анализ кода проводился на версии модуля 52 в апреле 2019 года.
Так как и автор и администрация форума были уведомлены в тех же датах, прошло достаточно времени, для того чтобы они предприняли все возможные действия по уведомлению покупателей устранению уязвимостей, мы со спокойной совестью, можем раскрыть все детали.
И вот на этом моменте, что-то случилось. На сервере разработчика стоял его же модуль. Но уязвимость на нем не работала. Она была прикрыта… И это большая странность. Очень большая!!!
А вторая странность всей этой истории заключается в том, что opencartforum в лице администрации и департамента по контролю качества дополнений не увидел ни первого ни второго инцидента, и в обоих случаях при первом уведомлении не нашел ничего критичного.
Конечно нет ничего критичного, когда у тебя конфиги в мир светят. Нет ничего критичного, когда добрая половина владельцев магазинов ни сном ни духом не в курсе, что у них происходит под капотом. Ведь им когда-то, кто-то поставил это дополнение, и без рассылки, они даже не догадываются, что весь бизнес под прямой угрозой.
И если по первому инциденту была внятная реакция от администрации, то по второму удяляются все посты с упоминанием о нем. И это все прикрывается офертой форума, согласно которой, про коммерческие дополнения комментарии оставлять можно как про покойников — или хорошо или никак.
Также автор утверждает что ошибки и дыры поправлены, qadepartment пропустил дополнение в продажу, однако нет, дыры не исправлены…
require() никуда не делось. Немного зафильтрован ввод. Но потенциальная LFI как жила так и живет. Если немного подробнее, то да, теперь мы не можем ни загрузить не выполнить шелл. Но предположим, что мы постарались, закрыли, зацементировали исполнение любых скриптов кроме index.php, и даже на уровне конфига nginx, к которому ну никак не получить доступ. Но где-то в другом месте протекло и злой хакир получил возможность поместить произвольный файл в папку кеш. Что произойдет? Правильно — выполнить посторонний код, сгенерировав хеш в get-запрос, дело двух минут. То есть в итоге получается, вроде как и закрыто, но вроде как и не до конца.
Ну и по мелочи: как светились в языковых файлах вот такие шикарные реляции:
Так и светятся… Хотите узнать полный путь к корню проекта. Просто найдите магазин с включенным отображением ошибок и запустите ссылку напрямую на языковой файл.
Изучать что еще может быть после обновления дополнения в мегабайте строк кода автора — нет ни ресурсов ни желания. Для этого есть специально обученные ответственные люди.
И вишенка на торте. Мы же помним, у нас коммерческое дополнение… И в коммерческом дополнении у нас скрытый текст на ресурс автора:
Интересно хоть кто-то из покупателей дополнения знает о такой “чудесной” пасхалочке?
На сегодня, дополнение как продавалось так и продается. Посты с призывами обратить внимание на ситуацию, просто удаляются. Никто из покупателей не получал уведомлений с предупреждением про второй инцидент. Возможно 20-30% пользователей дополнения и обновились, но 70% — даже если мы говорим про официальную авторскую статистику использования дополнений это 7000 магазинов (на самом деле больше — так как никто не знает достоверно, сколько еще было установлено фрилансерами копий на клиентские магазины по расширенной лицензии) находятся в огромной зоне риска.
Словами барона Мюнхаузена хочется сказать только одно: Обновляйтесь господа!
For customers
Что делать, тем кто оказался в этой тонущей подводной лодке:
Не оставлять мусора на фтп. Вида info.php, adminer.php и всякого остального хлама. У вас должен быть только index.php и точка.
Регулярно менять пароли, все пароли (ftp, админки, к базе данных). И следить, чтобы у вас не появлялись случайно лишние непонятные аккаунты.
Отключить вывод ошибок. Всегда, если не ведете работы — отключайте вывод ошибок на уровне конфигурации интерпретатора а не в настройках магазина.
Да я знаю что это не очень выполнимо, но стоит следить хотя бы за актуализацией обновлений кода движка связанных с безопасностью, а в идеале регулярно обновлять движок до стабильных версий.
Регулярно заглядывать в логи посещений, там может быть много интересного. Отслеживайте аномалии.
Закрыть дополнительно паролем или ограничением по айпи admin магазина и разного рода уязвимые разделы, типа phpmyadmin. Если это шаред в котором phpmyadmin общий для всех, бежать с такого хостинга подальше!
Если используете sxd и подобные утилиты — сразу переименовывайте их в рандомный набор символов.
При использовании nginx, в конфиг виртуал-хоста стоит добавить правила, которые запрещают запускать любые скрипты php кроме index.php в корне и admin разделе сайта.
Не устраивайте мусорку из аккаунта. Если у вас магазин — то пусть будет магазин. Не грузите под одним аккаунтом кучу wp-блогов, жумл помоек и разного рода тестовых доменов.
Никогда не храните бекапы сайта или базы в корне вашего виртуалхоста.
Следите за нагрузкой на сервер/хостинг, старайтесь держать запас хотя бы x2 от пиковых нагрузок и если вдруг резко нагрузка ни с того ни с сего увеличилась, попытайтесь выяснить причину. Лучше перебдеть, чем недобдеть!