Данная блогозапись на хабр прежде всего обусловлена появлением «Ключницы» — хороший повод связать и перевести накопленное.
У нас в программе: вольный пересказ спек OAuth2, слабые стороны и Threat Model, 0day на хабретрюк с аутенфикацией.
OAuth2 это просто
Длинный, но правильный путь изучить его.
Для ленивых я опишу своими словами Authorization Code Flow как самый распространенный и безопасный, aka Server-Side.
Ключевые понятия — Клиент(website/online game/app), Юзер(you), Провайдер(facebook/vkontakte/google), Код(code) и Токен(access_token).
Клиент посылает Юзера — «авторизуй меня для доступа к твоим ресурсам». Юзер идет по ссылке к провайдеру, смотрит что от него требуют — scope параметр — жмет Разрешить. Далее Провайдер редиректит его назад на указанный redirect_uri на домене Клиента со следующими параметрами:
code — идентификатор Юзера у Провайдера, который нужен Клиенту чтобы получить Токен
state — то же значение что было передано на начальный URL. используется для защиты от CSRF и для удобства.
Код из себя не представляет никакой ценности для Юзера(и User-Agent соответственно) т.к. с его помощью нельзя совершать запросы API и нужен он лишь для одной цели — получения Токена.
Для получения Токена Клиент производит запрос на определенный эндпоинт, передав client_id, client_secret, code, redirect_uri по которому был получен код — таким образом Провайдер уверен что это нужный Клиент и по Коду он отдает Токен того самого Юзера. Как видите ни Юзер, ни User-Agent и клиентские скрипты не увидили настоящий Токен. Его знают только Клиент и Провайдер — в идеале.
Дальше Токен используют для совершения API запросов, впоследствии его можно рефрешить (для этого вместе с Токеном возвращается refresh_token).
Threat Model или Я Знаю О Чем Вы Подумали
Длинный, но правильный путь безопасно пользоваться OAuth.
Здесь же отмечу:
1. А что если я подставлю такой redirect_uri который будет вести на свой сайт а потом использую этот Код сам для аутенфикации?
Все redirect_uri должны находится на домене Клиента. Часто разрешен еще домен Провайдера. Ваша ссылка вернет redirect_uri_mismatch.
2. ОК а что если я найду такое место на сайте Клиента, которое сольет мне Код? Может открытый редирект вида site.com?url=http://outsite.com или hot-linked картинку которая сольет реферер?
Каждый Код привязан к тому redirect_uri для которого он был выпущен и для получения Токена Клиент передает «правильный» redirect_uri. Если ваш Код был выпущен для clientsite.com/leak_referer а Клиент при получении Токена отошлет clientsite.com/facebook_callback то Провайдер не отдаст Токен.
3. А можно ли как то слить Код передав правильный redirect_uri?
Нет, т.к. любая правильная реализация Клиента обязана сразу редиректить на другую страницу после получения Кода — так что Код не виден даже в истории браузера.
Даже если вам удалось достать код для правильного redirect_uri то т.к. он уже был 1 раз использован он больше не активен.
4. Допустим у меня есть Код для моего аккаунта в соц сети выпущенный для правильного redirect_uri. Что будет если я заставлю посетить эту ссылку Васю?
В таком случае сайт Клиента подумает что ваш аккаунт в соц сети принадлежит Васе. И присоеденит его. Чтобы типичного CSRF не происходило Клиент обязан сохранять рандомное значение state в сессии/куке и проверять по возврату на колбэк на соответствие. Хотя, так почти никто не делает (точнее не делали).
Реальность
FB Reply Attack
Facebook Connect уязвим для классической Replay attack.
Демонстрирует пункт 3 — после одного использования кода он еще может быть использован для аутенфикации в течение 60-80 минут — стандартное expire_in токена. Реплай атака в чистом виде.
Допустим, на сайте нашли XSS — примерно такой инжект поможет вытащить код для правильного redirect_uri через document.referrer фрейма.
<iframe src="https://www.facebook.com/dialog/permissions.request?app_id=159618457465836&display=page&next=http://magru.net/users/auth/facebook/callback&response_type=code" name="refcontainer" onload="alert(refcontainer.document.referrer)"></iframe>
Hijacking Account
Самая частая уязвимость, присутствует например на хабре — об этом ниже. Нарушается пункт 4.
Если вы видите в запросе state не спешите сдаваться. Что хабр, что digg.com не видили разницы между a12b6467c3fb385e237109502277ab26 и heyman0day123123
VK redirect_uri
Реализация сделана по каким-то древним манускриптам — отсутствие проверки это грубое нарушение пункта 3.
Смотрите, вот ссылка Жмите, не бойтесь, и откройте Network Panel. Видите запрос?
Реферер слился, код слился — теперь его можно использовать чтобы зайти в ваш аккаунт через ваш вконтакте(если вы его привязали).
misc
Если вы используете Implicit Flow — т.е. получаете access_token от юзера напрямую, то нет никаких гарантий что этот токен принадлежит текущему юзеру. Он мог его просто украсть через вредоносного Клиента, и использовать чтобы попасть в аккаунт какого-то юзера на вашем сайте.
Обязательно проверяйте, был ли выпущен данный токен для вашего client_id.
Так же OAuth2 не дает никаких гарантий что пользователь дал вам те разрешения что вы запросили на этапе авторизации в параметре scope. Он мог просто удалить их — в итоге вы должны на колбэке проверить разрешил ли он что вы запросили.
Если на сайте найдена XSS то есть легкий способ украсть кучу access_token-ов. Возьмите authorize URL который сайт использует для facebook и замените response_type=code на token. Осталось вставить этот URL во фрейм, дождаться возврата токена в ссылке вида callback#access_token=123, срезать токен и слить его. Спамьте на здоровье!
0day на хабре?
Тот же эксплоит работает и для facebook/google только сложнее получить redirect_uri с кодом без использования его — нужен NoRedirect+FF
Поэтому демо на VK. дяденька не бань
1. Данный VK аккаунт не должен быть привязан к какому-то читательу. Авторизуйтесь тут: oauth.vk.com/authorize?response_type=code&client_id=3110645&redirect_uri=http%3A%2F%2Fhabrahabr.ru&scope=offline&display=page
2. Вас вернуло на habrahabr.ru/?code=CODE
3. Заставьте другого читателя посетить habrahabr.ru/social/callback/vkontakte/?code=CODE&state=whogivesafuckaboutstate можно подкинуть саму ссылку, но лучше спрятать в img или iframe и тд. Если он залогинен на хабре ваш ВК привяжен к его хабрааккаунту.
4. Логинимся через ваш ВК в его хабрааккаунт, меняем его аватар на nayncat.
Мораль
Вконтакте: перестаньте игнорировать письма в Поддержку, либо попросите ваших разработчиков снизойти поговорить с простым смертным. А еще введите bounty program (sarcasm: есть риск разориться на ней).
Хабр: рекомендую выключить Ключницу на время и пофиксить уязвимость путем правильной проверки 'state' со значением из cookie/session. Этот пост и есть репорт по причине отстутствия времени объяснять два раза.
Road to Hell короче. Вы можете присоедениться к разработке принципиально нового стандарта с нескучными токенами — OAuth2.a (Charm — Provider). Печенек, правда, нет.
Egor Homakov (@homakov) & isciurus #RT pls
Автор: Chikey