Friday, October 23, 2026

Spoofy for auditing SPF and DMARC spoofability across domain portfolios

Spoofy for auditing SPF and DMARC spoofability across domain portfolios

Spoofy evaluates whether domains can be email-spoofed by evaluating their SPF and DMARC records against a manually tested verdict table, intended for authorized assessments and email security hardening.

ToolMattKeeley/Spoofy — a Python 3.9+ tool that determines whether domains are spoofable based on tested SPF and DMARC record combinations
CategoryEmail security configuration analysis / pre-assessment reconnaissance
Primary UseBulk-auditing SPF and DMARC posture of domains you own or are contracted to assess, with optional DKIM selector enumeration via --dkim
Safe UseUse only in authorized penetration tests, internal email-security audits, phishing-simulation programs, and academic research on domains with explicit consent
Telemetry NoteSpoofy performs passive DNS lookups against public resolvers (1.1.1.1, 8.8.8.8, 9.9.9.9) or one supplied via --dns-server, so defenders observe only repeated TXT/DNS queries for _dmarc and SPF records from the scanning host

Spoofy is a Python utility, distributed on PyPI as spoofy, that answers a deceptively simple question with unusual rigor: given a domain's published SPF and DMARC records, can mail claiming to be from that domain actually land in a victim's inbox? The author anticipates the obvious objection — there are already several spoof-checking scripts — and differentiates Spoofy on the quality of its verdict logic. Rather than speculating from the RFCs about how mail providers will treat a given configuration, the tool is built around a master table of SPF/DMARC combinations that were empirically tested against real mailbox providers. For professionals running authorized phishing simulations or email deliverability audits, that distinction between theory and measurement is the whole value proposition.

The tool targets Python 3.9+ and installs cleanly from PyPI with pip3 install spoofy, after which a single-domain check is as simple as spoofy -d example.com. A source-tree workflow is also supported via pip3 install -r requirements.txt followed by ./spoofy.py. The CLI surface is compact: -d for a single domain, -iL for a file of domains (with blank lines and # comments skipped), -o to select output between stdout, xls, and json, -t to tune thread count (defaulting to 4), an optional --dkim flag, and --dns-server to override the default resolver pool. That parameter set tells you the intended mode of operation: bulk, scripted, output-to-spreadsheet assessment of an organization's entire domain estate.

Under the hood, Spoofy implements standards-based record discovery rather than naive record parsing. The README cites RFC 7208 evaluation for SPF and the RFC 9989 DMARC tree walk, correctly handling the sp tag for subdomain policy and the newer np tag for non-existent subdomains. This matters because many homegrown checkers treat a subdomain without its own _dmarc record as unprotected, when in fact policy can be inherited from the organizational domain. Spoofy walks the tree the way a receiving mail server would, then applies its verdict table to the effective, inherited policy rather than to whatever literal record string it found first.

The DNS layer is also more deliberate than typical scripts. Bulk lookups run over a shared, caching resolver with automatic failover across 1.1.1.1, 8.8.8.8, and 9.9.9.9, which reduces redundant queries and avoids the partial-failure noise you get from naive per-thread resolvers. Importantly for reliability, a failed DNS lookup produces a distinct verdict code (9, unable to determine) instead of being silently treated as a missing record — a common source of false positives in lesser tools, where a transient resolver hiccup would be reported as an unprotected domain. The tool also tracks SPF DNS query and void lookup counters, which is a genuine operational concern: SPF records that exceed the 10-lookup limit are misconfigured by definition and often silently ignored by receivers.

A particularly nice forensic touch is the detection of unregistered SPF include domains. When an organization's SPF record includes a third-party sender whose own domain has since lapsed and been dropped from the registry, the record becomes both broken and, in the worst case, a takeover primitive where an attacker registers the dangling include and inherits sending authority for the victim domain. Flagging these dangling includes during an audit is a defensive finding in its own right, entirely independent of any spoofing verdict, and it is the kind of thing that routinely slips through annual compliance reviews.

The verdict system itself is a ten-code taxonomy that maps every relevant SPF/DMARC combination to SPOOFING_POSSIBLE values of true, false, or null. Codes 0 through 2 cover unconditional spoofability at the direct, subdomain, and organizational-domain levels. Codes 3 through 6 and 7 capture the fuzzier cases — p=quarantine with pct below 100, where unsampled mail effectively gets p=none, and outcomes the author labels "mailbox dependent," where Protonmail and Gmail place spoofed mail in the inbox with a banner or in spam without one, while Microsoft 365 treats both conditions identically. Code 8 is a clean not-spoofable, and code 9 is the DNS-failure indeterminate. The verdict is computed from the domain's SPF all mechanism plus the DMARC p, sp, and aspf tags exactly as the records write them.

What gives the table credibility is the documented methodology. The author enumerated every relevant SPF and DMARC configuration, combined them, collected records at scale using an early version of the tool across a large set of US government domains, then validated spoofability for each combination through the emailspooftest email security pentesting suite. Initial testing on Protonmail and Gmail was abandoned for verdict purposes because those providers apply reverse-lookup checks that skewed subdomain results; Microsoft 365 was chosen as the primary test bed for its more controllable mail handling, with selected combinations re-verified against the consumer providers to classify the mailbox-dependent cases. The resulting master table ships as data inside spoofy/master_table.py, with python3 -m spoofy.master_table regenerating the spreadsheet artifact and test.py asserting that every row is reproduced — a testable link between the published evidence and the runtime logic.

Edge cases get explicit handling rather than being swept into a default. When an enforcing p exists alongside aspf but no explicit sp (24 untested combinations), the tool applies the RFC default that sp inherits from p and uses the tested row for the written-out equivalent. A subdomain inheriting DMARC from a parent is evaluated as a subdomain outcome against the parent's records, with np substituting for sp when the subdomain itself does not exist. Testing mode (t=y under RFC 9989) drops the effective policy one level — reject to quarantine, quarantine to none — before the lookup, which correctly reflects that receivers do not enforce testing-mode policies at full strength. These are exactly the corners where lazy checkers diverge from reality.

The optional --dkim flag enables DKIM selector enumeration via an external API, which rounds out the authentication picture: a domain with strong SPF and a strict p=reject DMARC policy can still be spoofable in practice if no DKIM key is published and the receiver's alignment checks fail gracefully. The README is candid that the spoofability table itself excludes DKIM modifiers, so the enumeration feature exists to give the operator supplementary signal rather than a verdict-changing input. In an authorized assessment report, pairing the spoof verdict with observed selectors makes the remediation guidance actionable: publish selectors, tighten p, and align aspf deliberately.

Operationally, Spoofy sits squarely in the passive-reconnaissance and configuration-audit tier of an engagement. It sends no mail and touches nothing beyond DNS TXT queries and, with --dkim, an enumeration API, so it leaves a minimal footprint that defenders can observe as clustered _dmarc and SPF lookups from a single scanning host against public resolvers. The repo carries roughly 770 stars, CI tests, and a CC BY-NC 4.0 license, and the README's own disclaimer is unambiguous: the tool is for testing and academic purposes only, where strict consent has been given, with legal compliance resting on the end user. For blue teams, the same tool run against your own domain portfolio is a fast, evidence-backed way to find the misconfigured pct, the inherited subdomain policy, and the dangling include before someone else does.

Official project repository for MattKeeley/Spoofy.
Download Tool

Educational analysis for authorized security professionals. Use only in controlled, authorized environments.

Share articleFacebookXLinkedIn

Continue exploring

Browse all articles →

0 comentários:

Post a Comment

Note: Only a member of this blog may post a comment.