SPF Misconfiguration Exploitation

Attackers exploit common SPF misconfigurations like +all, overly permissive includes, and missing records to spoof emails that pass authentication.

MITRE ATT&CK: T1566.002

Timeline: The Cat and Mouse

2006 ← Problem emerges → 2015 ← Awareness grows → 2019 ← Cloud creates new risks → Present

The Evolution

Phase Period What Happened
DESIGN 2006 SPF standardized; misconfigurations immediately appear
PEAK 2008-2012 Many domains have +all, ~all, or missing records
AWARENESS 2013-2018 Best practices spread; validators become common
RESPONSE 2015 DMARC reporting reveals misconfiguration impact
  2018 Cloud providers publish official SPF guidance
NEW RISKS 2019+ SaaS explosion creates overly permissive includes
CURRENT Present ~30% use ~all; ~25% overly permissive; ongoing problem

Key Events with Sources

Date Event Significance Source
2006 SPF RFC 4408 Standard published; +all vs -all confusion begins RFC 4408
2010 Online validators emerge Tools like MXToolbox make checking easy MXToolbox
2015 DMARC reveals issues Aggregate reports show misconfiguration impact DMARC.org
2018 Cloud SPF guidance Google, Microsoft publish official include records Google
2020+ Shared infrastructure risk Overly permissive includes allow spoofing via SaaS DMARCLY

Overview

SPF misconfigurations are surprisingly common, even among security-conscious organizations. A single misconfigured character (+all vs -all) can completely negate SPF protection. Attackers actively scan for and exploit these weaknesses.

The Attack

Misconfiguration: +all (Permit All)

The Mistake:

v=spf1 include:_spf.google.com +all
                               ↑
                         Should be -all

Impact:

  • +all means “allow everyone”
  • Any IP can send as this domain
  • SPF always passes, even for attackers

Why It Happens:

  • Copy-paste errors
  • Misunderstanding during testing
  • “Just make it work” debugging

Misconfiguration: ~all (SoftFail)

The Record:

v=spf1 include:_spf.google.com ~all

Impact:

  • ~all = “probably reject, but maybe not”
  • Many receivers accept softfail as “pass”
  • Provides false sense of security

Recommended:

v=spf1 include:_spf.google.com -all
                               ↑
                         Hard fail (-all)

Misconfiguration: Missing SPF Record

The Situation:

dig +short TXT example.com | grep spf
# No results

Impact:

  • No SPF = SPF “none” result
  • DMARC may still pass via DKIM
  • But attackers can spoof envelope freely

Common Causes:

  • New domain setup incomplete
  • DNS migration lost SPF
  • Subdomain without its own record

Misconfiguration: Overly Permissive Includes

The Problem:

v=spf1 include:_spf.google.com
       include:amazonses.com
       include:mailgun.org
       include:sendgrid.net
       include:mailchimp.com
       -all

Impact:

  • Each include authorizes ALL IPs of that service
  • If attacker has any account on these services, they can spoof
  • Shared infrastructure = shared trust

Exploitation:

1. Attacker signs up for Mailchimp free account
2. Target domain includes mailchimp.com SPF
3. Attacker sends from their Mailchimp account
4. SPF passes because Mailchimp IPs are authorized
5. From header spoofs target domain

Misconfiguration: Syntax Errors

Common Syntax Errors:

# Missing v=spf1
spf1 include:_spf.google.com -all

# Space in mechanism
v=spf1 include: _spf.google.com -all

# Missing colon
v=spf1 include_spf.google.com -all

# Multiple records (invalid)
v=spf1 include:_spf.google.com -all
v=spf1 include:other.com -all

Impact:

  • Syntax errors = SPF PermError
  • Treated as no SPF by many receivers
  • Spoofing becomes trivial

Misconfiguration: Subdomain Gaps

The Setup:

# Main domain has SPF
example.com: v=spf1 include:_spf.google.com -all

# Subdomain has NO SPF
mail.example.com: (no TXT record)

Impact:

  • Attackers spoof user@mail.example.com
  • Main domain SPF doesn’t apply
  • Subdomain has no protection

Exploitation:

MAIL FROM: <ceo@hr.example.com>    ← Unprotected subdomain
From: CEO <ceo@example.com>         ← What user sees

