Bug Bounty CloudTips. Account takeover или IDOR. История одной уязвимости

Привет, Хабр! Следующая статья расскажет одну историю о том, как был найден баг в программе CloudTips от Тинькофф.

99a3b60bb534b4458190ce417deeb776.jpg

Функционал тестируемого сервиса

Приложение CloudTips — это сервис для приема безналичных чаевых, а также донатов, который позволяет создавать платежные страницы для перевода этих самых чаевых. В статье будет описано тестирование поддомена lk.cloudtips.ru, на котором пользователь может зарегистрировать аккаунт и управлять своими страницами для выплат. В дополнение к этому можете прочесть прошлую статью, которая также описывает взаимодействие с сервисом https://habr.com/ru/articles/766350/.

Часть 1. Изучение api сервиса и нахождение самого бага

Само изучение сервиса было решено начать с поддомена lk.cloudtips.ru, который отвечает за личный кабинет юзеров, где создаются все страницы для выплат. При входе в свой профиль можно перехватить запрос, выводящий информацию по профилю. Для этого просто нужно отправить GET запрос на api.cloudtips.ru/api/receivers/me, который можно сделать только заранее авторизовавшись, так как идет проверка по HTTP заголовку Authorization: Bearer <ваш токен>:

ca0c61e38b1939a504415cec0dc4e1c0.PNG

Видно, что выводится информация о своем профиле. Сразу же возникла идея поискать IDOR (Insecure direct object reference) или «небезопасная прямая ссылка на объект». Грубя говоря каким-то образом получить данные профилей чужих пользователей. Отправлялись запросы на api.cloudtips.ru/api/receivers/* , подставляя возможные айдишники, номера телефонов, uid’ы, но все безуспешно. Если бы, например, профиль пользователя привязывался к ID профиля и передавался в URL, то можно было бы попробовать их перебрать с помощью атаки Sniper в Burp Suite. Пример атаки:

eca5ec08366ffbe8343c9b68065fa76a.PNG

Для начала перехватываем запрос и отправляем его в Intruder. Выбираем тип атаки (Sniper), который позволяет перебирать какой-либо параметр или несколько параметров при отправке запросов (Подходит для брут форса, а также для нашего случая, чтобы перебрать айдишник):

539bf9b056dd558fffcd188160218c92.PNG

Затем выделяем параметр в запросе, который хотим перебрать:

4bba78c9eba1ece5da4331b20cd68413.PNG

Переходим во вкладку Payloads, в котором можем задать словарь, по которому будет перебираться параметр. Можно как загрузить существующий словарь, так и выбрать уже имеющиеся словари в самом Burp Suite (Например числа, которые соответствовали бы айдишнику профиля):

681ad957c39d0dd8fa1650e3f6ca7fb1.PNG

Все параметры подогнаны, теперь начинаем нашу атаку.

9ae8d1f627761297cbb4b035cb68b5f1.PNG

Видим, что ответ от сервера приходит код 404, что говорит, что подобная страница с нашей сгенерированной нагрузкой не найдена, соотвественно в таком виде IDOR найти не получится… Но возможно, можно будет получить информацию о всех юзерах с помощью одного запроса на api.cloudtips.ru/api/receivers? Сейчас проверим.

13fea845fe226690d35e4545f0541db9.PNG

Безуспешно… Ошибка 403 (Permission denied). Отправим запрос OPTIONS на этот же эдпоинт, чтобы узнать какие методы запросов на него можно отправлять.

57ba5960510c4a64daf2f8a71da556dd.PNG

И видим, что один из разрешенных методов: POST. Пробуем отправить!

325a09be97a584b5c878cdbd92f7a9c7.PNG

И видим, что в ответе от сервера приходит 400 код ошибки, что означает , что POST запрос (со стороны клиента!) сформирован некорректно. Из-за чего приходит 400 код от сервера пи отправке POST запроса:

  1. При отправке слишком большого файла, который превышает допустимого размера

  2. Когда пользователь пытается обратиться к несуществующей странице

  3. Измененные или устаревшие куки

  4. Некорректно настроенные HTTP заголовки

На последнем пункте остановимся поподробнее…
Так как в прошлых запросах данные передавались в формате JSON, попробуем добавить заголовок Content-Type: application/json и какое-нибудь тело в формате JSON в наш запрос. И в результате получим:

7d9e3e7a2ce25548a96b33a1a14d476c.PNG

Можно сделать вывод, что на данный эндпоинт в формате POST запроса отправляется номер телефона! Модифицируем наш запрос, подставив в тело запроса номер телефона:

93b409c45f56a6b34e83b9e2bcbc94b1.png

И успешно подобрав все параметры нашего запроса, видим что в ответ от сервера приходит информация о чужом профиле. Можно узнать зарегистрирован ли вообще человек с указанным номером телефона на данной платформе, его email, айдишники страниц для выплат, а также userId пользователя, который мог бы использоваться злоумышленником для последующих атак.
После проделанных действий, я сразу же отправил репорт в тестируемую программу на площадке https://app.bugbounty.bi.zone.
Через короткий промежуток времени отчет был подтвержден с уровнем критичности «High» типа IDOR.
Но на этом начинается самое интересное.

Часть 2. Неожиданный поворот

После подтвержденного бага прошло пару месяцев. И вот я решил протестировать систему регистрации в личном кабинете CloudTips.
Так выглядит фронт формы для регистрации:

621fd4f364c18f5bdb109397331d959b.PNG

Вводим данные, отправляем, перехватываем запрос:

c0e99d63c93490f6298ae55b900876f5.PNG

И видим POST запрос на api.cloudtips.ru/api/receivers. И тут приходит понимание, что подобный запрос ранее уже где-то видел, так тут еще передается параметр PhoneNumber. В конце концов, становится очевидно, что прошлый репорт имеет непосредственное отношение ко всему происходящему.

Этого, конечно, невозможно утверждать, но скорее всего что прошлая найденная уязвимость являлся на самом деле не просто получением конфиденциальной информации пользователей (IDOR), а получением полного контроля над аккаунтом пользователя (Account takeover), так как мы могли бы сбрасывать пароль любому юзеру.

Доводы подтверждающие, что это был действительно Account Takeover

Сервис позволяет зарегистрировать пользователя и без пароля, надо всего лишь удалить два параметра password и passwordConfirm. Пробуем создать пользователя (которого в базе данных сервиса не существует!!!) без пароля:

5ce44899810c5574806aba017b25e619.PNG

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

57ccb6e84233de0cb21aa3b5448b3d4a.PNG

Видим, что вход по аккаунту без пароля действительно возможен, но требуется подтверждение по номеру телефона.
При изначальном случае, когда баг еще не был запатчен, скорее всего возможно было бы угнать чужой аккаунт, так как у существующего пользователя номер телефона подтвержден. То есть по факту, мы могли бы просто любому пользователю изменить пароль на нулевой и успешно авторизоваться.

Заключение

Думаю, из рассказанной выше истории станет ясно, что стоит всегда полностью анализировать найденный баг и искать дальнейшее его развитие, а не сразу бежать и отправлять репорт. Надеюсь, что данный баг поможет багхантерам, а также людям, кто просто интересуется темой информационной безопасности.
Спасибо представителям BI.ZONE BugBounty и CloudPayments за предоставленную возможность поиска уязвимостей в веб приложениях!

© Habrahabr.ru