Phisher’s Playbook: Creating a 10/10 Phishing Mail

Özgün Kültekin
16 min readJul 16, 2023

--

Welcome to the Phisher’s Playbook

In this thrilling series, we will discover the techniques behind crafting phishing emails, aiming to achieve a perfect 10/10 score on mail score testers 💯.

With my current role as an Offensive Security Engineer, I’ve been actively involved in conducting Red Team Campaigns, continuously improving my skills and gaining valuable knowledge. Now, I’m excited to pass on my firsthand experiences to you as we begin to this adventure together.

Throughout this series, I’ll provide you with every detail and key point I’ve encountered while manipulating the complexities of email security. Our focus will be on using Postfix as the Mail Transfer Agent (MTA) -or basically as the SMTP Server-.

To ensure our success, we’ll put our crafted emails to the test using online mail score testers. These tools will help us evaluate the effectiveness of our techniques and provide valuable feedback to fine-tune our approaches. By the end of this series, you’ll have the knowledge and skills to send a phishing email that not only achieves a perfect 10/10 score but also manages to land in the inbox of even the most vigilant email providers like Gmail 📬.

So fasten your seat belts and get ready to deceive and outsmart even the most cautious recipients. Let’s dive in and master the art of creating a phishing email!

Note: The techniques and knowledge shared in this series are intended for educational purposes only. It is essential to understand the ethical implications of email phishing and to always abide by legal and responsible practices 🥷.

1- Infrastructure details & installing Postfix

In this section, we’ll cover the essential elements of getting your email infrastructure up and running, beginning with the initial installation of Postfix.

For the purposes of this demonstration, we’ll be using Google Cloud Platform (GCP) with a virtual machine instance running Ubuntu 22.04 x86/64.

Moreover, ensure you have a domain name to serve as the sender for your emails. For the purpose of this article, I have chosen the domain ozgun.zip and managed it via Cloudflare.

And yes, we can even do all of these using .zip TLD. How? Here is the untrusted TLD’s shared by widely used open-source anti-spam platform SpamAssassin. They still did not label .zip as untrusted 🤡

With your virtual machine up and running, you can proceed with the installation of Postfix. Open a terminal session on your Ubuntu instance and enter the following command to update the package repository & install Postfix:

sudo apt update && sudo apt install -y postfix

During the installation, you will be prompted to choose the type of mail server configuration. Select “Internet Site” and press Enter. Next, provide the fully qualified domain name (FQDN) for your email server:

Postfix coniguration

Next, let’s update the /etc/postfix/main.cf file by setting the myhostname field to your fully-qualified domain name (FQDN). This field specifies the FQDN of the machine running the Postfix system.

By default, the myhostname parameter is set to the local machine name. However, if your local machine name is not in fully-qualified domain name form or if Postfix is running on a virtual interface, you must specify the FQDN that the mail system should use.

In our case, we can set the myhostname field as mail.ozgun.zip:

myhostname field in main.cf

Secondly, open the /etc/hostname file with a text editor and set the hostname to match your Postfix configuration. You can just specify the hostname without the domain. Therefore, you should set the hostname in the /etc/hostname file as “mail” (without the “.ozgun.zip” domain).

It ensures consistency between the system’s hostname and the hostname used by the mail server. When these values match, it avoids confusion and potential issues when sending and receiving emails. This consistency helps in smooth email delivery, reduces potential issues, and ensures the proper functioning of various services and protocols.

hostname change

As it can be seen from the above screenshot, our hostname field still seems “phishersplaybook”. We need to restart the system after modifying the hostname to ensure that the new hostname is properly initialized and propagated throughout the system.

Adding the “mail.ozgun.zip” and “mail” values to your /etc/hosts file is an optional step that can provide benefits such as bypassing DNS lookups for local connections and aiding in local hostname resolution. Although not strictly required, it is considered a good practice. Doing this can potentially enhance performance and streamline local network communication.

By adding these values in /etc/hosts file -before the localhost-, we can send mails without explicitly stating sender.

After the reboot, we should observe that our hostname has been updated. At this stage, our objective should be reflected in the following file configurations:

config check

/etc/mailname part should already be changed automatically to the same domain provided in the Postfix installation.

First mail -> 1.3/10:

It is time to send our very first email!

I will be using https://www.mail-tester.com to test our email score. But there are plenty of other cool alternatives out there if you want to mix it up a bit.

