На 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:- Recon - подтверждаем, что таргет использует Azure AD (DNS: MX-записи, autodiscover, openid-configuration endpoint)
- Initial Access - фишинговое письмо или сообщение с device code (T1566.002, Spearphishing Link)
- Execution - жертва переходит на microsoft.com/devicelogin и вводит код (T1204.001, Malicious Link)
- Credential Access - получаем access_token и refresh_token (T1528, Steal Application Access Token)
- Lateral Movement - через FOCI ротируем токен на другие приложения M365 (T1550.001, Application Access Token)
- Persistence - refresh-токен даёт доступ без повторной аутентификации до 90 дней (T1078.004, Cloud Accounts)
Механика 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 дней
Эксплуатация device code flow на пентесте
Требования к окружению
| Параметр | Значение |
|---|---|
| ОС | Linux / macOS / Windows |
| Python | 3.8+ (для кастомных скриптов и ROADtools) |
| PowerShell | 5.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 CLI | 04b07795-8ddb-461a-bbee-02f9e1bf7b46 | Azure-ресурсы, Graph API |
| Microsoft Office | d3590ed6-52b3-4102-aeff-aad2292ab01c | Документы, почта |
| Microsoft Teams | 1fec8e78-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. Светится как новогодняя ёлка
- IP - residential proxy в том же городе и стране, что и таргет. Два IP из разных стран при одном sign-in - мгновенный алерт. Экономить на прокси тут - глупость
- Timing - поллинг
/tokenне чаще одного раза в 5 секунд (минимальныйintervalиз ответа сервера). Более частый поллинг возвращаетslow_downи может логироваться отдельно - User-Agent - при работе через кастомный скрипт эмулируйте User-Agent Azure CLI или PowerShell: стандартные значения не вызывают аномалий в Sign-In Logs
Код:
SigninLogs
| where AuthenticationProtocol == "deviceCode"
| where ResultType == 0
| project TimeGenerated, UserPrincipalName, AppDisplayName,
IPAddress, Location, DeviceDetail
| sort by TimeGenerated desc
AuthenticationProtocol == "deviceCode" должно быть алертом. Проблема в том, что большинство организаций этот запрос не настроили. Запрос на 6 строк, а его нет в 90% тенантов, которые я видел.Conditional Access и ограничения техники
Conditional Access (CA) в Entra ID - главный защитный контрол. Политика может заблокировать authentication flow типаdeviceCode глобально или для групп пользователей.Когда CA блокирует атаку:
- Политика явно запрещает Device Code Flow для всех пользователей и всех облачных приложений
- Настроена привязка к compliant или hybrid-joined устройствам - токен выдаётся, но доступ к ресурсам отсечён
- Политика не создана - Device Code Flow разрешён по умолчанию. И это ключевое: flow не входит в стандартные шаблоны Microsoft security baseline
- Политика создана с исключениями для service accounts или break-glass аккаунтов - атакующий знает об этом из разведки
- CA требует MFA, но не проверяет тип flow - пользователь проходит MFA сам, CA пропускает
/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) - привязка токена к конкретному устройству, при запросе с другого устройства токен отклоняется
Чеклист защитных мер для отчёта
- Заблокировать Device Code Flow через Conditional Access в Entra ID - для всех пользователей и всех облачных приложений
- При необходимости Device Code Flow - создать исключения строго для конкретных service accounts с ограничением по IP и compliant devices
- Включить Continuous Access Evaluation (CAE) для сокращения окна использования украденных токенов
- Настроить алерт в Sentinel / SIEM:
AuthenticationProtocol == "deviceCode"сResultType == 0- каждый успешный deviceCode sign-in требует расследования - Мониторить Impossible Travel для sign-in events с deviceCode - два IP из разных локаций при одном sign-in
- При подтверждённой компрометации - отозвать все сеансы через
Revoke-AzureADUserAllRefreshTokenили Graph APIrevokeSignInSessions - Внедрить token protection (preview) для привязки токенов к конкретным устройствам
- Awareness: сотрудники должны знать, что ввод кода на microsoft.com/devicelogin по запросу из email - не стандартная процедура аутентификации
Неудобная правда: большинство организаций с настроенным 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 - у вас та же проблема, что и у той финтех-компании. Только пока без нас на другом конце.