This one is worth writing up because it is a miss, not a win — and because the reason it was missed is structural, not careless. The mail was fully authenticated. It carried no link to click and no file to open. It passed every reputation check a mail filter runs. And it was still phishing. The entire signal lived in one character of a domain the visitor typed into a form field, which is exactly the layer neither review was looking at.
If you only take one idea from this: for relayed form mail, the envelope is trusted and identical every time. The verdict depends entirely on what the visitor typed into the form. Everything below is why that is true, how both reviews went wrong, and the mail-filtering architecture the cleanup exposed underneath.
01How website-form mail actually arrives
The visitor never sends you mail. A relay does, under your own display name, from the vendor's domain.
A company's "Request a Quote" and "Contact Us" forms almost never send mail from the person filling them in. A web vendor's backend collects the submission and relays a notification to the company — sent from the vendor's own domain, under the company's display name, authenticated by the vendor. So the notification looks, at first glance, exactly like display-name spoofing: "Acme Corp" sent from an address that has nothing to do with acme.com. It isn't spoofing. It's how every legitimate form notification arrives.
02The submission
A polite quote request from a real-sounding vendor — with one character wrong.
The form notification carried the fields exactly as the visitor typed them. Read in isolation it is a bland sales inquiry. Read against the real company it claims to be, every line is a tell. (All values below are fictionalised; the pattern is what matters.)
| Form field | Submitted value | The real vendor |
|---|---|---|
| Name | a plausible buyer name | — |
| Company | Keystone Bolt & Supply Inc. | A real company — same name |
| brian.todd@keystonebolts.com | Real domain is keystonebolt.com — no trailing s | |
| Phone | a toll-free number | Does not match the vendor's published number |
| Website (in signature) | www.keystonebolt.com | The real site — pasted in to borrow credibility |
| Message | a vague "business opportunity," asking for the right email address to send a document to | — |
There are no part numbers, no quantities, no drawings — nothing a real RFQ contains. The only actual request is an email address to send something to. That is the entire payload: this is a first-stage pretext whose job is to get a human to reply, at which point the follow-up carries the malicious document or the credential-harvesting link. The signature even linked the real vendor's website while the From address used a lookalike one character off. That mismatch — real site, fake sender domain — is the signature move of vendor-impersonation phishing.
02bTwo reviews, two misses
One judged the envelope and got it wrong. One judged the body and missed what the fields contained.
The submission was reviewed twice before it was cleared, and the two reviews reached opposite wrong conclusions. An email has layers; each review looked at a different one, and the real threat sat in a layer neither inspected.
Display name from an unrelated domain = spoofing, treat as spam.
That is exactly how a legitimate form relay looks. When the vendor's DKIM passes, it is not spoofing. Acting on "spam" would have blocked the client's own lead intake.
No link and no attachment means there's no phishing here.
The absence of a payload is normal for a first-stage pretext. The attack depends on the reply, not on a click. The harm arrives in the follow-up.
Two automated notes disagree, so pick the one that sounds right.
Contradictory triage notes are a signal to reconcile, not to choose. The disagreement itself meant nobody had actually identified what the message was.
03Why the envelope could never help
Every sample of this relay's mail authenticates perfectly and filters as clean. That is the point.
Pulling raw headers from several of the vendor's real notifications made the trap explicit: all of them pass SPF, DKIM, DMARC and composite authentication, and all of them are scored as clean, non-spam mail delivered to the Inbox. There is no envelope signal that separates a malicious submission from a genuine one, because the envelope is the vendor's and the vendor is legitimate.
| Header signal | What every sample showed |
|---|---|
| SPF / DKIM / DMARC | All pass, aligned to the vendor's domain |
| Composite auth (CompAuth) | pass, reason 100 |
| Sending IPs | Several different IPs across two countries — an IP allow-list would break; the DKIM domain is the only stable identifier |
| Spam verdict | SFV:NSPM — went through full filtering and was judged not spam |
| Attachments | Some form types pass uploaded files through, so malware and Safe Attachments scanning must keep applying |
Read the verdict, not just the pass/fail
SFV:NSPM means "filtered normally, judged clean." If an allow-list in the applied policy had bypassed filtering, you'd see SFV:SKA instead. Spotting NSPM where you expected SKA was the first clue that the allow entry everyone assumed was protecting this mail wasn't being applied at all — which opened up the whole policy-scoping problem below.
04The investigation, and the trap underneath
Nothing was blocking the vendor's mail. But the reason that was true turned out to be luck, not design.
The first job was to disprove the automated review's claim that the vendor's mail was being treated as spam. A handful of read-only checks in Exchange Online PowerShell settled it — nothing was quarantining or blocking the relay — but check #7 is where it got interesting.
# Is the vendor's mail being quarantined? (30 days) Get-QuarantineMessage -SenderAddress info@webvendor.example ` -StartReceivedDate (Get-Date).AddDays(-30) | Select ReceivedTime, RecipientAddress, Subject, QuarantineTypes # → no messages # Is it on the Tenant Allow/Block List? Get-TenantAllowBlockListItems -ListType Sender | ? Value -like "*webvendor*" # → no entries # Is it referenced in any anti-spam policy? Get-HostedContentFilterPolicy | Select Name, AllowedSenderDomains, BlockedSenderDomains | ? { ($_ | Out-String) -match "webvendor" } # → found in the DEFAULT policy's allowed domains
So the vendor's domain was on an allow-list — in the Default anti-spam policy. The problem: the headers said that allow was never applied (NSPM, not SKA). That contradiction only resolves one way: the Default policy does not cover these mailboxes.
The mechanism, in plain terms: Exchange Online matches each recipient address (not the user) against enabled custom policy rules in priority order; the first match wins; anything unmatched falls to Default. Here a long-forgotten custom policy — named after a single vendor someone wanted to block years ago, then scoped to the entire domain — quietly became the real policy for almost everyone. So:
- Default's block list protected almost no one. Senders the client believed were blocked could still reach most mailboxes.
- Default's allow list bypassed filtering only for the stragglers — a few secondary-domain aliases and one excepted mailbox.
- The vendor allow entry had no effect for the primary-domain recipients it was supposed to protect. The relay reached the Inbox on its own clean reputation, not because anyone had allow-listed it.
A "missing cmdlet" usually means missing rights
The first attempt to add the block failed with New-TenantAllowBlockListItems is not recognized. Exchange Online only imports the cmdlets your session's roles permit, so a read-only role surfaces as a missing cmdlet rather than an access-denied error. Get-Command *TenantAllowBlockListItems* showed only the Get- verb loaded. Reconnecting with a write-capable role fixed it.
05The fix
Block the lookalike tenant-wide; protect the real relay with an authentication-conditioned rule.
Two changes, both designed to be immune to the scoping trap above. The block goes in the Tenant Allow/Block List so it covers every recipient regardless of policy. The allow is a transport rule that only fires when the vendor's own DKIM validates — so a forged From address gets no free pass, and malware/Safe Attachments scanning still applies.
# 1 — block the lookalike domain for the whole tenant, no expiry New-TenantAllowBlockListItems -ListType Sender -Block ` -Entries "keystonebolts.com" -NoExpiration ` -Notes "lookalike of keystonebolt.com (website-form pretext)" # 2 — guarantee the real form relay is never quarantined, # but ONLY when the vendor's DKIM actually passes New-TransportRule -Name "Allow web form relay (DKIM pass)" ` -From "info@webvendor.example" ` -HeaderMatchesMessageHeader "Authentication-Results" ` -HeaderMatchesPatterns "dkim=pass[^;]*header\.d=webvendor\.example" ` -SetSCL -1 ` -Comments "website form submissions must never be quarantined" ` -Priority 0
The block does double duty: it stops the attacker's follow-up mail — the second-stage message sent directly from the lookalike domain, which is where the actual payload would arrive — and, per Microsoft's docs, a sender/domain block also stops users sending to that domain (worth a test send to confirm). The transport rule's DKIM condition is the important bit: an unconditioned "allow this sender" rule is itself a spoofing hole.
The one-line model
Put sender allows and blocks in the Tenant Allow/Block List, not inside an anti-spam policy — policy lists silently depend on policy scoping. And never allow a relay by address alone; condition it on the relay's own DKIM, or you've built the spoofing bypass yourself.
06Triage checklist for relayed form mail
When the envelope is trusted and identical every time, the verdict lives entirely in the fields.
- Confirm it's the real relay. Expected From address, expected Message-Id domain, DKIM pass for the vendor. If authentication fails, you're looking at spoofing of the relay itself — a different problem.
- Identify the form from the subject (quote request vs contact-us). They behave differently on reply.
- Extract the submitted email domain, phone and website.
- Look up the claimed company independently and compare its real domain and phone against the submitted values — character by character. A one-character domain difference is decisive.
- Check the submitted domain's age and whether it hosts a real site.
- Read the request. Real quote requests name parts, quantities, materials, drawings. A vague "business opportunity" whose only ask is an email address is a pretext.
- For submissions with attachments, confirm Safe Attachments scanned them before advising anyone.
- For contact-us forms, warn that Reply goes straight to the submitted address — lookalike domains included.
- If any indicator fails, block the submitted domain in the Tenant Allow/Block List and run a message trace for any outbound replies.
# did anyone reply to the lookalike before the block went in?
Get-MessageTraceV2 -RecipientAddress "*@keystonebolts.com" `
-StartDate (Get-Date).AddDays(-10) -EndDate (Get-Date) |
Select Received, SenderAddress, RecipientAddress, Subject, Status
- Symptom: a clean, fully authenticated website-form notification cleared as a legitimate sales inquiry.
- Root cause: the sender's email domain was a one-character lookalike of a real vendor's — a first-stage pretext with no payload, invisible to envelope and body checks alike.
- Both reviews missed it: the automated pass judged the envelope ("bulk platform → spam"), the analyst judged the body ("no payload → legitimate"); neither compared the field values to the real vendor.
- Exposed underneath: a forgotten custom anti-spam policy scoped to the whole domain meant the allow/block lists in Default protected almost nobody.
- Fix: block the lookalike in the Tenant Allow/Block List; allow the real relay with a DKIM-conditioned transport rule; move sender allows/blocks out of policy lists.
Sources & further reading
- Manage the Tenant Allow/Block Listwhy TABL entries apply tenant-wide, independent of policy scope
- Configure anti-spam policieshow recipient-to-policy matching and priority actually work
- Anti-spam message headersreading SFV:NSPM vs SFV:SKA and the SCL verdict
- Mail flow (transport) rulesconditioning a rule on an authentication-results header
Anonymised from real support work. Client, vendor, domain, person and ticket details have been changed; the mechanism and the mistake are real.
Comments
Questions or corrections welcome. Sign in with GitHub to join the thread.