Уязвимость и баги, стоящие денег клиентам. Разбираем модуль от CS Coding для CMS CS Cart

от автора

На написание данной статьи меня вдохновила уязвимость в модуле «Авторизации по телефону», который разрабатывается и поддерживается CS Coding.

ДИСКЛЕЙМЕР: в статье я никого ни в чём не обвиняю, а объективно разбираю проблему уязвимости и сопутствующие баги, связанные с модулем «Авторизации по телефону»

Данный модуль написан для CMS CS Cart и является платным решением для бизнеса. Он позволяет регистрироваться и авторизовываться на сайте с помощью телефона и любого поддерживаемого СМС-провайдера, в нашем случае TargetSMS.

Мы купили этот модуль для наших магазинов в редакции Multivendor около пяти месяцев назад и с самого начала его использования у нас наблюдались проблемы, которые я рассмотрю подробно ниже. Оговорюсь, что статьи бы не было, если бы CS Coding «магическим» образом не подтирала негативные отзывы на маркетплейсах.

Проблема первая: зарегистрированные пользователи не могут войти в только что созданный аккаунт

В БД CS Cart, в таблице пользователей, есть два поля, отвечающих за пароли: 1 — password, 2 — salt.

Пароли, задаваемые пользователями, или, как в случае с модулем, создаваемые алгоритмом генерации, хэшируются в MD5. В некоторых случаях, в целях безопасности, пароли могут использоваться вместе с salt, таким образом, злоумышленнику придется постараться, чтобы выцепить пароль пользователя и, обработав его, попытаться авторизоваться, что практически невозможно.

При тестировании модуля, мы первым делом столкнулись с тем, что пользователь не может войти, а виной тому, как выяснилось в процессе изучения проблемы, являлось то, что создавалась salt, но она никак не участвовала в валидации при входе. Стандартные функции CS Cart задействованы не были.

На тот момент, я не знал всю структуру CS Cart и не мог сам в полной мере понять, как это исправить, да и у нас с CS Coding была поддержка на полгода, поэтому я написал им. Надо отдать им должное — они «пофиксили» баг. Почему в кавычках? — они пофиксили баг только у нас, а в модуле, скаченном для поиска проблем уязвимости, описанной ниже, этот баг не устранен. Значит все, кто купит модуль сейчас получат эту проблему. Они знают о проблемах, но не спешат их устранять. Вот два участка кода: первый — их модуль, скачанный неделю назад с их магазина для анализа, второй — их модификация в рамках поддержки, сделанная 5 месяцев назад:

<? if (!empty($user_data) &&      (!empty($password) &&       fn_generate_salted_password($password, $salt) == $user_data['password']       && $_REQUEST['login_type'] != 'sms_code' ||       $_REQUEST['login_type'] == 'sms_code' &&       $_REQUEST['sms_code'] == $_SESSION['csc_code'])) {...}
<? if (!empty($user_data) &&      (!empty($password) &&       fn_user_password_verify((int) $user_data['user_id'],         $password, (string) $user_data['password'],        (string) $salt) &&        $_REQUEST['login_type'] != 'sms_code' ||       $_REQUEST['login_type'] == 'sms_code' &&        $_REQUEST['sms_code'] == $_SESSION['csc_code'])) {...}

Тут становится понятно, почему невозможно было авторизоваться из-за salt: скрипт генерировал пароль с солью в условии, но никак не верифицировал его. У нас они эту оплошность исправили.

Проблема вторая: наличие двух полей с Email в профиле пользователя

Данный баг был обнаружен уже после запуска модуля на боевом сайте. Мы решили убрать поле email вовсе и сделать его не обязательным. Были проведены манипуляции в админке сайта и отключено соответствующее поле, однако, оно так и осталось на своем месте. Мы не обратили тогда внимания, что оказывается у нас теперь не одно, а два поля Email, одно из которых не убирается совсем никак. Данный запрос был продиктован начальством и мне пришлось прятать это поле вручную, в обход админки. Практика нехорошая, но она позволила получить нужный результат. Забегая наперед, скажу, что позже, мы вернули Email на сайт, а вот второе поле так и осталось призраком маячить в коде и было убрано с помощью JS.

