How attackers spoof email — and how to catch them

A practical guide to SMTP spoofing, SPF, DKIM, and DMARC — with a walkthrough of the FoilLab mail-trap challenge.

The trust model email was built on

SMTP — Simple Mail Transfer Protocol — was designed in 1982 for a world where every node on the internet was a trusted research institution. The MAIL FROM field (the envelope sender) and the From: header are free-text strings. Nothing in the original protocol verifies them. An attacker can set them to anything.

This means a message that looks like it came from ceo@example.com can originate from any mail server on the planet. This is email spoofing, and it has been responsible for billions of dollars in Business Email Compromise (BEC) fraud.

How SMTP spoofing works

When your mail server receives a message, the conversation looks like this:

EHLO attacker.example
MAIL FROM: <ceo@victim-corp.com>        ← envelope sender, free text
RCPT TO: <finance@victim-corp.com>
DATA
From: CEO Name <ceo@victim-corp.com>     ← display header, also free text
Subject: Urgent wire transfer
...

The receiving server sees the From: header and renders it for the user. Unless the server is configured to check authentication records, it has no idea the sender is lying.

SPF — the first line of defense

Sender Policy Framework (SPF) is a DNS TXT record that lists which IP addresses are allowed to send mail for a domain:

v=spf1 include:_spf.google.com ~all

When a mail server receives a message, it checks whether the sending IP is in the SPF record. If not, the result is FAIL or SOFTFAIL (~all). But SPF only checks the envelope sender (MAIL FROM), not the From: header the user sees. An attacker can spoof the header while using a valid envelope that passes SPF — this is called a header-from mismatch.

DKIM — cryptographic signature

DomainKeys Identified Mail (DKIM) adds a cryptographic signature to outgoing messages using a private key. The receiving server looks up the public key in DNS and verifies the signature covers the message body and selected headers:

DKIM-Signature: v=1; a=rsa-sha256; d=example.com; s=mail;
  h=from:to:subject:date;
  bh=47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=;
  b=MEQCIBj...

If a message arrives claiming to be from @example.com but has no valid DKIM signature (DKIM: NONE), or if the signature verification fails (DKIM: FAIL), that is a strong signal the message is spoofed or tampered with.

DMARC — the policy layer

DMARC (Domain-based Message Authentication, Reporting & Conformance) ties SPF and DKIM together. It requires that at least one of them aligns with the From: header domain, and lets domain owners specify what to do with failures:

v=DMARC1; p=reject; rua=mailto:dmarc@example.com

p=none = monitor only. p=quarantine = send to spam. p=reject = drop the message. Most domains have no DMARC record at all, or use p=none because they are afraid of breaking legitimate mail flows.

Reading email headers like a forensic analyst

When you receive a suspicious message, download the raw source and look for:

  • Received: headers — trace the actual hop path, bottom to top. The bottom-most Received header is the origin.
  • Authentication-Results: — shows the mail server's SPF/DKIM/DMARC verdict
  • X-Originating-IP: — some servers add the client IP that submitted the message
  • DKIM-Signature d= vs From: domain mismatch — the signed domain should match the From: domain
  • Message-ID domain — should match the sender's domain

FoilLab mail-trap challenge walkthrough

The mail-trap challenge gives you an SMTP transaction log. The log shows a message claiming to be from cfo@acmecorp.io, but the metadata tells a different story:

Authentication-Results: mx.foillab.internal;
  spf=fail (no matching record) smtp.mailfrom=suspicious-host.xyz;
  dkim=none

SPF fails because the sending server (suspicious-host.xyz) is not in acmecorp.io's SPF record. DKIM is absent entirely. The message body is base64-encoded — decoding it reveals the flag.

Steps to solve:

  1. Download smtp.log from the challenge page
  2. Find the DATA section — the body will be a base64 blob
  3. Run echo "BASE64STRING" | base64 -d to decode it
  4. The decoded text contains the flag in FOIL{...} format

Defending your organization

  • Publish a strict DMARC policy (p=reject) for your domain — it prevents spoofed messages from reaching recipients
  • Sign all outbound mail with DKIM using at least 2048-bit RSA keys (or Ed25519)
  • Keep your SPF record tight — use -all (hard fail) not ~all once you've audited all your sending sources
  • Enable DMARC reporting (rua) and process the reports weekly — you will see who is trying to spoof you
  • Train users to check the full From: address, not just the display name
← all postsTry the mail-trap challenge →