Previously on the War Room, we discussed some basic mail control implementations. Specifically, we looked at simple text records that can be posted to determine what is allowed to send on behalf on the domain. SPF records and DMARC records, when properly configured, can help reduce the chances of someone being able to spoof the domain in a phishing attack. So the next thing we want to look is DomainKeys Identified Mail (DKIM). For the purpose of this blog we will be using Postfix and an Ubuntu instance on Digital Ocean. We won’t go through the detail of installing Postfix and connecting a domain to your droplet. We are going to reuse a droplet from a previous walkthrough which already has the domain and Postfix.
DomainKeys Identified Mail (DKIM) functions as email authentication. It is another layer in email controls to help prevent spoofing. It works similarly to SSH keyed auth, in that it relies on public/private key-pairs. So as an email is sent out, it is signed with a public key. When the email is received by the server the key is checked for the private key pair on the originating server. Similar to its SPF and DMARC counterparts, DKIM is a text record that goes in the DNS. As all good defenses go, this is best utilized in conjunction with SPF and DMARC. No one control is a magic bullet, so layered security is the best security.
Much like SSH keys, DKIM keys need to be generated cryptographically. In order to do this we are going to use opendkim. OpenDKIM is fully open-source and will have everything we will need to generate our keys. From your terminal run the following:
apt-get install opendkim opendkim-tools
Once installed we have several configuration edits to get through in order to setup DKIM.
Postfix and OpenDKIM Configuration
Once OpenDKIM is installed we should a configuration file in /etc/. So using a text editor, open /etc/opendkim.conf. Inside this file you should mostly see commented out lines. We are going to add some configurations to the end of this file. There are quite a few settings we can do, so for a more in depth explanation of all the options check out OpenDKIM’s documentation. Some of these configurations include logging, which is encouraged, but for this example is not necessary. We are going to specifically look at:
- Canonicalization – The method to use when verifying signatures
- InternalHosts – List of internal hosts that should be signed (defaults to 127.0.0.1)
- KeyTable – File path where the private key is stored
- SigningTable – File path to the signature that will be applied based on the From field
- PidFile – File path to the file that should be created to contain the process ID
- SignatureAlgorithm – Algorithm to use when generating the keys
- UserID – User to use when creating
- Socket – Port to be used to receive connections
So at the end of our configuration file we will want to add:
Now, with the configurations set we have to create the files referenced and make sure the postfix is going to use opendkim. First we will need to make sure the correct socket is being used. In the configuration file we specified port 12345 for the socket. We can use any port we desire, and since this is a custom setting we need to update the file at /etc/default/opendkim to reflect this change. We can just change the preexisting line to show this.
Next we need to update Postfix configuration to use DKIM. Located in /etc/postfix/ we will want to edit the main.cf file. Now some settings here will depend on what version of Postfix we are using. If we running version 2.6 or higher, then we will having the milter_protocol be set to 6. On this server that is exactly what we are going to do, just keep in mind to check your version and run the appropriate protocol. So somewhere in our main.cf we will want to add the following lines:
milter_protocol = 6
milter_default_action = reject
smtpd_milters = inet:localhost:12345
non_smtpd_milters = inet:localhost:12345
As stated before, we are using the protocol appropriate for our version of Postfix. The second setting is the default action to take in the event our mail filter becomes unavailable. In the context of security, reject is our best option; however as most admin may tell us, this may not always be convenient or may even break things. Another option would be quarantine, which will place messages in a hold queue which will only be released after an admin allows it. The last two options are specifying where the mail filters we want to use can be found, in this instance it will be the DKIM policy running on port 12345.
Once Postfix is good to go, we will want to create the files and directories we will need for DKIM, assuming your installation is at /etc/opendkim. First we are going to want to make a directory to hold our keys, along with tables referenced in our opendkim.conf file. So if are using our old trusty domain from the SPF/DMARC post of hacme.com, we will make a directory called keys and then another inside of that with the domain (/etc/opendkim/keys/hacme.com/). Now that we have this, let’s navigate into the hacme.com foler and we will generate our keys.
Now that we are in the domain directory for the keys, we will use opendkim to generate the key pair. There are two arguments we will be using for this:
- s – the selector for the record
- d – the domain to sign
There is a third one of interest to specify the number of bits the key length to be. The default setting is 1024 bits, which will be sufficient for verification. The selector will be added to the domain name and will be used to query DNS for the private key. If you are utilizing multiple signatures for a single domain make sure that each selector is meaningful for your internal team in the event incident response or forensics has to look through logs regarding DKIM. For this exercise we will use the selector of default. Finally, the domain will be hacme.com; so our command to generate the key pair should look like:
opendkim-genkey -s default -d hacme.com
This will output two files, default.txt and default.private. Default.txt is the public key that we will post to the DNS record in DigitalOcean. Default.private is the private key that will need to be queried for. We need to make sure that the private key is set to the proper permissions so it can be read when queried for.
chown opendkim:opendkim default.private
Next, if we look at default.txt we will should something similar to the below:
The beginning of the file, default._domainkey, will be the hostname for the record. The value of the text record itself will be what is inside the parentheses. We will need to ignore the double quotes as well. So starting at v copy to the end of the string and paste it into the value box, removing any double quotes. Your final record on DigitalOcean should look like:
With the record created we need to create the KeyTable, SigningTable, and the TrustedHosts files that we referenced in the DKIM configuration file at the beginning.
First the SigningTable file, this file determines who gets what signature. Keeping with a single signature we are going to say any address found in the From field should get the default policy. So quite simply the contents of the SigningTable should contain:
Next the KeyTable will contain the hostname of the policy on the DNS and the path on server to the private key. The path should be appended to to the domain and the selector. The file contents should look something like:
Finally, the TrustedHosts file will be a list of the hosts that are considered trusted and we will be signed by the domain. Ordinarily this will contain all the hosts that are allowed to send on behalf of the domain (the hosts that are in your SPF record), however since we only have the one we should have a very simple file. The contents should look like:
With those files created all that’s left is to restart the services and send our first signed email! In order to restart Postfix and DKIM simply run:
service opendkim restart
service postfix restart
There areba few ways we can verify that our DKIM record is functioning. OpenDKIM has a tool to test with is aptly named opendkim-testkey. Using this we will specify the domain to test and the selector.
opendkim-testkey -d hacme.com -s default -vvv
The verbose output is not necessary, if you do not use them then do not expect to receive output. Without verbose output, if the key is functioning correctly then we should see nothing. However no output for a walkthrough is kind of dull, so for our example we should see:
Ordinarily something like “key not secure” is something to be concerned about. However the reason for this is because we did not implement DNSSEC on the server, which is beyond the scope of this post. If we implement DNSSEC properly and configure OpenDKIM to use it, that notification should go away. But without it, it should still function properly. So next let us test by actually sending an email. For this step we will use King Phisher since it is already running with Postfix from our previous post. For examples I am going to send to two test accounts, one on Gmail and one on Protonmail.
In Gmail we should see something like this:
We could inspect the headers on the email, but in Gmail that is not necessary. The dropdown next to the recipient field on the email should be enough to tell us who sent the email and who signed it. For Protonmail we will have to inspect the headers, but it should not be too hard to find. We should see:
In the headers we can see the the message passed the DKIM check along with the domain signer and the selector. With both of these emails signed by the domain, the receiving servers can confirm that the message did in fact originate from the within that domain.
When it comes to domain spoofing for phishing, a layered approach is the best approach. The top three controls to implement are an SPF, a DMARC, and DKIM record. The SPF will be what dictates what servers can send on behalf of the domain. The DMARC record should tell servers how to handle messages that fail the SPF record or in other words, messages that originate outside of the hosts specified in the SPF record. Finally, a DKIM record should be used to sign messages that originate from the authorized hosts to attest the email came from the authorized sender. All three of these working together will help prevent domain spoofing and help spam filter better identify phishing attempts. On the flipside, a red team can generate these records for malicious domains just as we have in this blog in order to get through some filters. That is why after all is said and done, the last line of defense from a phish will be the user. These records are great to help reduce an attacker’s ability to spoof and impersonate. A combination of technical controls with a well trained userbase is best to help keep an organization ahead of phishing attacks.