Skip navigation

Duo Security is now a part of Cisco

About Cisco

Detecting Phishing with SPF Macros

Jordan Wright July 10th, 2019 (Last Updated: July 10th, 2019)

01. Introduction

SMTP was designed without authentication, allowing anyone to send email as anyone else. While this can be useful in some scenarios, this also makes phishing easier since attackers have the ability to spoof legitimate domain names when sending emails to potential victims.

There have been a number of protocols such as SPF, DKIM, and DMARC built on top of SMTP to fix this problem by providing "email authentication", giving the recipient confidence that the email they received from, say, example.com came from someone authorized to send email as example.com.

Setting up email authentication correctly can prevent people from spoofing your domain name. However, it may be the case that for operational purposes it's still useful to monitor who is attempting to send spoofed emails from your domain.

In this post, I'll show how we can use a somewhat hidden but powerful feature of SPF to monitor who is sending email on behalf of our domain in real-time.

02. Introduction to SPF

Sender Policy Framework, or SPF, helps ensure that the host which sent an email with a particular envelope address was authorized to send email as that domain. SPF leverages DNS TXT records to publish which hosts are allowed to send email as a particular domain.

This process looks something like this:

SPF Sequence Diagram

The SPF records published in DNS have a format defined in RFC 7208. Normally, the entries you find will be pretty straightforward - just a list of IP addresses and hostnames allowed to send emails on behalf of a domain:

v=spf1 ip4:1.1.1.1/32 ip4:2.2.2.2/32 -all"

But what if we could do better? What if we could leverage the fact that SPF requires a DNS lookup in order to glean information about who is sending email on our behalf?

Buried in section 7.2 of the RFC, there is a powerful but less-known concept called macros which does just that.

03. Using SPF Macros

SPF macros allow you to create SPF records containing variables that are populated with information from the email transaction. This is useful if you want to create more dynamic SPF policies that can pass/fail depending on information such as the combination of the sender email address and which IP address it's coming from.

Here are the available macros you can use when building an SPF record:

s = <sender>
l = local-part of <sender>
o = domain of <sender>
d = <domain>
i = <ip>
p = the validated domain name of <ip> (do not use)
v = the string "in-addr" if <ip> is ipv4, or "ip6" if <ip> is ipv6
h = HELO/EHLO domain

Macros are used in DNS records by wrapping them with %{} delimeters. For example, %{s} expands to the sender portion (the MAIL FROM sender) of the sender, and %{i} expands to the IP address of the client sending the email.

Knowing this, we can set up an SPF record that causes the mail server to create a DNS lookup giving us information about emails being sent using our domain. The flow looks something like this:

SPF Macro Sequence Diagram

04. Giving it a Shot

To test this out, I stood up a VPS (142.93.197.227) and launched the DNS server dnsmasq with the SPF record v=spf1 exists:%{l}.sender.%{i}.ip.spftest.jordan-wright.com ~all:

dnsmasq \
--no-daemon \
--log-queries \
--txt-record="spftest.jordan-wright.com, v=spf1 exists:%{l}.sender.%{i}.ip.spftest.jordan-wright.com ~all" \
--address="/spftest.jordan-wright.com/142.93.197.227"

I had previously setup an NS record for spftest.jordan-wright.com that pointed to my VPS. Next, I sent an email to myself from testuser@spftest.jordan-wright.com:

root@spftesting:~# echo "This is a test of SPF!" | sendmail -v -f "testuser@spftest.jordan-wright.com" redacted@gmail.com
redacted@gmail.com... Connecting to [127.0.0.1] via relay...
220 spftesting.localdomain ESMTP Sendmail 8.15.2/8.15.2/Debian-8; Thu, 23 May 2019 01:25:06 GMT; (No UCE/UBE) logging access from: localhost(OK)-localhost [127.0.0.1]
>>> EHLO spftesting.localdomain
250-spftesting.localdomain Hello localhost [127.0.0.1], pleased to meet you
<snip>
>>> MAIL From:<testuser@spftest.jordan-wright.com> SIZE=23 AUTH=testuser@spftest.jordan-wright.com
250 2.1.0 <testuser@spftest.jordan-wright.com>... Sender ok
>>> RCPT To:<redacted@gmail.com>
>>> DATA
250 2.1.5 <redacted@gmail.com>... Recipient ok
354 Enter mail, end with "." on a line by itself
>>> .
050 250 2.0.0 OK  1558574706 58si13379547qtm.361 - gsmtp
050 <redacted@gmail.com>... Sent (OK  1558574706 58si13379547qtm.361 - gsmtp)
250 2.0.0 x4N1P6Kd028599 Message accepted for delivery

Then, back in my DNS logs, I see the connection as expected! First, we see Google request the SPF record:

dnsmasq: query[TXT] spftest.jordan-wright.com from 74.125.44.91
dnsmasq: config spftest.jordan-wright.com is <TXT>

Then, it evaluates the macros and requests the expanded DNS record:

dnsmasq: query[A] testuser.sender.142.93.197.227.ip.spftest.jordan-wright.com from 74.125.44.71
dnsmasq: config testuser.sender.142.93.197.227.ip.spftest.jordan-wright.com is 142.93.197.227

In this case, we can see that the email was sent as the testuser address from 142.93.197.227 and that 74.125.44.71 (Google) processed the email.

05. Taking Things Further with DMARC

SPF macros aren't the only way to receive information about potential spoofs of your domain. For example, DMARC has the ability to generate forensic reports, which include information such as any URLs in the emails, any attachments, and more.

However, there are a couple of reasons why you may want to leverage SPF macros either in addition to or instead of DMARC forensic reports:

  • There may be a delay between when the email was received and when the report is sent, potentially causing a delayed response to an ongoing phishing campaign. Alternatively, SPF macros are evaluated as soon as the email is received and is being processed, providing real-time information.
  • Not every mail receiver supports sending DMARC forensic reports, largely due to privacy concerns

Fortunately, there is another type of DMARC reporting called aggregate reporting which is far more widely supported that may still be useful.

Aggregate reporting is basically a way to tell mail providers to send you a daily report of summarized pass/fail statistics for emails from your domain received by them that day. These reports are GZIP'd XML files containing:

  • ISP information (the information from the mail provider)
  • Information about your DMARC record
  • Summary of authentication results

You can find more information on the structure of an aggregate report here.

The interesting parts of aggregate reports for our use case is the org_name provided in the ISP information section, as well as the IP addresses seen as sending email on behalf of our domain.

From our DNS logging earlier, we already know which IP addresses were sending unauthorized email on behalf of our domain, now we can map that to aggregate reports in order to figure out which IP addresses were used to send emails to which organizations. We can also use the IP address making the DNS request as a weak correlation to map specific DNS requests to the organization receiving the emails.

Here's the final information we have:

Final Information

06. Wrapping Up

The real power of SPF, DKIM, and DMARC is preventing phishing that uses your domain name. Monitoring phishing attempts as they occur just provides extra operational awareness.

The benefit of leveraging SPF macros is that you get another piece of data (the envelope sending address) and, more importantly, we get the information in real-time. This can allow security teams to be more proactive in identifying and responding to ongoing phishing campaigns targeting their customers and brand.