19 марта мы объявили о начале месяца поиска уязвимостей «Проверь Badoo на прочность». Сегодня нам хочется подвести первые итоги и поделиться с вами промежуточными результатами, рассказать, как мы готовились к проверке на прочность, рассмотреть самые интересные уязвимости и сделать «фейспалм».
И для начала немного статистики:
- за первые две недели участники прислали нам почти 500 заявок с потенциальными уязвимостями;
- около 50 заявок оказались дубликатами;
- каждая десятая заявка содержала в себе реальную уязвимость (самые опасные были исправлены в течение нескольких часов);
- Более 150 заявок составили ошибки, не связанные с безопасностью сайта, и около 10% из них относятся к платформам, не участвующим в конкурсе.
- большинство уязвимостей пришлось на самый главный компонент системы ― профиль (как только участники конкурса не издевались над аккаунтами пользователей: удаляли и загружали фотографии, манипулировали комментариями, интересами, личными данными и адресами электронной почты).
- Более половины присланных уязвимостей ― различные CSRF, в основном затрагивающие загруженный или написанный пользователями контент (удаление и загрузка фото и комментариев, работа с чёрным списком, избранным и т.д.).
![Конкурс уязвимостей, или Ломай меня полностью! Конкурс уязвимостей, или Ломай меня полностью!](https://www.pvsm.ru/images/konkurs-uyazvimostei-ili-lomai-menya-polnostyu-2.png)
Как мы готовились к конкурсу...
Несколько месяцев назад мы встроили глобальную защиту от XSS на всём сайте. Сразу оговоримся, что она не является панацеей и не закрывает 100% уязвимостей. Её цель ― помочь разработчику не допустить случайную ошибку. Суть метода заключается в автоматическом экранировании абсолютно всех переменных, выводящихся через blitz, за исключением тех моментов, когда разработчик специально разрешает выходным данным появляться в неэкранированном виде.
Самая интересная и единственная полноценная XSS-узявимость обнаружилась в выводе дополнительного SEO-контента в неавторизованном профиле. Некоторые данные профиля (в основном поле «О себе») для различных SEO-нужд добавлялись в один из мета-тегов. В этом месте когда-то был разрешен вывод сырых данных, что и позволило произвести атаку. Так как уязвимость работала только для неавторизованых пользователей, мы ей присвоили первую (наименее опасную) категорию: никаких конфиденциальных данных получить через неё было невозможно. Единственные разумные применения, которые приходят на ум, ― использование в качестве open redirect для дальнейшего фишинга данных аккаунта, но и в таком случае надо быть уверенным, что пользователь в данный момент не находится на сайте.
… и как не готовились.
В 2013 году проект OWASP понизил рейтинг атаки CSRF на три пункта, несмотря на повальный рост её популярности и отсутствие защиты у множества крупных веб-проектов. Суть атаки проста: при переходе на специальную страницу с формой, содержащей в себе POST- или GET-запрос, можно поменять контент или настройки аккаунта под сессией пользователя. В этом месте у нас всё было не так хорошо, как хотелось бы. Только часть функционала на сайте (авторизация, выход, изменение личной информации и т.д.) была защищена уникальными сессионными токенами. Как только пришёл первый десяток репортов об этом баге, стало очевидно, что проблему нужно решать целиком. Уже через несколько дней после старта конкурса мы выложили ещё одну глобальную защиту: все веб-сервисы на авторизованной части сайта стали проверять наличие токенов в POST- или GET- запросах к ним. Этим фиксом мы закрыли большую часть CSRF-заявок.
Далее мы рассмотрим одни из самых интересных уязвимостей и дадим комментарии участников, которые их нашли.
Уязвимость в обработке количества кредитов при покупке (BSI-13)
Автор: Asd
Категория: 5
Почему мы присвоили этой уязвимости пятую, самую высокую степень важности? Потому что она оказалась настолько проста в эксплуатации, что не требовала никаких специальных навыков и инструментов, кроме браузера и любой debug-консоли для него. Форма выбора количества кредитов выглядела в HTML-коде следующим образом:
...
<option data-ga-ev="100 кредитов - 45,00р." value="100">
<option data-ga-ev="550 кредитов (50 бесплатно!) - 200,00р." value="550" selected="selected">
<option data-ga-ev="1250 кредитов (250 бесплатно!) - 450,00р." value="1250">
<option data-ga-ev="2750 кредитов (750 бесплатно!) - 900,00р." value="2750">
...
Оказалось, что значение поля value использовалось напрямую! Заменив его на практически любое число, можно было провести оплату в размере 220 рублей и получить это количество кредитов себе на счёт. Уязвимость была исправлена в течение нескольких часов, а пользователь с ником Asd попал на первое место в нашем внутреннем топе как первый обнаруживший уязвимость самой высокой степени значимости.
«Сначала просто ходил по разным разделам сайта, пытался изменять значения параметров в запросах, но ожидаемо в проекте такого уровня найти серьезную уязвимость вроде sql injection таким образом не получилось. Вспомнилось исследование habrahabr.ru/post/117039/, в котором авторы искали уязвимости не непосредственно на сайтах, а при их взаимодействии со сторонними сервисами и в ходе него не устоял даже Amazon. Поискав целенаправленно подобные взаимодействия на badoo, обнаружил их в нескольких местах — импорт контактов, подтверждение аккаунта, оплата кредитов и суперсилы. Зайдя на страницу покупки кредитов обнаружил возможность оплаты несколькими способами, причем схемы взаимодействия с платежными системами отличались очень сильно.
Я решил, что ошибиться в одной из них вполне реально и начал исследовать более подробно. Первой выбрал webmoney, так как у меня был под рукой кошелек с небольшим количеством денег. Насколько я понял покупка производится следующим образом: пользователь выбирает один из возможных вариантов (100, 550, 1250, 2750), его выбор сохраняется где-то во внутреннем счете на сайте, после чего пользователь производит оплату на сайте webmoney, в ходе которой оплачиваемый счет сравнивается с сохраненным, и если они совпадают и оплата прошла успешно — пользователь получает свои кредиты. Посмотрев, какие параметры передаются при сохранении счета обратил внимание, что передается непосредственно количество оплачиваемых кредитов, а не некоторое значение, обозначающее один из 4 возможных вариантов. Заменив его на большое число, получил от сервера корректный ответ, с выставленным счетом на 7.5wmz (стандартная цена за 550 кредитов) и указанным мной числом кредитов. Оплатил его — и кредиты оказались у меня на аккаунте, правда в немного урезанном виде — всего 2147483647».
Наш комментарий: На самом деле ошибка была в несколько формах оплаты, которых у нас великое множество — наличие которых определяется страной пользователя.
Misconfiguration при изменении данных профиля (BSI-12)
Автор: whitebureau
Категория: 5
В тот же день пользователем whitebureau была найдена ещё одна серьёзная брешь. При взаимодействии с профилями других пользователей (показ дополнительной информации, совершение каких-либо действий) в параметрах запроса передавался параметр cuid (current user id), указывавший ID этого пользователя.
GET http://badoo.com/ws/profile-ws.phtml?section=pimore&cuid=31337
Эти взаимодействия проводились через тот же контроллер, который отвечал за редактирование профиля, отличия были лишь в действии action, и whitebureau попытался добавить данный userid параметр с произвольным значением в запрос редактирования пользователя.
POST http://badoo.com/ws/profile-ws.phtml
...
section=interested_in&interested_in_text=cats&cuid=31337
Оказалось, что проверка этого параметра на соответствие текущему ID пользователя при редактировании профиля была пропущена. Так у нас появилась вторая критическая уязвимость.
Подобные ошибки часто встречаются именно на больших проектах. И в частности на проектах, активно работающих с ajax. Возникает путаница с суперглобальными php-переменными: разработчики используют $_REQUEST со всеми переменными http-запроса, вместо более точных $_GET, $_POST или $_COOKIE. В итоге злоумышленник может очень просто дезориентировать код, подменив желаемую переменную в post или get запросе. Скрипт же не заметит подмены.
Clickjacking на всех страницах сайта (BSI-9)
Автор: Eugene Farfel
Категория: 1
Также к нам поступили сообщения о некритичных, но интересных уязвимостях. Одной из них оказалась возможность проведения так называемого кликджекинга (англ. clickjacking) на всех страницах сайта.
Этот термин впервые использовал Еремия Гроссман (англ. Jeremiah Grossman) ещё в 2008 году, но до сих пор мало кто задумывался о значимости и последствиях этой уязвимости. Что же такое кликджекинг? Как говорит OWASP, «Clickjacking, also known as a “UI redress attack”, is when an attacker uses multiple transparent or opaque layers to trick a user into clicking on a button or link on another page when they were intending to click on the the top level page».
Проще говоря, можно показать сайт внутри iframe, перекрыв некоторые его элементы с помощью прозрачных div-тегов, содержащих обработчики нажатия. Таким образом можно заставить ничего не подозревающего авторизованного пользователя совершать произвольные действия.
Один из самых известных способов защиты имеет название «framebusting». Он проверяет открытие страницы внутри iframe с помощью Javascript. Если адрес текущей страницы отличается от того, что написано в адресной строке браузера, выполняется перенаправление:
var frameBusted = (top != self);
if (frameBusted) top.location.href = '/index.htm';
Как оказалось позже, и этот способ можно обойти в большинстве современных браузеров:
<iframe sandbox src="http://badoo.com/></iframe>
Поэтому с 2009 года все современные браузеры (даже IE) поддерживают заголовок X-Frame-Options, в котором можно полностью запретить вставлять сайт внутрь iframe, либо разрешить этот функционал только для конкретного списка доменов.
А ваш сайт защищен от кликджекинга?
На сегодняшний день, практически все интернет-ресурсы подвержены подобным Clickjacking уязвимостям. Процесс их поиска не особо сложный, есть общее правило — если ресурс не использует заголовок X-Frame-Options в своих ответах, то существование возможности встроить его в фрейм с последующим созданием различного рода фишинг-страниц практически гарантировано. Таким образом, уязвимость можно считать найденной уже по результатам просмотра HTTP-заголовков.
Изучение .js-файлов, используемых Badoo, показало, что для выхода из фрейма использовался распространенный, но ошибочный метод замены свойства window.top.location. При использовании данного метода страница, встраивающая Badoo в фрейм, получала событие onload и могла принять меры по противодействию. Одной из таких мер является перенаправление браузера на адрес, возвращаемый с кодом ответа 204. Запрос к таким URL не приводит к навигации в браузере (страница с фреймом остается открытой), но и отменяет попытку кода Badoo заменить location.
Первый вариант исправления, внедренный Badoo, дополнил этот код заменой body страницы внутри фрейма пустой строкой. Это исправление, хотя и не решало проблему встраивания в фрейм, убирало из него все содержимое и приводило к невозможности создания фишинг-страниц. Данное исправление также легко обходилось с использованием атрибута sandbox тега iframe, отключающего javascript в фрейме.
В принципе, единственным способом защиты от Clickjacking-атак является использование заголовка X-Frame-Options для всех страниц, для которых не предполагается встраивание в фрейм и подтверждение всех действий, изменяющих состояние системы, на страницах, предполагающих встраивание в фрейм.
CSRF и изменение пользовательского email (BSI-38)
Автор: graphite
Категория: 3
(частично про эту уязвимость есть в BSI-21, автор Илья Киль)
Вот уж чего мы не ждали, так это описания способа смены адреса электронной почты для произвольного пользователя. Но за один один день пришло целых три описания различных уязвимостей, связанных с компонентом повторной отправки пароля, с помощью которых можно было изменить почтовый ящик пользователя, то есть «угнать» его учётную запись.
Существует форма повторной отправки пароля при регистрации, в которой можно изменить свой email, если в него закралась опечатка. Оказалось, что пользователю можно даже изменить уже подтверждённый почтовый адрес, воспользовавшись CSRF:
<form action=http://eu1.badoo.com/not_confirmed/" method="post" id="form">
<input type="hidden" name="post" value="1" />
<input type="hidden" name="newemail" value="attacker@somemail.com" />
<input type="submit" />
</form>
<script>$('#form').submit();</script>
Исправить уязвимость можно было несколькими способами:
1. Добавить проверку на наличие подтверждения.
2. Добавить CSRF-токен.
3. Добавить CAPTCHA.
Поскольку модуль критически важен, были применены все три способа, а пользователи, нашедшие уязвимости, были вознаграждены.
Т.к. с Badoo я до этого знаком не был, в первую очередь, решил выяснить, а что вообще здесь можно делать. Сразу же внимание привлекла возможность импорта контактов из разных почтовых сервисов — захотелось проверить, не вносится ли такой адрес в список дополнительных e-mail для восстановления пароля. Увы, не вносится. Но мысли уже пошли в эту сторону и через некоторое время я наткнулся на форму повторной отправки e-mail для подтверждения регистрации с возможностью ввести другой e-mail, вместо ошибочного. В этой форме была CSRF, но удивительнее всего было то, что если аутентифицированный пользователь заполнял эту форму, у него менялся e-mail в профиле. Очевидно, в этом сценарии отсутствовала проверка на то, подтвердил ли уже пользователь свой e-mail.
CSRF и привязка внешнего аккаунта социальных сетей (BSI-44)
Автор: chipik
Категория: 4
Также обнаружились уязвимости при взаимодействии сайта с внешними ресурсами. У нас существует функционал, позволяющий связывать профиль на сайте с внешними аккаунтами Facebook, VK или Google+. Через них пользователи могут авторизоваться на сайте, не запоминая логин и пароль. Участник chipik проверил этот алгоритм на прочность и нашёл способ его обмануть.
С помощью плагина Tamper Data для Firefox можно перехватить запрос на привязку внешнего аккаунта и получить ОAuth-токен авторизации. Если с помощью CSRF злоумышленник заставит авторизованного пользователя перейти по ссылке с этим токеном, его учётная запись окажется привязанной к внешнему аккаунту злоумышленника и позволит последнему авторизоваться под этим пользователем. Самым верным решением было добавить сессионно-уникальный CSRF-токен ко всем операциям с привязкой к учётной записи.
Спасибо Е.Х. за вектор :)
Напоминаем, что заявки принмаются до 19 апреля и у вас ещё есть шанс поучаствовать в конкурсе, получить вознаграждение, а также помочь сделать нашу платформу более безопасной.
После окончания конкурса мы выберем 3 самых активных участников и подарим им дополнительно по 1000 фунтов.
А ещё у нас есть 50 классных футболок с символикой конкурса и благодарственных писем с подписью жюри, которые мы раздадим самым активным участникам.
Продолжайте проверять нас на прочность!
Станислав Еремин и команда Badoo Development
Автор: blackybr