Back to blog
Threat Intel
Phishing Forensics

Beyond p=reject: DMARC's Forgotten Subdomain Gap

Attackers are bypassing your `p=reject` policy by spoofing from subdomains you forgot to lock down.

MailSleuth Research
Email Security Team
June 25, 20267 min read
Illustration of a fortress representing a DMARC-protected domain, with a small, forgotten side door allowing threats to

You get the alert. A convincing BEC phish impersonating your CFO just landed in the finance team's inboxes. Impossible, you think. You personally configured the DMARC record for yourcompany.com to `p=reject` six months ago. The domain is locked down. But when you pull the headers, the `From` address isn't what you expected: `cfo@billing.yourcompany.com`.

This is the scenario that catches even seasoned security teams off guard. They secure the organizational domain, celebrate the win, and move on, completely missing the subtle but critical hierarchy that governs subdomains. It's a failure rooted not in the DMARC protocol itself, but in a partial implementation of it.

Understanding the DMARC inheritance model, particularly the role of the subdomain policy tag (`sp=`), is the difference between a truly secure email posture and a false sense of security. Attackers thrive on these gaps, and forgotten or misconfigured subdomains are their favorite entry point.

The Inheritance Trap: How DMARC Policies Cascade

The fundamental concept of DMARC inheritance is defined in RFC 7489. When a mail receiver gets an email from a subdomain, say `alerts.corp.com`, it first looks for a DMARC policy published as a TXT record at `_dmarc.alerts.corp.com`. If no record is found, the receiver is instructed to traverse up the DNS tree and check for a policy at the organizational domain, `_dmarc.corp.com`.

On the surface, this fallback mechanism seems like a great feature. It implies you can set one strong policy at your root domain and have it automatically protect all your subdomains. But this assumption is where the trouble begins. The inheritance is not as simple as a direct copy-paste of the parent policy. The RFC was designed for flexibility, allowing domain owners to set different enforcement levels for the primary domain and all the subdomains beneath it.

The policy applied to a subdomain is determined by the parent's `p=` tag *only if* an `sp=` tag is not present. The `sp=` tag, or subdomain policy tag, explicitly declares the desired policy for all subdomains. When it's missing, you're relying on a default behavior that might not match your security intent, especially in complex environments with a mix of an organization's own and third-party sending services.

Anatomy of a Subdomain Spoof

Let's return to the BEC attack from `billing.yourcompany.com`. As the analyst investigating the incident, your first step is to examine the headers of the malicious email. You're looking for the `Authentication-Results` header, which is the receiving mail server's report card for SPF, DKIM, and DMARC.

You quickly find the smoking gun. The attacker's sending IP failed its SPF check, and the message had no valid DKIM signature. Both authentication mechanisms failed, as expected for a spoofed email. But the DMARC result is the crucial piece of evidence:

Authentication-Results: mx.example.com; spf=fail (sender IP is 198.51.100.50) smtp.mailfrom=billing.yourcompany.com; dkim=fail (no key for signature) header.d=billing.yourcompany.com; dmarc=fail (p=NONE) header.from=yourcompany.com

The verdict is `dmarc=fail`, but the policy applied was `p=NONE`. This tells the receiver to take no action, allowing the fraudulent email to proceed straight to the inbox. Your organizational domain's `p=reject` policy was completely ignored. Why?

A quick DNS query reveals the cause. Years ago, a team set up a billing portal and used a third-party service to send payment reminders. To get things working quickly, they published a DMARC record specifically for that subdomain: `_dmarc.billing.yourcompany.com IN TXT "v=DMARC1; p=none;"`. The portal was decommissioned, but the DNS record was never removed. Because a policy exists at the subdomain level, receivers stop their search there; they never look up to the organizational domain's stronger `p=reject` policy. The attacker simply discovered this forgotten, permissive record and exploited it.

Controlling the Cascade: `sp=` as Your Subdomain Safety Switch

What is the `sp=` tag?

The `sp=` tag is an optional modifier you can add to your organizational domain's DMARC record. It explicitly tells receivers what policy to apply to any subdomain that does *not* have its own DMARC record. Think of it as the default security posture for the rest of your namespace.

You can set it to one of three values: `sp=reject`, `sp=quarantine`, or `sp=none`. By setting `sp=reject`, you are making a powerful statement: 'Unless I have specifically and intentionally created a separate DMARC policy for a subdomain, assume it sends no email and reject any attempts to impersonate it.'