Проблема третья: отсутствие четких масок ввода

К сожалению, мне пришлось повозиться, чтобы добиться полной валидации номера телефона и невозможности ввода других данных. Пришлось задействовать модуль «Маска ввода» и дополнительно писать обработки, чтобы нельзя было вводить что попало. Странно, что разработчики не включили данный функционал. Обратите внимание, что маска ввода сейчас работает штатно — это будет очень важно, когда мы подойдем к теме уязвимости модуля.

Проблема четвертая: жадность разработчиков и период поддержки

Как я сказал ранее, модуль был куплен с поддержкой длительностью шесть месяцев. Поддержка подразумевает решение любых проблем, а также внесение небольших настроечных правок в модуль. У начальства возникла потребность в отключении некоторых типов СМС уведомлений, которые также рассылал этот модуль. Мы обратились к разработчикам, на что получили такой ответ: «Мы готовы убрать уведомления за 2000 рублей…». Логично, что данный ответ нас не устроил и я решил сам понять как это сделать. Изучив поверхностно модуль, я за десять минут определил где находится вызов функции, отвечающей за отправку сообщений с нужным текстом и просто закомментировал две строчки кода. Нужный код находится в директории модуля в файле hooks.php. Вот этот код, отвечающий за отправку измененного статуса заказа:

