Статья OAuth Device Code Flow атака: от фишинга до persistent-доступа в Azure AD

Монитор светится в тёмной комнате: страница входа Microsoft с кодом авторизации и терминал с запросом к OAuth API. На столе кружка с кофе и свёрнутый USB-кабель.


На red team проекте для финтех-компании с P2-лицензиями Entra ID, настроенным Sentinel и FIDO2-ключами для C-level - мы получили persistent-доступ к почте CFO за 12 минут. Без перебора паролей, без обхода MFA, без единого credential prompt на стороне жертвы. Один device code, введённый пользователем на легитимной странице microsoft.com/devicelogin. Refresh-токен оставался валидным 47 дней.

По данным IBM X-Force Threat Intelligence Index 2025, атаки с использованием действительных учётных данных выросли на 71% за год. Device code phishing - один из самых чистых способов получить такие токены, не касаясь ни одного endpoint. Ни антивирус, ни EDR, ни FIDO2 - ничего из этого не срабатывает, потому что атаки на endpoint нет.

Место в цепочке атаки​

Device code phishing - техника initial access, которая одновременно закрывает credential access. Полная цепочка при атаке на Azure AD / Microsoft 365:
  1. Recon - подтверждаем, что таргет использует Azure AD (DNS: MX-записи, autodiscover, openid-configuration endpoint)
  2. Initial Access - фишинговое письмо или сообщение с device code (T1566.002, Spearphishing Link)
  3. Execution - жертва переходит на microsoft.com/devicelogin и вводит код (T1204.001, Malicious Link)
  4. Credential Access - получаем access_token и refresh_token (T1528, Steal Application Access Token)
  5. Lateral Movement - через FOCI ротируем токен на другие приложения M365 (T1550.001, Application Access Token)
  6. Persistence - refresh-токен даёт доступ без повторной аутентификации до 90 дней (T1078.004, Cloud Accounts)
Что должно быть выполнено до: базовый OSINT по домену (tenant ID, подтверждение Azure AD, наличие M365-сервисов). Что следует после: enumeration через Microsoft Graph API - пользователи, группы, роли, почта, SharePoint, Teams.

Механика Device Authorization Grant​

Device Authorization Grant (RFC 8628) проектировался для устройств без браузера - Smart TV, IoT-датчики, принтеры. Устройство запрашивает у authorization server пару кодов (user_code + device_code), пользователь вводит user_code через браузер на другом устройстве, а исходное устройство поллит endpoint /token в ожидании access_token.

Фича для телевизоров. А теперь - вектор атаки на C-level.

Microsoft реализовала это в Identity Platform под именем Device Code Flow. Для атакующего ключевые свойства:
  • Запрос на /devicecode не требует данных о жертве - нужны tenant, client_id, scope
  • Tenant можно указать как /common - работает для любого аккаунта в любом Entra-тенанте (personal и organizational)
  • В качестве client_id допустимы идентификаторы first-party приложений Microsoft, одинаковые во всех тенантах: Azure CLI (04b07795-8ddb-461a-bbee-02f9e1bf7b46), Microsoft Office и другие (по данным исследования LevelBlue)
  • Жертва видит consent-запрос от имени доверенного приложения Microsoft - не от какого-нибудь «Secure Document Viewer»
  • Время жизни user_code - 15 минут, refresh_token - до 90 дней
MFA не спасает. Вообще. Пользователь проходит полную аутентификацию на легитимном портале Microsoft, включая MFA-challenge. Это не обход MFA (T1621) - это использование MFA как ожидаемой части flow. Пользователь сам прошёл MFA, сам нажал «Approve», токены ушли атакующему. По данным Verizon DBIR 2025, 36% инцидентов начинаются с фишинга - device code phishing добавляет к этой статистике вектор, который стандартные anti-phishing контролы пропускают.

Эксплуатация device code flow на пентесте​

Требования к окружению​

ПараметрЗначение
ОСLinux / macOS / Windows
Python3.8+ (для кастомных скриптов и ROADtools)
PowerShell5.1+ (для TokenTactics)
СетьИсходящий HTTPS к login.microsoftonline.com
ПривилегииНе требуются на стороне атакующего
КонтекстВнешний пентест или red team с разрешённым фишингом
RAMМинимальные требования - утилиты работают в CLI

Генерация device code и доставка жертве​

Запрос к endpoint /oauth2/v2.0/devicecode с client_id Azure CLI:
📚 Часть контента скрыта. Этот материал доступен участникам сообщества с рангом One Level или выше
Получить доступ просто — достаточно зарегистрироваться и проявить активность на форуме


Фишинговая страница - сайт-приманка (стилизация под Adobe, SharePoint, HR-портал), JavaScript автоматически запрашивает device code через бэкенд и показывает user_code с ссылкой на microsoft.com/devicelogin. Требует фишинговую инфраструктуру - дополнительный IoC.

Прямое письмо - «Для подключения к защищённой встрече Teams введите код ABCD-EFGH на microsoft.com/devicelogin». Ноль фишинговых доменов, ноль подозрительных URL в теле письма. Все ссылки ведут на microsoft.com. Красота.