Just a heads up, the changes we’ve made so far won’t impact the score yet. Our Postfix server isn’t aware that we truly own ozgun.zip because we haven’t set up any DNS records. So, it is going to be a slightly dubious email experience.

We will simply use mailx for that purpose:

sudo apt install -y mailutils

Send an example mail:

echo "our first untrustworthy email" | mailx -s "no subject" -r test@ozgun.zip <code>.mail-tester.com

# You can change the "From" part via "-r" parameter.
# -r "ozgun@ozgun.ozgun" will also work and get a nice 0 score :)

And the perfect result!:

1st mail test result

As it’s been said, our email may never see the light of an inbox.

2- Setting initial DNS records

Now it’s time to take things up a notch and boost our server’s reliability! We’ll do this by adding an A record and MX record.

This move also proves that we truly own the domain.

A Record:

A record on CF

Our A mail server (mail.ozgun.zip) is now directing to our Postfix GCP instance.

MX Record:

MX record on CF

By configuring this MX record, any emails sent from ozgun.zip will be seamlessly handled by our GCP instance, mail.ozgun.zip. That means, now we can send mails with specifying sender as <username>@ozgun.zip.

Second mail -> 5.8/10:

This time, because we have MX record, we’ll make sure to explicitly specify the sender/return address for our email.

echo "our second untrustworthy email" | mailx -s "no subject 2" -r "test@ozgun.zip" <code>@srv1.mail-tester.com

And we are inching closer to perfection!:

2nd mail test result

We have increased our score by making the following fields ticked:

mail score improvement

3- Creating an SPF record:

Firstly, why do we need that?

An SPF record on your Postfix server is like a reliability superhero for your emails. It acts as a defender, ensuring that only authorized servers can send messages on your behalf. By including an SPF record, you reduce the risk of email impersonation and make your messages more trustworthy. It’s like adding a secret code that tells the world, “Hey, this email is legit!”

And more importantly, many mailboxes and email security gateways of companies do perform SPF checks as part of their email filtering and security measures. When an email is received, the recipient’s mail server or security gateway may query the SPF record of the sender’s domain to verify if the originating server is authorized to send emails on behalf of that domain. If the SPF check fails or no SPF record is found, it could result in the email being flagged as suspicious or potentially rejected by the receiving system.

I suppose that drives the message home, doesn’t it?

Creating an SPF record is a breeze — simply add the following DNS record:

TXT    @    v=spf1 mx ~all
SPF TXT record

v=spf1 denotes the version of SPF.

mx allows the domain’s MX records to be designated as authorized senders for the domain. We might want to use other server’s MX as include:_spf.example.com

The “~all” qualifier specifies the policy for all other hosts that are not explicitly defined. In this case, it suggests a soft fail, which means the server should still accept the email but may flag it as potentially suspicious. Alternatives are “-all” (hard fail) and “+all” (allow all).

(Optional) SPF policy daemon configuration:

In that step, you can configure your Postfix server to check SPF on incoming emails and reject emails which are unauthorized.

However, in this particular scenario where we are the attackers, our objective is solely to send emails. We don’t expect to receive any emails in return. Therefore, if there are no plans to retrieve emails from the target you’ve phished, feel free to skip this part.

Install the Postfix policy daemon for SPF checking:

sudo apt install postfix-policyd-spf-python

Now it’s time to instruct Postfix to start the SPF policy daemon alongside the Postfix service itself. Add the following lines to /etc/postfix/master.cf:

policyd-spf  unix  -       n       n       -       0       spawn
user=policyd-spf argv=/usr/bin/policyd-spf

We will provide Postfix with instructions to perform SPF checks on incoming emails and reject any emails that are unauthorized. Add the following lines to /etc/postfix/main.cf:

policyd-spf_time_limit = 3600
smtpd_recipient_restrictions =
permit_mynetworks,
permit_sasl_authenticated,
reject_unauth_destination,
check_policy_service unix:private/policyd-spf

policyd-spf_time_limit defines the time limit, in seconds, for the SPF policy daemon to process SPF checks for an email. If the SPF check takes longer than the specified time, it may result in a timeout.

smtpd_recipient_restrictions defines the restrictions applied to incoming emails when the Postfix SMTP server receives them. No need to dive into details, they are the most common restrictions.

After all of these configurations, we need to restart the Postfix service:

sudo systemctl restart postfix