Reconnaissance

Finding Vulnerable Domains:

# Check SPF record
dig +short TXT target.com | grep spf

# Look for:
# - +all (pass all)
# - ~all (softfail)
# - No record
# - Syntax errors
# - Excessive includes

Automated Scanning:

def check_spf_weakness(domain):
    spf = get_spf_record(domain)

    if not spf:
        return "NO_SPF"
    if "+all" in spf:
        return "PLUS_ALL"
    if "~all" in spf:
        return "SOFT_FAIL"
    if spf.count("include:") > 5:
        return "OVERLY_PERMISSIVE"

    return "OK"

Raw Email Headers (Exploiting +all)

Spoofed email passing SPF due to +all misconfiguration:

Return-Path: <spoofed@misconfigured-domain.com>
Received: from mail.attacker.com (mail.attacker.com [198.51.100.66])
        by mx.victim.com (Postfix) with ESMTPS id SPFMIS01
        for <target@victim.com>; Sat, 01 Feb 2025 09:30:22 -0500 (EST)
Authentication-Results: mx.victim.com;
        spf=pass (sender IP is authorized by SPF)
            smtp.mailfrom=spoofed@misconfigured-domain.com;
        dkim=none;
        dmarc=pass (p=NONE) header.from=misconfigured-domain.com
From: "IT Support" <support@misconfigured-domain.com>
To: target@victim.com
Subject: Password Reset Required
Date: Sat, 01 Feb 2025 09:30:18 -0500
Message-ID: <spf-misconfig-001@attacker.com>
MIME-Version: 1.0
Content-Type: text/html; charset=UTF-8

<html>
<body>
Your password has expired. Click here to reset...
</body>
</html>

Key observations:

  • spf=pass — Attacker’s IP passes due to +all
  • Attacker sends from their own server (198.51.100.66)
  • misconfigured-domain.com has v=spf1 +all
  • Any IP is authorized, including attacker’s
  • Perfect spoofing with legitimate authentication

Defenses

SPF Record Auditing

Regular validation:

# Check for common issues
spf_audit example.com

# Verify:
# ✓ Record exists
# ✓ Uses -all (hard fail)
# ✓ No syntax errors
# ✓ Under 10 lookups
# ✓ Includes are necessary and minimal

Principle of Least Privilege

Only include what’s necessary:

# Bad: Include entire service
v=spf1 include:sendgrid.net -all

# Better: Include specific sender ID (if service supports)
v=spf1 include:u12345.wl.sendgrid.net -all

Subdomain Protection

Explicit subdomain records:

# Main domain
example.com: v=spf1 include:_spf.google.com -all

# All subdomains (catch-all)
*.example.com: v=spf1 -all

# Or specific subdomains
mail.example.com: v=spf1 include:_spf.google.com -all
hr.example.com: v=spf1 -all  # No mail from this subdomain

Monitoring and Alerting

DMARC aggregate reports reveal:

  • SPF failures you didn’t expect
  • Sources sending as your domain
  • Misconfigurations causing issues
Alert on:
dmarc_report.spf_result = "fail"
AND dmarc_report.source_ip NOT IN (authorized_ips)

Current State

Status: Active

SPF misconfigurations remain common:

Misconfiguration Prevalence Impact
~all instead of -all ~30% of domains Weak enforcement
Missing SPF ~10% of domains No protection
Overly permissive includes ~25% Shared infrastructure abuse
Syntax errors ~5% Complete failure
Subdomain gaps ~40% Subdomain spoofing

Detection Guidance

Regular Audits

# Quarterly SPF review
for domain in all_domains:
    record = get_spf(domain)
    if issues := analyze_spf(record):
        create_ticket(domain, issues)

DMARC Report Analysis

Look for patterns:

  • High failure rates from known services
  • Unexpected sources passing SPF
  • Subdomains with no authentication

Third-Party Monitoring

Services that:

  • Alert on SPF changes
  • Notify when includes change
  • Track authentication trends

What Killed It (or Weakened It)

Defense Introduced Impact
SPF Record Validators 2010 Tools to check for common misconfigurations
DMARC Reporting 2015 Visibility into SPF failures reveals misconfigurations
Cloud Provider SPF Guidance 2018 Standardized includes for SaaS platforms