Через мессенджер - если уже компрометирован один аккаунт, device code отправляется в Teams/Slack от имени коллеги. Доверие максимальное.

Второй вариант проще с точки зрения OPSEC: нет веб-инфраструктуры, нет доменов для мониторинга, нет JavaScript-пейлоадов. По данным Microsoft (Storm-2372 campaign, 2025), государственные APT-группы использовали именно прямую отправку device code в фишинговых письмах. Если APT так делают - значит, работает.

Получение токенов и FOCI-ротация​

Пока жертва вводит код, атакующий поллит /token:
Bash:
curl -X POST "https://login.microsoftonline.com/common/oauth2/v2.0/token" \
  -d "grant_type=urn:ietf:params:oauth:grant-type:device_code" \
  -d "client_id=04b07795-8ddb-461a-bbee-02f9e1bf7b46" \
  -d "device_code=<DEVICE_CODE>"
До авторизации сервер возвращает authorization_pending. После - JSON с access_token, refresh_token, id_token. С этого момента всё, что доступно жертве в Microsoft 365 - доступно атакующему.

Дальше - lateral movement внутри облака. По данным LevelBlue, Microsoft поддерживает недокументированную группу приложений - Family of Client IDs (FOCI). Приложения в этой группе разделяют общий Family Refresh Token (FRT): получив refresh_token для Azure CLI, можно запросить access_token для Microsoft Office, Teams, Outlook - без повторной аутентификации. Один токен - весь стек M365.

Приложения с общими идентификаторами через тенанты (по данным LevelBlue):

ПриложениеClient IDДоступ
Azure CLI04b07795-8ddb-461a-bbee-02f9e1bf7b46Azure-ресурсы, Graph API
Microsoft Officed3590ed6-52b3-4102-aeff-aad2292ab01cДокументы, почта
Microsoft Teams1fec8e78-bce4-4aaf-ab1b-5451cc387264Чаты, каналы, файлы

Один украденный refresh-токен превращается в доступ ко всему стеку Microsoft 365. Lateral movement (T1550.001) без единого подключения к внутренней сети - чисто облачная операция.

Инструменты device code phishing: выбор под задачу​

ИнструментКогда использоватьОграниченияOPSEC-профиль
TokenTactics (PowerShell)Быстрый device code phishing с автоматической FOCI-ротациейТребует PowerShell; характерный User-AgentСредний
ROADtools (Python)Enumeration Azure AD после получения токена, работа с refresh-токенамиНе генерирует device code - только post-exploitationВысокий
curl / Python requestsКастомные сценарии, полный контроль параметровРучная работа, нет автоматизации FOCIМаксимальный
Фишинговый сайт + JSМассовые кампании, автоматическая генерация кодовВеб-инфраструктура; домен = IoCНизкий

На практике комбинация выглядит так: TokenTactics для initial access и быстрой ротации токенов через FOCI, ROADtools для enumeration после - пользователи, группы, роли, политики.

Для максимального контроля OPSEC - кастомный скрипт на Python с прокси и контролем User-Agent. TokenTactics удобен, но на проектах, где IR-команда активно мониторит, я предпочитаю кастомный скрипт: полный контроль timing, headers, IP-ротация. Разница между «нашли через час» и «жили 47 дней» - в деталях.

OPSEC: что светится в Azure Sign-In Logs​

Техника срабатывает надёжно, но вопрос - как долго доступ останется живым. Azure Sign-In Logs фиксируют:
  • Authentication Protocol = deviceCode - отдельное значение, которое легко фильтровать KQL-запросом
  • Application = имя приложения за client_id (Azure CLI, Microsoft Office)
  • IP-адрес: два разных IP - жертвы (ввод кода на devicelogin) и атакующего (поллинг /token). Разные страны = алерт Impossible Travel
  • User-Agent: при использовании curl стандартный curl/8.x.x нетипичен для Azure CLI. Светится как новогодняя ёлка
Что снижает вероятность обнаружения:
  1. IP - residential proxy в том же городе и стране, что и таргет. Два IP из разных стран при одном sign-in - мгновенный алерт. Экономить на прокси тут - глупость
  2. Timing - поллинг /token не чаще одного раза в 5 секунд (минимальный interval из ответа сервера). Более частый поллинг возвращает slow_down и может логироваться отдельно
  3. User-Agent - при работе через кастомный скрипт эмулируйте User-Agent Azure CLI или PowerShell: стандартные значения не вызывают аномалий в Sign-In Logs
KQL-запрос, который используют SOC-аналитики для детекции (по данным LevelBlue и рекомендациям из Azure Security):
Код:
SigninLogs
| where AuthenticationProtocol == "deviceCode"
| where ResultType == 0
| project TimeGenerated, UserPrincipalName, AppDisplayName,
          IPAddress, Location, DeviceDetail
| sort by TimeGenerated desc
Если в организации никто не использует device code flow в легитимных целях - любое событие с AuthenticationProtocol == "deviceCode" должно быть алертом. Проблема в том, что большинство организаций этот запрос не настроили. Запрос на 6 строк, а его нет в 90% тенантов, которые я видел.

Conditional Access и ограничения техники​