Third mail -> 8.6/10:

Just like before, we sent an email from test@ozgun.zip and guess what? We received an impressive score of 8.6! Way to go!

3rd mail test result

4- Creating a DKIM record:

Let’s explain DKIM record with an example first:

DKIM (DomainKeys Identified Mail) is like a secret code that makes your emails more trustworthy and ensures they arrive at their destination intact. It’s a digital signature that you add to your outgoing emails to prove they’re legit.

Here’s how it works: Let’s say you send an email from test@ozgun.zip Before it’s sent, your email server signs it with a special key, creating a unique digital signature. This signature is then attached to the email.

When the recipient’s email server gets your email, it checks the DKIM record in the DNS for ozgun.zip. The DKIM record contains the public key needed to verify the signature.

Using the public key, the recipient’s server unlocks the digital signature and checks if it matches the content of the email. If it does, it knows the email is genuine, unaltered, and from a trusted source like you. Awesome, right?

DKIM helps protect against sneaky scammers trying to trick people with fake emails. Oh my goodness, who are these evil people! 👀

Unlike SPF, the level of DKIM record checking may not always be as strict. While DKIM provides an additional layer of email authentication, some email gateway solutions may not consistently perform DKIM checks. Nonetheless, configuring DKIM is recommended for optimal email security and delivery.

DKIM installation & configuration:

To get started, let’s install the necessary tools for OpenDKIM:

sudo apt install opendkim opendkim-tools

By adding the postfix user to the opendkim group, you ensure that Postfix has the necessary privileges to access the OpenDKIM service and perform DKIM signing or verification of outgoing and incoming emails:

sudo gpasswd -a postfix opendkim

We need the following changes on /etc/opendkim.conf:

# Add the following line under the -> Syslog   yes
LogWhy yes

Enabling LogWhy yes helps with troubleshooting and understanding the DKIM verification process

# Uncomment the following lines and change Canonicalization as the following:
Canonicalization relaxed/simple
Mode sv
SubDomains no

For canonicalization, “/’ separates the header/body part of the email. The relaxed canonicalization algorithm slightly modifies the email content and headers to ensure consistent DKIM signature generation, while the simple canonicalization algorithm preserves the original structure without modifications.

The “sv” mode stands for “signing and verifying.” This mode enables OpenDKIM to both sign outgoing emails with DKIM signatures and verify DKIM signatures of incoming emails.

SubDomains: The “no” setting indicates that DKIM signing and verification will not be automatically applied to subdomains of the signing domain.

# Add the following lines:
AutoRestart yes
AutoRestartRate 10/1M
Background yes
DNSTimeout 5
SignatureAlgorithm rsa-sha256
  1. AutoRestart: Enables automatic restart of OpenDKIM in case of issues or crashes.
  2. AutoRestartRate: Specifies the rate at which OpenDKIM restarts.
  3. Background: Allows OpenDKIM to run as a background process.
  4. DNSTimeout: Sets the maximum time for DNS lookups in OpenDKIM.
  5. SignatureAlgorithm: Defines the cryptographic algorithm used for DKIM signature generation.
# Add the following lines at the end of the file, under the "UserID":  
KeyTable refile:/etc/opendkim/key.table
SigningTable refile:/etc/opendkim/signing.table
ExternalIgnoreList /etc/opendkim/trusted.hosts
InternalHosts /etc/opendkim/trusted.hosts

In OpenDKIM configuration, the KeyTable specifies the location of the file containing DKIM keys, the SigningTable determines which domains to sign emails for, and the ExternalIgnoreList and InternalHosts files define trusted hosts for external and internal email sources, respectively.

Here’s how the opendkim.conf file should appear with our changes implemented (without comments):

Creating necessary folders/files and configuring them:

Create opendkim folders and set the necessary permissions:

# Create opendkim folders:
sudo mkdir /etc/opendkim
sudo mkdir /etc/opendkim/keys

# Change owners & permissions:
sudo chown -R opendkim:opendkim /etc/opendkim
sudo chmod go-rw /etc/opendkim/keys

Create the signing table and edit:

# Create the signing.table:
sudo touch /etc/opendkim/signing.table

# Add the following lines to signing.table:
*@ozgun.zip default._domainkey.ozgun.zip
*@*.ozgun.zip default._domainkey.ozgun.zip

