DEV Community

Spicy
Spicy

Posted on

How AI Phishing Emails Are Built (And the One Pattern That Always Gives Them Away)

Most phishing detection advice is now actively harmful. Teaching users to look for typos and generic greetings made sense when phishing was a spray-and-pray operation running on bad grammar. That era is over.

Here's how modern AI phishing is actually constructed, what signals remain reliable, and how to implement detection logic that accounts for the current threat model.


How an AI Spear Phishing Email Gets Built

The workflow an attacker follows in 2026 is largely automated:

Step 1: Target reconnaissance

# Typical OSINT data sources for a targeted attack
sources = {
    "linkedin": "job title, manager name, team structure, tenure",
    "company_website": "email format (first.last@company.com), press releases",
    "social_media": "recent posts, projects mentioned, travel",
    "data_broker": "personal email, phone, home address",
    "previous_breaches": "password patterns, security question answers"
}
Enter fullscreen mode Exit fullscreen mode

All of this is publicly available or purchasable. A targeted attack on a finance manager will include their correct name, their CFO's actual name, and may reference a real business event pulled from a press release.

Step 2: Prompt engineering for the attack

The attacker doesn't write the email. They prompt a model:
The output is indistinguishable from a real internal email.

Step 3: Infrastructure

Lookalike domains are registered with realistic names (company-billing.com, companyfinance.io), SSL certificates acquired (free via Let's Encrypt — the padlock means nothing), and emails sent through legitimate SMTP infrastructure to pass basic spam filters.


What Traditional Detection Gets Wrong

The signals security training still teaches:

Signal Why It Fails Now
Typos / bad grammar LLMs produce perfect prose
Generic greeting OSINT provides correct names trivially
Unknown sender Lookalike domains pass visual inspection
Suspicious links Links go to legitimate sites that redirect
Urgency alone Legitimate emails also have urgency

None of these are reliable discriminators in 2026.


What Still Works: Authentication Layer Checks

SPF, DKIM, DMARC — these operate at the email infrastructure level and can't be faked without compromising the legitimate domain.

# Check authentication results for a received email
# Look for these headers in the raw message

# SPF: did the email originate from an authorized server?
Received-SPF: pass (google.com: domain of cfo@company.com designates 
  198.51.100.1 as permitted sender)

# DKIM: was the email cryptographically signed by the domain?
DKIM-Signature: v=1; a=rsa-sha256; d=company.com; s=selector1;

# DMARC: does the domain's policy require both to pass?
Authentication-Results: mx.google.com;
  dkim=pass header.d=company.com;
  spf=pass smtp.mailfrom=company.com;
  dmarc=pass (p=REJECT)
Enter fullscreen mode Exit fullscreen mode

A legitimate internal email from your CFO should pass all three. Any failure is a hard signal — not a soft one. Most attackers can't pass DMARC on the domain they're spoofing without compromising it directly.

Programmatic header parsing:

// Parse authentication results from email headers
function parseAuthResults(headers) {
  const authHeader = headers['authentication-results'] || '';

  return {
    spf: authHeader.match(/spf=(pass|fail|softfail|neutral)/)?.[1] || 'missing',
    dkim: authHeader.match(/dkim=(pass|fail|none)/)?.[1] || 'missing',
    dmarc: authHeader.match(/dmarc=(pass|fail|none)/)?.[1] || 'missing',
  };
}

function isAuthenticationSuspicious(authResults) {
  const { spf, dkim, dmarc } = authResults;
  // Any failure on a supposedly internal or financial email = flag
  return spf !== 'pass' || dkim !== 'pass' || dmarc !== 'pass';
}
Enter fullscreen mode Exit fullscreen mode

The Signal That Doesn't Depend on Content

Authentication checks require access to headers. The signal that works at the human layer — and that AI cannot defeat — is the request pattern.

Legitimate organizations have consistent behavioral signatures:

const PHISHING_REQUEST_PATTERNS = [
  'wire transfer outside normal approval chain',
  'request for credentials or MFA codes via email',
  'urgency to bypass standard process',
  'confidentiality instruction (do not tell X)',
  'new payment method or vendor not in system',
  'action requested on behalf of unavailable approver',
];

// The key insight: legitimate urgent requests
// arrive through established channels with context.
// Phishing creates the urgency in the email itself.
Enter fullscreen mode Exit fullscreen mode

This pattern holds regardless of how the email is written. AI can generate perfect prose but cannot change the fact that a real CFO initiating a real wire transfer uses the company's actual payment system, not a direct email to a finance manager with a new bank account.


Building a Detection Heuristic

For teams building email security tooling or internal automation:

def phishing_risk_score(email):
    score = 0

    # Authentication failures (high weight)
    if email.spf != 'pass': score += 40
    if email.dkim != 'pass': score += 30
    if email.dmarc != 'pass': score += 30

    # Domain analysis
    if is_lookalike_domain(email.sender_domain): score += 50
    if email.reply_to != email.from_address: score += 25

    # Request pattern signals (content analysis)
    content = email.body.lower()
    if any(phrase in content for phrase in [
        'wire transfer', 'bank account', 'routing number'
    ]): score += 20
    if any(phrase in content for phrase in [
        'urgent', 'immediately', 'today only', 'close of business'
    ]): score += 10
    if any(phrase in content for phrase in [
        'keep this confidential', "don't mention", 'just between us'
    ]): score += 30

    # High score = route to additional verification, not auto-block
    return score

def is_lookalike_domain(domain, legitimate_domains):
    from jellyfish import jaro_winkler_similarity
    return any(
        jaro_winkler_similarity(domain, legit) > 0.85 
        and domain != legit
        for legit in legitimate_domains
    )
Enter fullscreen mode Exit fullscreen mode

The key design decision: high-risk emails should trigger an out-of-band verification requirement, not an auto-block. Auto-blocking has false positive costs; requiring phone verification for flagged financial requests has almost none.


The Defense That Defeats All Variants

Out-of-band verification: any email requesting financial action, credential changes, or process exceptions gets verified via phone call to a number already on record.

This rule is architecturally sound because it breaks the attack at the social engineering layer regardless of how convincing the email is. It doesn't matter how good the AI gets at writing emails — it can't intercept a phone call the target initiates to a known number.

The consumer version of this — what non-technical users should watch for — is at lucas8.com/how-to-spot-ai-phishing-email

Top comments (0)