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.002Timeline: 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 | |
| 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:
+allmeans “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 |