Browser, email and cloud sync · Lesson 4 of 6
Email files and header analysis
11 min read
Email is evidence of communication: who claimed to write to whom, when, through which systems and with which attachments. On an endpoint it sits in mailbox files that keep messages long after the user believes they are gone, and every received message carries a header that records its route. This lesson covers the mailbox formats found on Windows computers, how deleted messages survive, and how to read a header so that you can name the originating IP, state reliable times in UTC and decide whether the sender is genuine.
Email stores on the endpoint
Outlook PST and OST
A PST (Personal Storage Table) is a classic Outlook mailbox file. Outlook 2003 introduced the Unicode PST, which removed the 2 GB limit of the older ANSI format; the default maximum size is 50 GB from Outlook 2010 onward. Every PST and OST starts with the 4-byte signature !BDN (21 42 44 4E) at offset 0. At offset 8, the client signature SM marks a PST and SO an OST; the 2-byte version at offset 10 is 14 or 15 in an ANSI file, while Unicode files use 23 or higher (OST files from Outlook 2013 onward commonly show 36, a variant with 4 KB pages). A PST "password" does not encrypt the data: the content is at most obfuscated with a reversible encoding that parsing tools undo without the password.
An OST is the offline cache of an Exchange or Microsoft 365 mailbox in Cached Exchange Mode. It is tied to the Outlook profile and account, so Outlook will not open it as a standalone file, but forensic parsers read it directly. An OST can still hold items that have since been deleted from the server, if the client did not synchronize before collection or if the data remains in unallocated blocks. It can also be incomplete, because the cache may cover only a recent period set by a synchronization slider.
| File | Typical location (Windows 10/11, classic Outlook) |
|---|---|
| OST | %LOCALAPPDATA%\Microsoft\Outlook\<account>.ost |
| PST (Outlook 2010 and later) | %USERPROFILE%\Documents\Outlook Files\*.pst |
| PST (older or migrated profiles) | %LOCALAPPDATA%\Microsoft\Outlook\*.pst |
The new Outlook for Windows stores data differently and does not use OST files the way classic Outlook does, so confirm which client was in use before concluding that a mailbox cache is missing. Lesson 4.5 covers webmail and cloud account traces.
Thunderbird profiles
Thunderbird keeps everything under %APPDATA%\Thunderbird\Profiles\<profile>\. POP and local accounts are in Mail\, IMAP accounts in ImapMail\<server>\. Each folder is one MBOX file with no extension (Inbox, Sent), in which every message begins with a separator line starting with From (Thunderbird writes From - followed by a date). Subfolders live in a directory with the suffix .sbd. Each MBOX has a companion .msf file, an index (summary) that Thunderbird can rebuild, so it is not the message store. A deleted message is first only flagged as expunged (bit 0x0008 in the X-Mozilla-Status header) and stays inside the MBOX until the folder is compacted. The profile also contains global-messages-db.sqlite, the search index, which holds subjects, participants and indexed body text and may not reflect recent deletions at once. Profiles set to the optional maildir format store one file per message instead.
Single messages: EML and MSG
An EML file is one message in the RFC 5322 text format, header and MIME body exactly as transmitted. An MSG file is a single Outlook item saved as an OLE compound file (signature D0 CF 11 E0 A1 B1 1A E1). Its properties are streams named __substg1.0_<tag><type>; the original internet header of a received message is property 0x007D, so look for the stream __substg1.0_007D001F (007D001E when the string is stored as ANSI).
Deleted messages
Deletion in Outlook happens in layers. A normal delete moves the item to Deleted Items, which is still an ordinary folder. Emptying that folder, or Shift+Delete, removes it from the folder tree; for Exchange and Microsoft 365 mailboxes the server then keeps it in the Recoverable Items folder for a retention period, which is a server-side source that needs a legal request or a tenant export rather than the endpoint. Inside the PST or OST, the freed blocks are not wiped immediately, so parsers can recover deleted and orphaned items from unallocated PST space. The open-source libpff tool pffexport can export recovered items alongside normal ones; readpst converts PST content to MBOX or EML files; commercial PST viewers and email forensic suites do the same with a graphical interface, and a working copy of a Thunderbird profile can be reviewed in Thunderbird itself. Always state which method produced each message.
Reading the header
RFC 5322 defines the message header. Most fields are typed by the sender's client and prove nothing about origin.
| Header | Written by | Can the sender forge it? |
|---|---|---|
From, To, Cc, Subject | Sender's client | Yes, freely |
Date | Sender's client (local time with offset) | Yes, and the clock may be wrong |
Message-ID | Client or submission server | Yes; the domain part often reveals the sending system |
In-Reply-To, References | Client, for threading | Yes |
Reply-To | Client | Yes; a mismatch with From is a phishing indicator |
Return-Path | Final delivering server, from the envelope sender | The address is sender-chosen; SPF tests its domain |
Received | Each server, at the top | Yes below the trust boundary; no for hops your servers added |
Authentication-Results | Receiving server | Trust only those added by your own server |
DKIM-Signature | Signing server of domain d= | Not for a domain without its private key |
X-Originating-IP and similar | Some providers' submission servers | Provider-specific, often removed today |
The Received chain
Each server that accepts the message adds one Received header at the top, so the newest hop is first and the route is read bottom-up. A typical line has the form from <claimed host> (<reverse DNS> [<IP>]) by <receiving host> with <protocol> id <queue ID>; <date>. The name after from is what the connecting system announced and can be anything; the IP in brackets was recorded by the receiving server from the TCP connection. The protocol word tells you more: ESMTPS means TLS, and ESMTPSA means the client authenticated, which marks the submission hop.
Find the trust boundary: the lowest Received header written by a server you trust, normally the recipient organization's MX. The IP it recorded is reliable. Everything below it was written by systems outside your control and may be forged, so treat an originating IP found there as a lead to corroborate, for example with the provider's logs. Convert every hop to UTC, compute the delays, and check that the times rise; a negative delay of seconds is clock drift, while minutes or hours point to a wrong clock, a queued message or a forged header.
Authentication results: SPF, DKIM, DMARC and ARC
The recipient's server records its checks in Authentication-Results (or the older Received-SPF).
- SPF checks whether the connecting IP may send for the envelope sender domain, shown as
spf=pass smtp.mailfrom=<address>. It says nothing about the visibleFrom. - DKIM verifies a signature in
DKIM-Signature, whered=is the signing domain ands=the selector; the public key is published at<selector>._domainkey.<domain>. The result isdkim=passordkim=failwithheader.d=. - DMARC passes only if SPF or DKIM passes and that domain aligns with the
Fromdomain. It is the only one of the three that protects the address the user sees. - ARC headers (
ARC-Seal,ARC-Message-Signature,ARC-Authentication-Results, numberedi=1,i=2…) preserve earlier results when a mailing list or forwarder modifies the message.
Phishing indicators are a display name that imitates a brand while the address differs, From, Return-Path and Reply-To in different domains, and dmarc=fail.
Worked example: a phishing message
A user at corp.example received a message that appears to come from a bank. The header (top to bottom) contains:
| # | Header line |
|---|---|
| R1 | Received: from mx1.corp.example (10.20.1.15) by mbx03.corp.example with ESMTPS id 91AC4; Tue, 3 Mar 2026 12:14:39 +0000 |
| R2 | Received: from relay.bank-secure.example (relay.bank-secure.example [198.51.100.27]) by mx1.corp.example with ESMTPS id 3B77E; Tue, 3 Mar 2026 12:14:37 +0000 |
| R3 | Received: from smtp.bank-secure.example ([198.51.100.20]) by relay.bank-secure.example with ESMTP id A93F0; Tue, 3 Mar 2026 13:14:05 +0100 |
| R4 | Received: from [192.168.1.23] (unknown [203.0.113.45]) by smtp.bank-secure.example with ESMTPSA id 7C1D2; Tue, 3 Mar 2026 09:13:58 -0300 |
| AR | Authentication-Results: mx1.corp.example; spf=pass smtp.mailfrom=bank-secure.example; dkim=pass header.d=bank-secure.example header.s=s1; dmarc=fail (p=none) header.from=bank.example |
| From | From: "Bank Security" <security@bank.example> |
| Return-Path | Return-Path: <bounce@bank-secure.example> |
| Reply-To | Reply-To: <verify-team@freemail.example> |
| Message-ID | Message-ID: <a81f3c09.7d2e@vps-4821.hosting.example> |
| Date | Date: Tue, 3 Mar 2026 09:06:10 -0300 |
Walk bottom-up and convert. UTC = local time minus the offset.
| Hop | Written by | Local time | UTC | Delay |
|---|---|---|---|---|
| R4 | smtp.bank-secure.example | 09:13:58 −0300 | 09:13:58 + 3 h = 12:13:58Z | — |
| R3 | relay.bank-secure.example | 13:14:05 +0100 | 13:14:05 − 1 h = 12:14:05Z | 7 s |
| R2 | mx1.corp.example | 12:14:37 +0000 | 12:14:37Z | 32 s |
| R1 | mbx03.corp.example | 12:14:39 +0000 | 12:14:39Z | 2 s |
The times rise consistently and the whole route took 41 s. The Date header converts to 09:06:10 + 3 h = 12:06:10Z, 7 min 48 s before submission: a sender-side value from a queued message or a skewed clock, not a time to report as receipt.
Trust boundary. R2 is the first header written by the recipient's own MX, so the reliably recorded source is 198.51.100.27. R3 and R4 were written by the sender's infrastructure; R4 names 203.0.113.45 as the client that authenticated (ESMTPSA), apparently from a private address behind NAT (HELO [192.168.1.23]). Report it as the claimed originating IP, to be corroborated by the provider of bank-secure.example.
Verdicts. SPF and DKIM both pass, but for bank-secure.example. DMARC fails because neither domain aligns with bank.example in From, and the bank's p=none policy let the message through. With replies diverted to a freemail address and a Message-ID from a hosting VPS, the conclusion is: spoofed sender, sent through attacker-controlled infrastructure, received by the organization at 2026-03-03T12:14:37Z.
MIME structure and attachment hashes
A message with attachments is usually multipart/mixed: one part is a nested multipart/alternative with text/plain and text/html versions of the body, and each further part is an attachment with Content-Disposition: attachment; filename="invoice.pdf". Parts are separated by the boundary string declared in Content-Type. Binary attachments use Content-Transfer-Encoding: base64, in which every 4 characters encode 3 bytes, in lines of at most 76 characters. Hash the decoded bytes, never the base64 text: a PDF part beginning JVBERi0xLjcK decodes to %PDF-1.7 plus a newline, and only the decoded file's SHA-256 will match a copy found elsewhere, such as in the Downloads folder or Outlook's secure temporary folder (its path is in the OutlookSecureTempFolder registry value).
Exam focus: Expect to read aReceivedchain bottom-up, convert hop times with different offsets to UTC, and choose between the trusted receipt time and theDateheader. Be ready to name the first reliable IP, explain why SPF and DKIM can pass while DMARC fails, and hash a decoded attachment. Know the!BDNsignature, the default PST and OST paths, and that Thunderbird keeps unexpunged deletions in the MBOX until compaction.
Key points
- PST and OST files start with
!BDN; the OST is a profile-bound cache that can contain items already deleted on the server, and deleted items can be recovered from unallocated PST blocks. - Thunderbird stores one MBOX file per folder, with
.msfindexes andglobal-messages-db.sqlite; deleted messages stay in the MBOX until compaction. Receivedheaders are added at the top, so read them bottom-up and trust only the hops your own servers wrote.- Report receipt as the trusted server's
Receivedtime in UTC; theDateheader is the sender's claim. - DMARC ties SPF or DKIM to the visible
Fromdomain; passing SPF and DKIM for another domain does not authenticate the sender. - Hash attachments after base64 decoding.