First line signifies that all email addresses ending with @ozgun.zip should use the default DKIM selector default._domainkey.ozgun.zip for DKIM signing.

The second line indicates that any subdomain of ozgun.zip (e.g., subdomain.ozgun.zip) should also employ the same default DKIM selector for signing.

Create the key table and edit:

# Create the key.table:
sudo touch /etc/opendkim/key.table

# Add the following line to key.table:
default._domainkey.ozgun.zip ozgun.zip:default:/etc/opendkim/keys/ozgun.zip/default.private

This line associates the DKIM selector default._domainkey.ozgun.zip with the domain ozgun.zip and specifies the location of the corresponding private key file /etc/opendkim/keys/ozgun.zip/default.private. This key is used for signing outgoing emails from the ozgun.zip domain using the DKIM selector “default”.

Create the trusted.hosts and edit:

# Create the trusted.hosts:
sudo touch /etc/opendkim/trusted.hosts

# Add the following lines to trusted.hosts:
127.0.0.1
localhost

*.ozgun.zip

The line *.ozgun.zip signifies that any subdomain under the ozgun.zip domain (e.g., subdomain.ozgun.zip) is also considered a trusted source exempt from DKIM verification. This allows emails from any subdomain of ozgun.zip to bypass DKIM checks.

Generating private-public key pair:

Create key’s folder:

sudo mkdir /etc/opendkim/keys/ozgun.zip

Generate keys using opendkim-genkey (default selector):

sudo opendkim-genkey -b 2048 -d ozgun.zip -D /etc/opendkim/keys/ozgun.zip -s default -v

This line generates a DKIM key pair for the domain ozgun.zip with a key size of 2048 bits, storing the keys in the specified directory (/etc/opendkim/keys/ozgun.zip) using the selector default for DKIM signing and verification.

Set ownerships & permissions of generated keys:

sudo chown opendkim:opendkim /etc/opendkim/keys/ozgun.zip/default.private
sudo chmod 600 /etc/opendkim/keys/ozgun.zip/default.private

Publishing the DKIM public key as a DNS record:

Read the public key via:

sudo cat /etc/opendkim/keys/ozgun.zip/default.txt

Add the readed line, without spaces and quotes, as a TXT record with the value default._domainkey.

DKIM TXT record

Testing the DKIM record:

Now, it’s time to test if our DKIM record is correctly configured:

sudo opendkim-testkey -d ozgun.zip -s default -vvv
DKIM test succeeded

key OK suggests that the key has passed the test and is considered valid, key not secure part is generally related to DNSSec configurations, which is not our main concern right now.

Connecting the Postfix to OpenDKIM:

Create opendkim socket files and set ownerships:

sudo mkdir /var/spool/postfix/opendkim  
sudo chown opendkim:postfix /var/spool/postfix/opendkim

Edit the /etc/opendkim.conf:

# Replace the Socket with the following line:
Socket local:/var/spool/postfix/opendkim/opendkim.sock

Edit the /etc/default/opendkim:

# Replace the Socket with the following line:
SOCKET="local:/var/spool/postfix/opendkim/opendkim.sock"

Lastly, edit the /etc/postfix/main.cf:

# Add the following lines: 
milter_default_action = accept
milter_protocol = 6
smtpd_milters = local:opendkim/opendkim.sock
non_smtpd_milters = $smtpd_milters

These lines configure Postfix to utilize OpenDKIM as a milter for both incoming SMTP connections and non-SMTP mail delivery, enabling the integration of DKIM signing and verification into the mail server’s processes.

Now it’s time to restart everything!

sudo systemctl restart opendkim postfix

Fourth mail -> 9.8/10:

As we did previously, we sent an email from test@ozgun.zip and to our delight, we achieved a remarkable score of 9.8!

4th mail test result

Is… that enough? Hell no.

5- The Secret Ingredient: Reverse DNS Records

Now, for the final step, let’s introduce our secret ingredient: PTR (Pointer) records, also known as Reverse DNS. But before we dive in, let’s understand what PTR and Reverse DNS are all about and why they are crucial.

PTR (Pointer) records are a type of DNS record that maps an IP address to a domain name. In the context of Reverse DNS, it associates an IP address with its corresponding hostname. This reverse mapping helps establish trust and authenticity for email servers.

Reverse DNS is important because it verifies the identity of the sender’s IP address. When an email is received, the recipient’s server performs a reverse DNS lookup to match the IP address to a domain name. If the lookup fails or doesn’t match, it can raise suspicions and affect email deliverability.

