SPF Lookup Limit Exploitation
SPF records have a 10 DNS lookup limit; attackers exploit overly complex SPF records that exceed this limit, causing validation failures and enabling spoofing.
MITRE ATT&CK: T1566.002Timeline: The Cat and Mouse
2006 β Problem emerges β 2015 β Solutions develop β 2019 β Cloud complexity β Present
The Evolution
| Phase | Period | What Happened |
|---|---|---|
| DESIGN | 2006 | SPF RFC includes 10 lookup limit; poorly understood |
| PROBLEM | 2010-2012 | Complex infrastructures start exceeding limits |
| RESPONSE | 2015 | SPF flattening services emerge |
| Β | 2017 | Validation monitoring tools developed |
| Β | 2019 | RFC 7208 clarifies void lookup limits |
| CLOUD ERA | 2019+ | SaaS explosion (Google, O365, Salesforce, etc.) makes limits hard |
| CURRENT | Present | ~15% of domains broken; ~30% fragile; ongoing problem |
Key Events with Sources
| Date | Event | Significance | Source |
|---|---|---|---|
| 2006 | RFC 4408 published | SPF includes 10 lookup limit | RFC 4408 |
| 2014 | RFC 7208 updates SPF | Clarifies limit behavior and void lookups | RFC 7208 |
| 2015 | Flattening services emerge | Tools to inline IPs and reduce lookup count | AutoSPF |
| 2018 | Academic research | USENIX paper on SPF lookup vulnerabilities | USENIX |
| 2020+ | Cloud complexity | Typical org uses 5-10 SaaS services, each needing SPF include | Industry-wide |
Overview
SPF records limit DNS lookups to 10 per validation. Organizations with complex email infrastructure often exceed this limit, causing SPF validation to permanently fail (PermError). Attackers can exploit this: if a domainβs SPF always fails, spoofing emails appear no worse than legitimate ones.
The Attack
The 10 Lookup Limit
RFC 7208 specifies that SPF validation must not exceed 10 DNS lookups:
Lookup-generating mechanisms:
include:β 1 lookup eacha:β 1 lookup eachmx:β 1 lookup eachptr:β 1 lookup each (deprecated)redirect=β 1 lookupexists:β 1 lookup
Non-lookup mechanisms:
ip4:β No lookup (direct IP)ip6:β No lookup (direct IP)-all/~allβ No lookup
How Limits Are Exceeded
Complex Cloud Infrastructure:
v=spf1 include:_spf.google.com # 1 lookup β chains to more
include:spf.protection.outlook.com # 1 lookup β chains to more
include:_spf.salesforce.com # 1 lookup β chains to more
include:servers.mcsv.net # 1 lookup (Mailchimp)
include:mail.zendesk.com # 1 lookup
include:_spf.freshdesk.com # 1 lookup
-all
Each include: counts as 1 lookup, but the included records themselves contain more lookups:
_spf.google.com contains:
include:_netblocks.google.com # +1
include:_netblocks2.google.com # +1
include:_netblocks3.google.com # +1
Total for just Google: 4 lookups
Real-World Example (Exceeds Limit):
Direct includes: 6 lookups
_spf.google.com chain: 4 lookups
spf.protection.outlook.com chain: 2 lookups
βββββββββββββββββββββββββββββββββ
Total: 12 lookups β PERMERROR
What Happens When Limit Exceeded
Receiving Server:
1. Query SPF record for sender domain
2. Follow includes, count lookups
3. Lookup count exceeds 10
4. Return SPF PermError
5. Email treated as SPF=none or SPF=fail (depending on config)
Impact:
- Legitimate email may be rejected or quarantined
- If receivers treat PermError as βnone,β spoofing goes undetected
- Organization canβt tell the difference between spoofed and legitimate
Void Lookup Attacks
RFC 7208 also limits βvoid lookupsβ (lookups returning no records) to 2:
Attack:
Attacker's SPF record:
v=spf1 include:nonexistent1.attacker.com # void lookup 1
include:nonexistent2.attacker.com # void lookup 2
include:nonexistent3.attacker.com # void lookup 3 β exceeds limit
ip4:attacker_ip
-all
This causes validation errors on the receiving server, potentially used for DoS or to confuse logging.
Exploiting Broken SPF
Reconnaissance:
# Check if target exceeds lookup limit
dig +short TXT example.com | grep spf
# Use online SPF validator
# If result = PermError, domain is exploitable
Spoofing When SPF is Broken:
If target.com has PermError:
- Legitimate mail: SPF=PermError
- Spoofed mail: SPF=PermError or SPF=Fail
- No distinguishing signal!
Attacker sends:
MAIL FROM: <ceo@target.com>
From: CEO <ceo@target.com>
β SPF fails, but so does everything else from that domain
Raw Email Headers (SPF PermError)
Email from a domain with broken SPF:
Return-Path: <spoofed@broken-spf-domain.com>
Received: from mail.attacker.com (mail.attacker.com [198.51.100.77])
by mx.victim.com (Postfix) with ESMTPS id SPFLIMIT01
for <target@victim.com>; Fri, 31 Jan 2025 11:45:22 -0500 (EST)
Authentication-Results: mx.victim.com;
spf=permerror (too many DNS lookups) smtp.mailfrom=spoofed@broken-spf-domain.com;
dkim=none;
dmarc=fail (p=NONE) header.from=broken-spf-domain.com
From: "Finance Team" <finance@broken-spf-domain.com>
To: target@victim.com
Subject: Wire Transfer Instructions
Date: Fri, 31 Jan 2025 11:45:18 -0500
Message-ID: <spf-exploit-001@attacker.com>
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Please process the attached wire transfer...
Key observations:
spf=permerrorβ Validation failed due to lookup limit- Legitimate mail from this domain would ALSO show permerror
- No way to distinguish spoofed from legitimate
dmarc=failbutp=NONEmeans no enforcement- Attacker exploits the domainβs broken SPF
Defenses
SPF Flattening
Inline IP addresses to reduce lookups:
Before Flattening:
v=spf1 include:_spf.google.com -all
After Flattening:
v=spf1 ip4:35.190.247.0/24 ip4:64.233.160.0/19 ip4:66.102.0.0/20
ip4:66.249.80.0/20 ip4:72.14.192.0/18 ip4:74.125.0.0/16
ip4:108.177.8.0/21 ip4:173.194.0.0/16 ip4:209.85.128.0/17 -all
Challenges:
- IPs change; requires regular updates
- Use automated flattening services
- Must monitor for changes in included records
SPF Subdomains
Split SPF across subdomains:
# Main domain
v=spf1 include:_spf1.example.com include:_spf2.example.com -all
# Subdomain 1 (3 lookups)
_spf1.example.com: v=spf1 include:_spf.google.com -all
# Subdomain 2 (3 lookups)
_spf2.example.com: v=spf1 include:spf.protection.outlook.com -all
This distributes lookups but adds complexity.
Lookup Monitoring
Regular auditing:
# Check current lookup count
spf_check example.com --count-lookups
# Alert if approaching limit
if lookups > 8:
alert("SPF approaching limit")
DMARC with DKIM
If SPF is fragile, ensure DKIM is robust:
- DMARC passes if either SPF or DKIM aligns
- Strong DKIM provides fallback
- Monitor DMARC reports for SPF failures
Current State
Status: Active
Many organizations still struggle with SPF limits:
| Problem | Prevalence | Impact |
|---|---|---|
| Exceeded limits | ~15% of domains | SPF never validates |
| Approaching limits | ~30% of domains | Fragile, one change breaks it |
| Poor monitoring | Common | Donβt know theyβre broken |
| Void lookup abuse | Rare but increasing | DoS potential |
Detection Guidance
SPF Health Monitoring
# Regular SPF audits
for domain in critical_domains:
result = validate_spf(domain)
if result.lookups > 8:
alert("SPF approaching limit")
if result.status == "permerror":
critical_alert("SPF broken")
Email Authentication Analysis
Alert on:
email.authentication.spf.result = "permerror"
AND email.from.domain IN (monitored_domains)
DMARC Reporting
Monitor aggregate reports for:
- High SPF failure rates
- PermError in SPF results
- Unexpected sources sending as your domain
What Killed It (or Weakened It)
| Defense | Introduced | Impact |
|---|---|---|
| SPF Record Flattening | 2015 | Tools that inline IP addresses to reduce lookups |
| SPF Validation Monitoring | 2017 | Alerting when SPF records approach or exceed limits |
| Void Lookup Limits | 2019 | RFC 7208 limits void lookups to prevent DoS |