<?php function fn_csc_sms_change_order_status($status_to, $status_from, $order_info, $force_notification, $order_statuses, $place_order) { $status_data = fn_get_status_params($status_to);     if (!empty($order_info['s_phone'])) { if ($status_data['grant_reward_points'] == "Y") { $points_text = ''; if ($order_info['user_id']) { $user_info = fn_get_user_info($order_info['user_id']); if (!empty($user_info)) { if (!empty($user_info['points'])) { $points_text = __('you_have_n_points', array('[points]' => $user_info['points'])); } } } else { if (!empty($_SESSION['cart']['points_info']['reward'])) { $points_text = __('you_have_n_points', array('[points]' => $_SESSION['cart']['points_info']['reward'])); } } //fn_csc_send_sms($order_info['s_phone'], $points_text); }  if ($status_to == 'O')  { $points_text = '';  $sms_text = __('thank_you_for_the_placing_order', array('[invoice_url]' => fn_url('orders.print_invoice?order_id=' . $order_info['order_id'], 'C'), '[points_text]' => $points_text));  //fn_csc_send_sms($order_info['s_phone'], $sms_text); }     } }

То есть, в их понимании одна закомментированная строчка кода с вызовом функции в модуле, который они разработали стоит 1000 рублей (!) и это в период поддержки.

Последняя капля: уязвимость, которая стоила нашей компании крупной суммы денег

Благополучно, мы так или иначе быстро решали предыдущие проблемы и после последней мы не обращались к CS Coding за помощью, так как всё работало штатно. Однако, спустя почти шесть месяцев после покупки у нас случился очень неприятный инцидент, который привел к серьезным проблемам и практически парализовал работу интернет магазина, так как все пользователи были переведены на авторизацию и регистрацию по SMS.

Проблемы начались внезапно, но об их существовании я узнал только через неделю, когда пришел запрос от администратора сайта, а ему от начальства. Суть проблемы состояла в том, что сайт бесконтрольно стал отправлять запросы к TargetSMS, а сервис, в свою очередь, стал отправлять SMS на иностранные номера. Наши магазины работают только по России и масками ввода я на 100% ограничил возможность ввода другого типа номера.

Чтобы вы понимали всю серьезность ситуации: 1 СМС на российский номер стоит от 1 рубля до 5 рублей, а на номера иностранных государств от 10 и до 30 рублей за штуку. Именно такие тарифы у TargetSMS. Но самое главное, что деньги улетали моментально. Игнорировался таймер задержки отправки и за несколько секунд отправлялось с полсотни SMS. Так, за неделю, в течении которой я не знал о проблеме, компания потеряла около 30 000 рублей. Я отключил модуль и стал его досконально изучать в поисках проблем.

Первым делом, я на стороне сервера в контроллере модуля прописал дополнительное условие с регулярным выражением, которое бы блокировало все иностранные номера. Это не решило ситуацию и я стал тестировать модуль на тестовом сайте, закрытом от пользователей. Я отключил в браузере JS и пытался через средства разработчика и консоль вносить некорректные данные. По понятным причинам скрипты не работали, а это значило только то, что бот или злоумышленник не мог ввести неправильные данные, да еще и в обход таймера задержки отправки сообщений, да еще и несколько десятков раз в минуту.

И тут у меня стали закрадываться подозрения. Версий было много, начиная от саботажа от разработчиков, с целью получить дополнительное финансирование на излете срока поддержки, до сервисов, которые спамят абонентов (SMS Бомберы). Забегая дальше, скажу, что CS Coding виноваты только в том, что не устранили уязвимость, которая лежала на поверхности и дальше объясню почему.

Для сбора данных об абоненте модуль использует один контроллер, который так и называется csc_sms.php. В нем есть следующий код (я немного изменил его вид, чтобы не подвергать опасности обладателей модуля):

<?php use Tygh\Registry;  if ($mode == 'generate_code') {     $delay = Registry::get('addons.csc_sms.sms_delay');     if (!empty($_SESSION['csc_code_time']) && time() - $_SESSION['csc_code_time']         < $delay)     //if (1 == 2)     {         fn_set_notification(             'W',             __('warning'),             __('csc_sms_delay_not_ready',                 array('[sec]' => $delay - (time() - $_SESSION['csc_code_time']))             )         );     }     else     {         $code = fn_csc_generate_code();         $_SESSION['csc_code'] = $code;         $_SESSION['csc_code_time'] = time();         $_SESSION['csc_phone'] = /*Код скрыт из соображений безопасности*/;         $config = Registry::get('config');         fn_csc_send_sms(/*Код скрыт из соображений безопасности*/ . ' ' . $code);         fn_set_notification('N', '', __("confirm_code_sent"));     }     exit; } 

Мы видим здесь переменную $mode, которая отвечает за режим запроса к серверу и в данном случае это GET-запрос. А теперь ответьте мне, как можно это использовать? Очень просто: безопасность GET-запросов при работе с важными данными и критическими элементами бекенда всегда была очень низкой и никогда не использовалась для реализации подобных функций. Всегда используется только POST запрос с дополнительной защитой.

Посмотрев логи сервера, всё встало на свои места: атака не прекращалась ни на минуту, даже если счет на TargetSMS пустел. Как только на счете появлялись деньги, то они тут же списывались и тонна SMS уходила заграничным адресатам. Виной всему были постоянные запросы напрямую к контроллеру csc_sms.php с удаленных серверов:

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

Реакция CS Coding

Мы написали письмо в CS Coding, чтобы убедиться, что атаки совершали не они. Для этих целей мы взяли левый почтовый ящик и прикинулись полными дилетантами. Вот тексты писем и ответы от них:

Как можно видеть, они не хотят даже разобраться в проблеме и игнорируют серьезную уязвимость модуля. Однако, я полагаю, что они даже не знают о проблеме, а рефакторингом заниматься не хотят.

Негативные отзывы мы писали ещё после четвертой проблемы на https://marketplace.cs-cart.com/avtorizaciya-po-telefonu.html, но отзыв «магически» так и не появился. На форуме CS Cart считаю бессмысленно что-то писать, там тоже цензура.

Рекомендации

Если вы уже купили модуль авторизации и используете TargetSMS, используйте дополнительный пароль для HTTP, XML запросов к API, а также, если у Вас в штате есть разработчики, добавьте, в качестве временного решения дополнительный GET-параметр, не забыв про JS функцию, в которой Вы также должны будете его передать. И требуйте от CS Coding устранения данной уязвимости и решения других проблем, описанных в этой статье.

Обращение к CS Coding

Уважаемые представители CS Coding, я не хотел Вас обидеть или дискредитировать. Я с уважением отношусь к Вашему труду. Ситуации бывают разные, однако, такая уязвимость и такое обилие багов для таких серьезных разработчиков как Вы, неожиданны. Сделайте выводы из написанного и научите, пожалуйста, коллег, которые пишут ответные письма основам деловой переписки (мы специально в письмах писали как люди далекие от технологий).


ссылка на оригинал статьи https://habr.com/ru/articles/592335/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *