Franky opens her email in the morning and sees the following email in her inbox:

Franky’s heart raced as she recalled the “pleasure” of eating a cupcake from the CFO a few months back. Not wanting to eat another one of… those cupcakes, Franky’s lizard brain kicks in and she clicks on the link instantly. There is no way she could refuse when the Chief Financial Officer offered one of her cupcakes! On the page, the typical username and password fields stared back at her. She starts typing her username… Suddenly, her rational brain comes back with a vengeance. Why is the password manager not offering to fill in username and password? Dread overcomes her. This wasn’t right! She hadn’t typed the password yet, but what if it was already too late? It should still be safe, right?

Have you or someone you know ever been in this situation? Then you were the target of a CEO fraud, where someone impersonates a CEO to get their hands on sensitive information. But how real the threat of the CFO’s cupcakes is? Let’s find out!

To answer these questions, let’s follow the email from creation to delivery. We will look at the email headers to find out if the email was really sent from Marly. If you want to follow along with an example, open any email sent from an external sender and find the headers. In Outlook, they can be found by opening the email in a new window, clicking on File, then Properties. The headers are listed under “Internet Headers” (as opposed to football headers?). In other tools, they are sometimes found under an option called “raw” which will show the raw email including the headers. MxToolbox has a handy list of how to get to the headers in different email clients.

Mail User Agent (MUA)

After clicking “send” in the email client (or the Email User Agent in RFC 5068 parlance), the story of the headers begins immediately. The email client (Outlook, Apple Email, etc.) already adds the first few headers.

Date: Wed, 31 Jul 2024 12:30:25 +0000 (UTC)
From: Fictional Franky <fictional.franky@aol.com>
To: "Mythical Marly" <mythical.marly@m365.example.com>
Reply-To: Fictional Franky <fictional.franky@aol.com>
Message-ID: <186715119.2011275.1722429025825@email.yahoo.com>
Subject: =?UTF-8?Q?_Invoice_Reminder:_Pay_Now_or_O?=
 =?UTF-8?Q?ur_CFO_Might_Start_Baking_=F0=9F=93=8A=F0=9F=8D=B0_?=
MIME-Version: 1.0
Content-Type: multipart/alternative; 
    boundary="----=_Part_2011274_1670263300.1722429025822"
X-Emailer: WebService/1.1.22544 AolEmailNorrin
Content-Length: 40149

The clients usually adds a Date, From, To, and Subject header (though, according to RFC 5322, only the Date and From headers are mandatory). They pretty much do what they say. The Message-ID is an ID generated either by the client or the Email Submission Agent (see next chapter). When replying to an email, the email client will add an In-Reply-To header containing the Message-ID. This makes it possible for email clients to group all messages that are replies to each other in a single thread. Another header of note is the X-Emailer header. If present, it shows which program was used to write the message.

The Reply-To header specifies an alternate email where a reply will be sent to. This is an optional header. Whenever the Reply-To header exists and points to a different email than the From header, it is worth investigation why. It could be benign, or it could be an threat actor that sends email from fictional.franky@aol.com but tries to receive the replys on a email under their control.

We once had a case where a customer was worried that not just the email account, but also the laptop was infected. The X-Mailer header showed that the email was sent from a Mac, while the customer only used Windows laptops. This was a strong indication that the laptop itself was not infected.

Of course, all these headers are added by the client and can be tampered with. The Mail Submission Agent (MSA, see next chapter) should validate that the From header is a valid value for the current user. The other fields are usually not validated, as the MSA does not have enough information. The Date header, for example, can be in the past in case the email was written offline and then later sent by the email client.

The email is then sent to the MSA.

Mail Submission Agent (MSA)

The Mail Submission Agent (MSA) is the server where the emails are sent by the Mail User Agent. Basically, the server identified by the “smtp://” gibber-gabber we sometimes have to add manually to the email clients. The MSA verifies that the user is allowed to send emails for the email account listed in the From header and figures out how to forward the email to the recipients.

It, of course, also adds some headers! First, it adds a Received header to the top that states which Mail User Agent sent the email to the server. The server also adds a DKIM-Signature. The DKIM Signature is intended to verify that the email is not tampered with during transmission. More on the security features towards the end.

DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=aol.com; s=a2048; t=1722429146; bh=6ydO6GhkZaP9pOLY53HnWoZDVdbdjSBTwBNNzxVp3Uw=; h=Date:From:To:Subject:References:From:Subject:Reply-To; b=C3HMmETpQBa+I3zTypH2bNcrwIA7DphSNGRBh2dsyx48tENigvyREvImL0h+1lakBo0qt3dE5gygFQf2C9Y3UAelT7yVHmFReLV8P3ssQtYTdT03dIdKJUot/7SbinEAaLL0qjleSY7jCtj5Uk1WMWTSJRi1JOFleuvs3Vuy61n+nrfVIu8/ZRrQg3cBX0jjgRnfFVX/llArg0ijn/iIoqrXFxsw7iFoi78TjZbn0pFOD8cs0/xhdfh/aCMsq/xPpU2NYHFpZX7EYJuuqdog8kM+Yh51c6epwbMn6gn0ako8dJ7Jlx+L34MtD4FJPLRqeZxG4s3d2sNVpxE17Au6zA==
Received: from sonic.gate.mail.ne1.yahoo.com by sonic315.consmr.mail.ne1.yahoo.com with HTTP; Wed, 31 Jul 2024 12:32:26 +0000

For each email address in the To, CC and BCC headers, the server forwards the email to the Mail Transfer Agent (MTA) for that domain. The MTA is listed in the MX DNS record. For example, if an email is to be sent to “compass-security.com”, it will query the MX record of the domain:

$ dig compass-security.com +noall +answer MX 
compass-security.com.   5       IN      MX      0 compasssecurity-com01i.mail.eo.outlook.com.

Here, the emails will be sent to the MTA compasssecurity-com01i.mail.eo.outlook.com.

Mail Transfer Agent (MTA)

The Mail Transfer Agent receives the email from a Mail Submission Agent and forwards the email to potentially more Mail Transfer Agents until the email reaches the Mail Delivery Agent, which is responsible for managing the receiver’s mailbox.

Each Mail Transfer Agent adds its own Received header to help trace through which servers the email was sent to the destination. There may be many more MTAs as emails can take very convoluted paths through the internet. Each server that handles the email will add a Received header to the top of the email.

Received: from GVAP278MB0183.CHEP278.PROD.OUTLOOK.COM (2603:10a6:710:38::9) by
 ZR1P278MB1233.CHEP278.PROD.OUTLOOK.COM with HTTPS; Wed, 31 Jul 2024 12:32:33
 +0000
Received: from ZRAP278CA0016.CHEP278.PROD.OUTLOOK.COM (2603:10a6:910:10::26)
 by GVAP278MB0183.CHEP278.PROD.OUTLOOK.COM (2603:10a6:710:38::9) with
 Microsoft SMTP Server (version=TLS1_2,
 cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.7828.21; Wed, 31 Jul
 2024 12:32:30 +0000
Received: from ZR2PEPF0000012C.CHEP278.PROD.OUTLOOK.COM
 (2603:10a6:910:10:cafe::74) by ZRAP278CA0016.outlook.office365.com
 (2603:10a6:910:10::26) with Microsoft SMTP Server (version=TLS1_2,
 cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.7784.35 via Frontend
 Transport; Wed, 31 Jul 2024 12:32:29 +0000
Received: from sonic315-47.consmr.mail.ne1.yahoo.com (66.163.190.173) by
 ZR2PEPF0000012C.mail.protection.outlook.com (10.167.241.36) with Microsoft
 SMTP Server (version=TLS1_3, cipher=TLS_AES_256_GCM_SHA384) id 15.20.7828.19
 via Frontend Transport; Wed, 31 Jul 2024 12:32:28 +0000

According to RFC 5322, header ordering should be kept. But, more importantly, Received headers must be kept:

However, for the purposes of this specification, header fields SHOULD NOT be reordered when a message is transported or transformed.  More importantly, the trace header fields and resent header fields MUST NOT be reordered, and SHOULD be kept in blocks prepended to the message.”

(Emphasis mine)

This means that the Received headers (one of the trace headers mentioned in the specification) can be read bottom to top to determine an email’s path through the network of tubes. As per the standard, other headers should still be in order. In practice, we noticed that the SHOULD is interpreted according to RFC 2119, as some email clients still reorder headers.

In this case, the email took the following path:

  • Yahoo (from sonic315-47.consmr.mail.ne1.yahoo.com) sent it to Microsoft (at ZR2PEPF0000012C.mail.protection.outlook.com).
  • Microsoft then forwards it to Office365 (ZRAP278CA0016.outlook.office365.com)
  • and then to outlook.com (GVAP278MB0183.CHEP278.PROD.OUTLOOK.COM), the Mail Delivery Agent.

Another interesting thing in the Received headers is the “with” and “via” part. These parts tell us which server software was used and via which channel the message was received. However, with and via are not mandatory and not always present.

Mail Delivery Agent (MDA)

The Mail Delivery Agent is the penultimate stop of the email. The Mail Delivery Agent is responsible for delivering the email to the receiver’s inbox. In addition, it (or one of the MTAs before it) should also check some security features of emails.

For example, Microsoft’s MDA will add another valuable header with the results of the different security checks they perform on the email: the Authentication-Results header:

Authentication-Results: spf=pass (sender IP is 66.163.190.173)
 smtp.mailfrom=aol.com; dkim=pass (signature was verified)
 header.d=aol.com;dmarc=pass action=none header.from=aol.com;compauth=pass
 reason=100
Received-SPF: Pass (protection.outlook.com: domain of aol.com designates
 66.163.190.173 as permitted sender) receiver=protection.outlook.com;
 client-ip=66.163.190.173; helo=sonic315-47.consmr.mail.ne1.yahoo.com; pr=C

The Authentication-Results header lists which checks Microsoft performed and if they passed or not. In this example, both SPF and DKIM checks were successfully. But what are SPF and DKIM?

Email Security

The three most important email security features are:

  • SPF, the Sender Policy Framework, defines who is allowed to send emails from a certain domain.
  • DKIM, the DomainKeys Identified Email, signs parts of the email contents to make sure that it was not tampered with in transit.
  • DMARC, the Domain-based Message Authentication, Reporting and Conformance, that states what should happen if a check fails.

Sender Policy Framework (SPF)

Let’s first look at SPF (defined in RFC 7208). How does Microsoft know that 66.163.186.147 is an IP that is allowed to send emails from aol.com? Through DNS, of course! The SPF record is stored on the domain as a TXT record.

$ dig @9.9.9.9 aol.com +noall +answer TXT | grep 'v=spf'
aol.com.                3542    IN      TXT     "v=spf1 redirect=_spf.mail.yahoo.com"

This record states that the SPF entries are stored in _spf.mail.yahoo.com.

$ dig @9.9.9.9 _spf.mail.yahoo.com +noall +answer TXT | grep 'v=spf'
_spf.mail.yahoo.com.    1733    IN      TXT     "v=spf1 ptr:yahoo.com ptr:yahoo.net ?all"

The ptr:yahoo.com indicates that any IP that has a reverse mapping to yahoo.com (or a subdomain of it) is allowed to send emails for yahoo.com (and, because of the redirect entry in aol.com, also for aol.com). Fun fact: Check the RFC, section “5.5 “ptr” (do not use)“. Wait what? Something is specified that should not be used, and a large company decides “that is handy, let’s use it!”… Anyways.

The Received header shows that the IP of the sender was 66.163.190.173. Let’s check where the reverse mapping of that IP points to.

$ dig @9.9.9.9 173.190.163.66.in-addr.arpa +noall +answer ptr
173.190.163.66.in-addr.arpa. 600 IN     PTR     sonic315-47.consmr.mail.ne1.yahoo.com. 

Note that the IP has to be reversed. Because this is a common check, dig has a shortcut for it dig -x 66.163.190.173.

And yes, the IP 66.163.190.173 points to sonic315-47.consmr.mail.ne1.yahoo.com, which is a subdomain of yahoo.com and is thus allowed to send emails for yahoo.com and aol.com.

With that, the MDA knows that whoever sent the email is allowed to send emails from aol.com. It is important to remember, however, that this does not validate the actual sender. Only that the MSA was allowed to send emails for aol.com. If Yahoo had a bug in their system which allowed threat actors to send emails in the name of other Yahoo users, SPF would not catch that.

SPF check: PASS!

DomainKeys Identified Mail (DKIM)

Now let’s turn to DKIM (defined in RFC 6376). Following the DKIM signature from the headers:

DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=aol.com; s=a2048; t=1722429146; bh=6ydO6GhkZaP9pOLY53HnWoZDVdbdjSBTwBNNzxVp3Uw=; h=Date:From:To:Subject:References:From:Subject:Reply-To; b=C3HMmETpQBa+I3zTypH2bNcrwIA7DphSNGRBh2dsyx48tENigvyREvImL0h+1lakBo0qt3dE5gygFQf2C9Y3UAelT7yVHmFReLV8P3ssQtYTdT03dIdKJUot/7SbinEAaLL0qjleSY7jCtj5Uk1WMWTSJRi1JOFleuvs3Vuy61n+nrfVIu8/ZRrQg3cBX0jjgRnfFVX/llArg0ijn/iIoqrXFxsw7iFoi78TjZbn0pFOD8cs0/xhdfh/aCMsq/xPpU2NYHFpZX7EYJuuqdog8kM+Yh51c6epwbMn6gn0ako8dJ7Jlx+L34MtD4FJPLRqeZxG4s3d2sNVpxE17Au6zA==

This header shows the following:

  • DKIM version 1 (v=1) is used, the only one currently specified.
  • It uses rsa-sha256 (a=rsa-sha256) as the signing algorithm.
  • It used the relaxed method to canonicalize the message (c=relaxed/relaxed). This means that the signature will pass even if an email system changes some whitespaces or line wrapping.
  • It has been signed by aol.com (d=aol.com),
  • with the key specified in the selector a2048 (s=a2048). More on key selection later.
  • It has been signed at timestamp 1722429146 (t=1722429146)
  • The message had the hash C3HMmETpQ[…] (b=C3HMmETpQ[…])
  • It signed the Date, From, To, Subject, References, From, Subject and Reply-To headers (h=Date:From:To:Subject:References:From:Subject:Reply-To;). Some headers are listed twice because there could be multiple occurrences of these headers in the email. The signature will then contain two instances of these header, if multiple are contained in the email.
  • The signature is calculated by first canonicalizing and hashing the body (the body hash), then calculating the hash of the headers and the body hash (the data hash) and lastly signing the data hash.

To verify the DKIM signature, the signing key needs to be known. It can be extracted from the domain (aol.com) and the selector (a2048). The domain keys are always stored in <selector>._domainkey.<domain>.

$ dig @9.9.9.9 a2048._domainkey.aol.com +noall +answer TXT
a2048._domainkey.aol.com. 294   IN      TXT     "k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAugjjoWOWXaAXZh5iWdX9a27P6q0EZnjQQdFCplsFnxL85ppilMPGKZc1OA/aQK6bUFTuf4MJKr8JF3mp+O/Eo022W98Yc2JKPFY6PYxb81GWwpXNaQnug6bPVrLaQVJmxC7izdK6F0VmM6oLIiTNGMUVCwVndmDauFb3jFrf2Dgvgy/d9pjGKYEHKkSeYebWcxrGfOEkBA" "PcHfr0PUqe/hmp7Id6gO6EvMQ1kzyZHVmuJ3TZmpkQA5xY9CyD9f10lZ9MiFv89wM/u/m7ocAmmsk49aPi4DBwXSO8VSpbI5/oulw2SI41CYFgTdDByTvSr4hRjcyc4B3feYdhZ9egdQIDAQAB;"

And there we have the key. If analyzing historical emails, it might be that the key has been rotated. A lookup in historical DNS databases might be required to get the right key. For recent emails, however, the current key is usually the correct one.

Now, to verify the email signature, dkimpy can be used:

$ python dkimverify.py < cupcakes_or_no_cupcakes.txt
signature ok

However, this is very finnicky, especially if the email has to be exported first, as tools like to change things slightly during export. In a past case, an email we tried to verify did not pass DKIM checks even though we knew it should. Through a lot of trial and error, we figured out that for some reason, during the export, Outlook stripped the “(UTC)” from the Date header. Manually adding it again allowed us to verify that the DKIM signature is correct.

With this, it can be proven that the email has not been tampered with (at least the selected headers and the body) during transit. There is a caveat here, however. As the signature was created by the Mail Submission Agent, it can only be proven that the email was not tampered with after the MSA. The MSA itself (and anything before the MSA received the message) could have altered the email from the way the user intended to send it.

DKIM: PASS

But what if any of these checks fails? Then the Mail Delivery Agent will request the policy on how to handle the failure from DMARC.

Domain-based Message Authentication, Reporting and Conformance (DMARC)

DMARC (specified in RFC 7498) allows domain holders to define how emails sent from their domains should be handled by the MDA when a security check fails. DMARC is stored in a DNS TXT records under _dmarc.<domain>.

$ dig @9.9.9.9 _dmarc.aol.com +noall +answer TXT
_dmarc.aol.com.         3600    IN      TXT     "v=DMARC1; p=reject; pct=100; rua=mailto:d@rua.agari.com; ruf=mailto:d@ruf.agari.com;"

The entry for aol.com states the email server should reject (p=reject) 100% of the emails (pct=100) if SPF or DKIM fails. Rejecting means not delivering the message at all. It should also send aggregated failure reports to d@rua.agari.com (rua=mailto:d@rua.agari.com) and message-specific failure reports to d@ruf.agari.com (ruf=mailto:d@ruf.agari.com).

Of course, checking all of this manually is time consuming. Luckily, tools like MxToolbox make this a lot easier. Another nice tool is the Message Header Analyzer by Microsoft and the Analyze Headers tool by MxToolbox. Just paste the headers of an email to see all headers displayed in a nicer, more readable way. MxToolbox will also verify SPF and DKIM.

Everyone reading this should head over to MxToolbox right now and check if DMARC, SPF and DKIM are set up correctly for their domains!

So… Who Sent the Email, After All?

Has the email been sent by Mythical Marly? How real is the cupcake-threat? Who knows. While we can trace the email to the correct Mail Submission Agent, we can’t be 100% sure who actually sent it. It could have been someone who just happened to have access to Marly’s unlocked iPad. Or it could have been someone who can send emails in the name of Marly. Or someone who knows Marly’s password (everyone knows it’s “cupcakes2”!) and 2FA has still not been enabled for all accounts.

The moral of the story? While email headers can tell us a lot, they cannot prove who sent the email. The best option to combat Business Email Compromise, as with all social engineering attacks, is to be suspicious of anything.

  • Has the payment information changed between invoices?
  • Does a vendor suddenly request a partial invoice?
  • Does a contact out of the blue send a SharePoint link to a document requesting you to sign in?

Then do not open links, do not copy the payment information, do not enter your password, do not reply to the email, but be old school cool, pick up the phone and ask for confirmation.

Should you need support with Business Email Compromise or to investigate an email, don’t hesitate to contact us!