Уязвимость и баги, стоящие денег клиентам. Разбираем модуль от 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 месяцев назад:

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

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

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

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

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

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

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

 $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. В нем есть следующий код (я немного изменил его вид, чтобы не подвергать опасности обладателей модуля):

 $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 с удаленных серверов:

image-loader.svg

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

Реакция CS Coding

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

image-loader.svgimage-loader.svgimage-loader.svgimage-loader.svgimage-loader.svgimage-loader.svg

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

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

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

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

image-loader.svg

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

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

© Habrahabr.ru