The Ambiguity of a Missing `sp=` Tag

What happens if you don't include an `sp=` tag at all? According to RFC 7489, if the `sp=` tag is absent, the policy specified by the `p=` tag is used instead. So, a record like `v=DMARC1; p=reject;` effectively means `p=reject` and `sp=reject`. This seems secure, and for a simple setup, it is. However, it relies on default behavior and doesn't explicitly state your intent.

Explicitly setting `sp=reject` removes all ambiguity. It acts as a safety net, protecting you from future mistakes like the `billing.yourcompany.com` scenario. It ensures that any new or forgotten subdomains are born secure. Relying on the default inheritance is fine, but stating your policy with `sp=reject` is better. It is a clear, machine-readable declaration of your security strategy.

The Secure Pattern: `sp=reject` and Intentional Overrides

A mature DMARC strategy doesn't just block bad email; it safely enables good email. The reality for any large organization is that you'll need to use third-party services for marketing, transactional emails, or support. Many of these services require you to delegate a subdomain to them. This is where a secure pattern combining a strict default with explicit exceptions becomes critical.

Step 1: Set a Strong, Explicit Baseline

Your organizational domain's DMARC record should be your fortress. By explicitly setting both `p=` and `sp=` to `reject`, you establish a 'deny-by-default' posture for your entire domain namespace.

_dmarc.yourcompany.com. IN TXT "v=DMARC1; p=reject; sp=reject; rua=mailto:dmarc-rua@yourcompany.com;"

This record achieves two things. First, `p=reject` protects your primary brand asset, the organizational domain itself. Second, `sp=reject` protects against the unknown: attacker-invented subdomains (`secure-login.yourcompany.com`) and forgotten internal subdomains are now automatically covered by the `reject` policy.

Step 2: Create Deliberate, Monitored Exceptions

Now, let's say your marketing team needs to use a third-party platform that sends email from `mktg.yourcompany.com`. This service might have trouble with DMARC alignment, making `p=reject` impossible without blocking legitimate campaigns. Here, you can intentionally override the parent policy.

You publish a separate, more permissive DMARC record specifically for that subdomain: `_dmarc.mktg.yourcompany.com. IN TXT "v=DMARC1; p=quarantine; pct=50; rua=mailto:dmarc-mktg@yourcompany.com;"`. This specific record is respected by receivers, overriding the `sp=reject` from the parent domain. Crucially, this is a conscious security decision, not an accident. You are creating a controlled exception and directing the DMARC reports to a dedicated mailbox for monitoring, while the rest of your namespace remains locked down. This approach gives you security by default and flexibility by design.

Finding the Gaps Before They Do

You cannot defend an attack surface you can't see. The key to preventing subdomain spoofing is proactive discovery. An attacker needs to find only one weak link, but you need to defend all of them.

Your first resource is DMARC aggregate (`rua`) reports. These XML reports are a goldmine of information, providing a daily summary of all email seen by receivers that claims to be from your domain and subdomains. By analyzing these reports, you can discover sending sources you didn't know existed, both legitimate and malicious. If you see traffic from `legacy-app.yourcompany.com` failing DMARC, you've just found a gap to close.

Manual auditing is also essential. Use command-line tools like `dig` or `nslookup` to periodically query for `_dmarc` records on known subdomains. More importantly, use asset discovery tools and DNS enumeration techniques to find subdomains you weren't aware of. Every subdomain is a potential policy loophole until it's explicitly accounted for, either by the parent `sp=` tag or its own specific record.

The takeaway

Don't let your DMARC implementation create a false sense of security. Setting `p=reject` on your organizational domain is a vital first step, but it's not the end of the journey. The real work lies in managing the full hierarchy of your domain namespace.

The `sp=reject` tag is your most powerful tool for this. It flips the security model from opting-in subdomains to a secure-by-default posture, protecting you from forgotten infrastructure and novel attack vectors. Audit your DNS, analyze your DMARC reports, and close the subdomain gap before an attacker does. Use comprehensive analysis tools like MailSleuth.AI to continuously monitor your entire email-sending surface, ensuring no subdomain is left behind.

#dmarc#email-security#spoofing#dns#rfc-7489#bec
MailSleuth Research
Email Security Team

We dissect phishing campaigns and email infrastructure so you don't have to.