Case Study 1 — The Invoice That Changed Its Bank

A mid-size mechanical contractor nearly wired $214,000 to a fraudster because one email looked like it came from a trusted vendor. The headers, read bottom-up, told a different story than the friendly From: line — and the difference was the whole case.

Background

Cascade Mechanical Contractors had paid its steel supplier, Ferro Steel Supply, by ACH for eight years. On a Wednesday afternoon, an accounts-receivable clerk received what appeared to be a routine email from her usual Ferro contact: an updated invoice for a standing order, with a polite note that "our bank has changed — please use the new remittance details below for this and future payments." The display name read "Ferro Steel Supply — Accounts." The clerk, mid-quarter and busy, opened a change-of-banking ticket and queued the wire.

A second approver paused. The new account was at an out-of-state bank Cascade had never used for Ferro, and the email's tone was slightly off — over-eager about "future payments." He flagged it to IT, and the incident landed on the desk of the company's incident-response lead. The wire was held with twenty minutes to spare. Now the question was not just "is this fraud?" but "can we prove it, preserve it, and report it correctly?" — a 🛡️ Incident Response problem that is, at its core, an email-header forensics problem.

The investigation

The first move was preservation, not analysis. The IR lead exported the message as an original .eml with full headers intact — not a forwarded copy, which would have buried the real headers inside a new message and stripped the Authentication-Results. He hashed the .eml, logged it, and only then began reading. The original is sacred applies to a single email as surely as to a disk image.

The body was unremarkable; the headers were not. Reading the Received: chain bottom-up:

Return-Path: <ar@ferro-supply-billing.com>            ← envelope sender (NOT ferrosteel.com)
Received: from mx.cascade-mech.com (mx.cascade-mech.com [203.0.113.10])
        by store.cascade-mech.com with LMTP id 51bd
        for <receivables@cascade-mech.com>; Wed, 03 Apr 2024 15:18:22 -0400   ← (3) internal hop [TRUSTED]
Received: from out3.cheapmailhost.example (out3.cheapmailhost.example [198.51.100.91])
        by mx.cascade-mech.com (Postfix) with ESMTPS id a04f
        for <receivables@cascade-mech.com>; Wed, 03 Apr 2024 15:18:21 -0400   ← (2) OUR EDGE MX [BOUNDARY]
Received: from [10.10.0.3] (unknown [196.251.86.14])
        by out3.cheapmailhost.example with ESMTPA id 7c20;
        Wed, 03 Apr 2024 19:18:16 +0000                                       ← (1) origin claim [NOT trusted]
Authentication-Results: mx.cascade-mech.com;
        spf=fail (198.51.100.91 not permitted) smtp.mailfrom=ferro-supply-billing.com;
        dkim=none; dmarc=fail (p=none) header.from=ferrosteel.com
Message-ID: <20240403191815.7c20@ferro-supply-billing.com>
From: "Ferro Steel Supply — Accounts" <billing@ferrosteel.com>
X-Mailer: PHPMailer 6.8.0 (https://github.com/PHPMailer/PHPMailer)

Hop (1), the claimed origin, was written by out3.cheapmailhost.example — a server Cascade did not control — and recorded a connecting IP of 196.251.86.14, geolocating to a hosting range with no business nexus to a regional steel supplier. Hop (2), Cascade's own edge MX, was the trust boundary: it had genuinely received the message from 198.51.100.91, a bulk-mail relay, not from Ferro's real mail infrastructure. Everything below the boundary was only as honest as those foreign relays.

The authentication results closed the case. SPF failed — the sending IP was not authorized for the envelope domain ferro-supply-billing.com. DKIM was absent. And DMARC failed against the real ferrosteel.com. Two domains were in play: the envelope Return-Path used a lookalike (ferro-supply-billing.com), while the visible From: displayed the genuine billing@ferrosteel.com — a classic display spoof made possible because Ferro's real DMARC policy was only p=none (monitor), so the receiver delivered the message instead of quarantining it. The X-Mailer: PHPMailer line betrayed a bulk-mail script, not a corporate MTA.

A quick correlation sealed it. The IR lead pulled the last legitimate Ferro invoice from the mailbox: it had arrived from Ferro's real mail servers with spf=pass and dkim=pass, and its Message-ID domain matched ferrosteel.com. The fraudulent message shared none of that. He preserved both, wrote the finding, and routed it: the held wire was cancelled, the bank's fraud unit notified, and the matter referred to law enforcement, who would use a preservation letter and warrant to pursue the receiving account and the relay logs.

Recovery vs. Forensics. Stopping the wire was the operational win; proving the fraud was the forensic one. The endpoint copy of the email proved Cascade received and viewed the spoof at a specific time; only provider/bank records (reachable by legal process) could attribute the receiving account and the relay. The IR lead documented exactly which he had and which he did not — and never presented the endpoint copy as the whole story.

The analysis

  1. Read the Received: chain bottom-up to the trust boundary. The friendly From: is the easiest field to forge; the header your own edge MX wrote is the one you can trust. The origin lives at the bottom of the chain, and your conclusions anchor at the first server you control.
  2. SPF pass is not "genuine," and SPF fail is not always "fraud" — DMARC alignment is the test. SPF authenticates the envelope, not the visible From:. Here, two different domains (envelope lookalike vs. displayed real domain) were the giveaway, and DMARC fail tied the authentication gap to what the victim actually saw.
  3. Policy matters as much as protocol. Ferro's DMARC was only p=none, so the receiver delivered a message it could have quarantined. The protocols detected the spoof; a weak published policy let it through. Part of the IR report was a recommendation that Ferro — and Cascade — move to p=quarantine or p=reject.
  4. Preserve the original, with headers, before you analyze. A forwarded copy would have destroyed the Received: chain and Authentication-Results. Exporting the verbatim .eml, hashing it, and logging it kept the evidence admissible and reproducible.
  5. Know where forensics ends and legal process begins. The endpoint proved local receipt and timing; attributing the relay, the receiving bank account, and the human behind it required instruments the SCA ladder defines — preservation letters and warrants served on providers and banks, not anything recoverable from Cascade's own image.

Discussion questions

  1. The clerk nearly acted on a message that passed the eye test. What process controls — independent of any technical defense — should a finance team have for banking-detail changes, and how do they complement header forensics?
  2. Walk the Received: chain above aloud. Identify the connecting IP at each hop, name the trust boundary, and state which single header you would quote in a one-paragraph executive summary.
  3. The real ferrosteel.com had p=none. Explain to a non-technical CFO what would have changed if the policy were p=reject, and what the trade-offs of stricter policies are.
  4. ⭐ Suppose the attacker had instead compromised a real mailbox at Ferro and sent from Ferro's genuine servers, so SPF, DKIM, and DMARC all passed. Which artifacts in this chapter would you turn to next, and how would your conclusion language change when the authentication triangle no longer helps you?
  5. Draft the two-sentence chain-of-custody note for the preserved .eml, and the one sentence in your report that distinguishes what the endpoint copy proves from what only the bank's and provider's records can establish.