Let’s take a look at how our mail server’s reverse DNS appears now, note that my current GCP instance’s ip is 35.239.232.204:

nslookup on our GCP instance

DNS A records are associated with domain names, while DNS PTR records are linked to IP addresses in reverse format with the addition of “.in-addr.arpa”. For instance, for our IP address 35.239.232.204, the corresponding PTR record would be stored under 204.232.239.35.in-addr.arpa.

Because my server is running on Google Cloud Provider, my mail server’s ip address resolves to the hostname 204.232.239.35.bc.googleusercontent.com.

When we send an email from test@ozgun.zip, the recipient server, thanks to MX records, identifies that our email is originating from mail.ozgun.zip. To validate this information, the recipient server performs a DNS lookup for mail.ozgun.zip and retrieves its corresponding IP address, which is 35.239.232.204:

DNS lookup for mail.ozgun.zip

However, when the recipient server attempts a reverse DNS lookup for 35.239.232.204, it receives an inconsistent response of 204.232.239.35.bc.googleusercontent.com :

Reverse DNS lookup for GCP ip

This inconsistency between the forward and reverse DNS lookups needs to be addressed. Ensuring consistent forward and reverse DNS lookups is crucial for establishing trust and maintaining proper email deliverability. Let’s take the necessary steps to resolve this inconsistency and align the reverse DNS lookup with our desired hostname.

It is worth noting that PTR records can be edited on various cloud providers, including AWS. However, for the purpose of this demonstration, I will showcase the process specifically on GCP.

Google Cloud has a great documentation to create a PR record for a VM instance. First and foremost, we must confirm our domain ownership: mail.ozgun.zip.

Domains can be verified from https://search.google.com/u/0/search-console/welcome.

Following the documentation, Public DNS PTR Record can be set from the Network settings of the target instance:

PTR Record change on GCP

As a result of these modifications, it is important to be aware that your instance’s IP address may have been changed. So don’t forget to update your A record with the new ip.

Reverse DNS lookup on my new ip: 35.202.215.47

Reverse DNS lookup on new ip

This change can also be noticed by examining the Received header of the sent emails:

Received header

Fifth mail -> 10/10:

Are you ready for the final email? Here we go:

echo "our trustworthy email" | mailx -s "Final mail" -r "final@ozgun.zip" <code>@srv1.mail-tester.com
5th mail test result

And voila! We can now send innocent 😇 emails with a flawless 10/10 score on the mail tester!

6- Gmail Test

Now, let’s verify if we can successfully send emails to the target’s Gmail primary inbox.

echo "our trustworthy email" | mailx -s "Gmail Delivery Test" -r "support@ozgun.zip" ozgunkultekin@gmail.com

And guess what:

Successful gmail delivery test

Mail details:

Mail details.

Yep! Now we can send signed/secure emails directly to the target’s primary inbox!

7- In case of Outlook…

But here’s the catch: when you try sending emails to Outlook, you’ll hit a roadblock! Outlook’s spam policy is like a tough bouncer, making it harder to get into the inbox. But fear not!

In my upcoming write-up, I’ll spill the beans on how to outsmart Outlook’s spam filters and successfully land your emails in those hardened Outlook inboxes. Get ready for some sneaky tricks and expert tips!

8- Final Notes & Resources:

We’ve reached the end of the first part of the Phisher’s Playbook series. Thanks for reading! I’ll keep going with the series and share more about my adventures in offensive security. I’ve already hinted at what to expect in my upcoming writing 👀 📪.

Here are the files we created and edited during our journey. For those who may encounter any issues with them, I am sharing each file with their latest version that was used in this writing:

You can follow/reach me anytime from:

Warning ⚠️ : Before concluding, it is crucial to emphasize the legal and ethical considerations surrounding the topic of creating phishing emails. While this article aimed to shed light on achieving a high mail tester score, it is imperative to utilize this knowledge responsibly. The techniques and information provided here should only be used for educational or security awareness purposes. Engaging in any illegal activities, such as sending deceptive phishing emails, is strictly prohibited and can result in severe legal consequences. It is essential to respect privacy, adhere to laws, and prioritize the well-being of others in all online activities. Let us all strive for a safer and more secure digital environment.

--

--

Özgün Kültekin
Özgün Kültekin

No responses yet