Conditional Access (CA) в Entra ID - главный защитный контрол. Политика может заблокировать authentication flow типа deviceCode глобально или для групп пользователей.

Когда CA блокирует атаку:
  • Политика явно запрещает Device Code Flow для всех пользователей и всех облачных приложений
  • Настроена привязка к compliant или hybrid-joined устройствам - токен выдаётся, но доступ к ресурсам отсечён
Когда CA не помогает:
  • Политика не создана - Device Code Flow разрешён по умолчанию. И это ключевое: flow не входит в стандартные шаблоны Microsoft security baseline
  • Политика создана с исключениями для service accounts или break-glass аккаунтов - атакующий знает об этом из разведки
  • CA требует MFA, но не проверяет тип flow - пользователь проходит MFA сам, CA пропускает
Как проверить до начала фишинга, заблокирован ли flow: отправьте запрос на /devicecode с client_id Azure CLI и scope openid. Если в ответе есть user_code - flow разрешён. Если ответ содержит ошибку AADSTS53003 - заблокирован Conditional Access. Генерация device code не привязана к конкретному пользователю и не оставляет следов в Sign-In Logs - безопасная проверка на этапе recon. Я всегда начинаю с неё.

ПараметрЗначение
Тип пентестаВнешний с разрешённым фишингом, red team
Целевая инфраструктураAzure AD / Entra ID, Microsoft 365
MFA bypassДа - пользователь проходит MFA самостоятельно
Работает при заблокированном CAНет - главное ограничение
Требует endpoint-доступНет - облачная атака
Детектируется EDRНет - на endpoint жертвы нет вредоносной активности
Детектируется SIEM/SentinelДа - AuthenticationProtocol=deviceCode, anomalous IP
Google CloudАналогичный flow существует (по данным Huntress)
Окно для фишинга15 минут с момента генерации user_code

Что убивает persistence после успешной атаки:
  • Отзыв всех сеансов через Entra ID или Graph API - инвалидирует все refresh-токены
  • Continuous Access Evaluation (CAE) - критические события (смена пароля, блокировка аккаунта) отзывают токены в near real-time
  • Token protection (preview) - привязка токена к конкретному устройству, при запросе с другого устройства токен отклоняется

Чеклист защитных мер для отчёта​

  1. Заблокировать Device Code Flow через Conditional Access в Entra ID - для всех пользователей и всех облачных приложений
  2. При необходимости Device Code Flow - создать исключения строго для конкретных service accounts с ограничением по IP и compliant devices
  3. Включить Continuous Access Evaluation (CAE) для сокращения окна использования украденных токенов
  4. Настроить алерт в Sentinel / SIEM: AuthenticationProtocol == "deviceCode" с ResultType == 0 - каждый успешный deviceCode sign-in требует расследования
  5. Мониторить Impossible Travel для sign-in events с deviceCode - два IP из разных локаций при одном sign-in
  6. При подтверждённой компрометации - отозвать все сеансы через Revoke-AzureADUserAllRefreshToken или Graph API revokeSignInSessions
  7. Внедрить token protection (preview) для привязки токенов к конкретным устройствам
  8. Awareness: сотрудники должны знать, что ввод кода на microsoft.com/devicelogin по запросу из email - не стандартная процедура аутентификации
По данным CrowdStrike Global Threat Report 2025, 75% вторжений используют действительные учётные данные, а количество cloud-инцидентов выросло на 26% за год. Device code phishing вписывается в оба тренда: атакующий получает легитимный токен через легитимный процесс.

Неудобная правда: большинство организаций с настроенным Sentinel, P2-лицензиями и security baseline Microsoft - уязвимы к этой технике прямо сейчас. Conditional Access policy для блокировки device code flow не входит в стандартные шаблоны security baseline и не включена в wizard первоначальной настройки Entra ID. Это не баг - это легитимная функция, включённая по умолчанию. Пока защитники не начнут относиться к Device Code Flow как к attack surface, а не как к фиче для Smart TV, техника будет работать.

На ближайший год прогнозирую рост комбинированных атак: device code phishing как initial access с последующим adversary-in-the-middle для перехвата session cookie поверх украденного OAuth-токена. Storm-2372 показала, что государственные APT-группы уже используют device code flow в production-кампаниях. Корпоративные red team должны включать эту технику в стандартный playbook - не как экзотику, а как базовый вектор. Откройте свой тенант, отправьте тестовый запрос на /devicecode с Azure CLI client_id. Если получили user_code - у вас та же проблема, что и у той финтех-компании. Только пока без нас на другом конце.
 
Мы в соцсетях:

Взломай свой первый сервер и прокачай скилл — Начни игру на HackerLab

Похожие темы

🚀 Первый раз на Codeby?
Гайд для новичков: что делать в первые 15 минут, ключевые разделы, правила
Начать здесь →
🔴 Свежие CVE, 0-day и инциденты
То, о чём ChatGPT ещё не знает — обсуждаем в реальном времени
Threat Intel →
💼 Вакансии и заказы в ИБ
Pentest, SOC, DevSecOps, bug bounty — работа и проекты от проверенных компаний
Карьера в ИБ →

HackerLab