SMTP
SMTP is the protocol that sends mail. It speaks plaintext by default on TCP 25, optionally upgraded to TLS via STARTTLS or running TLS-only on 465. Despite being one of the oldest internet protocols, SMTP enumeration remains useful - it leaks valid usernames, sometimes accepts mail from anyone (open relay), and the banner often reveals more than it should.
# 1. Banner grab and command listingnmap -sV -sC -p25 <target>
# 2. Manual interactiontelnet <target> 25> EHLO test> VRFY root
# 3. Username enumeration via VRFYsmtp-user-enum -M VRFY -U users.txt -t <target>
# 4. Open relay testnmap -p25 --script smtp-open-relay -v <target>
# 5. Manual mail send (for spoofing tests)swaks --to [email protected] --from [email protected] --server <target>Success indicator: user-existence confirmation via VRFY/EXPN/RCPT (different SMTP codes for valid vs. invalid users), or successful open-relay test (server accepts mail not destined for it).
Protocol overview
Section titled “Protocol overview”SMTP is text-based, line-oriented, command-response. Conversation looks like:
S: 220 mail.target.com ESMTP PostfixC: EHLO myhostS: 250-mail.target.com Hello myhostS: 250-PIPELININGS: 250-SIZE 10485760S: 250-VRFYS: 250-STARTTLSS: 250 ENHANCEDSTATUSCODESC: MAIL FROM:<[email protected]>S: 250 2.1.0 OkC: RCPT TO:<[email protected]>S: 250 2.1.5 OkC: DATAS: 354 End data with <CR><LF>.<CR><LF>C: From: [email protected]C: To: [email protected]C: Subject: helloC:C: body textC: .S: 250 2.0.0 Ok: queued as ABC123C: QUITS: 221 2.0.0 ByeThe three SMTP ports
Section titled “The three SMTP ports”| Port | Use | Encryption |
|---|---|---|
| 25 | Server-to-server mail transfer (MTA → MTA) | Often plaintext, optionally STARTTLS |
| 587 | Mail submission from authenticated clients (MUA → MSA) | STARTTLS expected, auth required |
| 465 | Mail submission (older, “SMTPS”) | Wrapped in TLS from the start |
Most of the time port 25 is what you encounter on internet-facing mail servers. Port 587 is for authenticated submission by clients (and is the right port for AUTH-related abuse), and 465 has been deprecated then un-deprecated by various standards bodies.
The command alphabet
Section titled “The command alphabet”| Command | Purpose |
|---|---|
HELO <name> | SMTP greeting. Plain SMTP. |
EHLO <name> | Extended SMTP greeting. Server replies with capabilities list. |
MAIL FROM:<addr> | Sender address (envelope sender, not the visible “From:” header) |
RCPT TO:<addr> | Recipient (envelope recipient) |
DATA | Begin message body. End with <CR><LF>.<CR><LF> (a line containing only a period). |
RSET | Reset transaction without disconnecting |
VRFY <user> | Verify whether a user exists on this server |
EXPN <list> | Expand a mailing list (return members) |
NOOP | No-op, keeps the connection alive |
QUIT | End session |
AUTH PLAIN/LOGIN/CRAM-MD5 | Authenticate (after STARTTLS in practice) |
STARTTLS | Upgrade plaintext connection to TLS |
The mail-handling pipeline
Section titled “The mail-handling pipeline”Client (MUA) → Submission Agent (MSA, often :587) → Relay (MTA, :25) → ... → MTA → Delivery Agent (MDA) → User mailbox (POP3/IMAP for the user to fetch)The Mail User Agent (MUA) is the client app - Thunderbird, Outlook, etc. The Mail Transfer Agent (MTA) is the server-side daemon - Postfix, Exim, Sendmail. The Mail Submission Agent (MSA) is often the same daemon listening on a different port with different rules. The Mail Delivery Agent (MDA) takes the final delivered mail and writes it to the user’s mailbox.
Open-relay misconfigurations happen when the MTA on port 25 accepts mail not destined for one of its own hosted domains.
Default configuration - Postfix
Section titled “Default configuration - Postfix”Postfix is the most common Linux MTA. Main config at /etc/postfix/main.cf. Filtered for non-defaults:
smtpd_banner = ESMTP Serverbiff = noappend_dot_mydomain = nocompatibility_level = 2smtp_tls_session_cache_database = btree:${data_directory}/smtp_scachemyhostname = mail1.inlanefreight.htbalias_maps = hash:/etc/aliasesalias_database = hash:/etc/aliasesmydestination = $myhostname, localhostmasquerade_domains = $myhostnamemynetworks = 127.0.0.0/8 10.129.0.0/16mailbox_size_limit = 0recipient_delimiter = +smtp_bind_address = 0.0.0.0inet_protocols = ipv4smtpd_helo_restrictions = reject_invalid_hostnamehome_mailbox = /home/postfixSettings to recognize:
mynetworks- which networks are trusted (allowed to relay through this server without auth)mydestination- which domains this server is the final destination forsmtpd_helo_restrictions- how strictly HELO/EHLO is policedsmtpd_recipient_restrictions(not in defaults shown) - usually the primary anti-relay control
Dangerous settings
Section titled “Dangerous settings”| Setting | What it enables |
|---|---|
mynetworks = 0.0.0.0/0 | Allow relay from anywhere → open relay → mass spoofing/spam |
mynetworks = 0.0.0.0/0, ::/0 | Same but explicit IPv4+IPv6 |
Permissive smtpd_recipient_restrictions | If anti-relay rules don’t reject_unauth_destination, the server relays |
smtpd_helo_restrictions = (empty) | No HELO validation |
disable_vrfy_command = no | VRFY command works → enables user enumeration |
smtpd_banner = ESMTP Server <version-string> | Discloses version, helps CVE matching |
smtpd_authorized_xclient_hosts = ... | Allows XCLIENT command from listed hosts - if a host is wrongly included, attacker can spoof identity |
The big two: open relay (mynetworks = 0.0.0.0/0 or equivalent) and VRFY enabled (disable_vrfy_command = no, which is also the default in older Postfix).
Footprinting commands
Section titled “Footprinting commands”Banner + script scan
Section titled “Banner + script scan”sudo nmap 10.129.14.128 -sC -sV -p25PORT STATE SERVICE VERSION25/tcp open smtp Postfix smtpd|_smtp-commands: mail1.inlanefreight.htb, PIPELINING, SIZE 10240000, VRFY, ETRN, ENHANCEDSTATUSCODES, 8BITMIME, DSN, SMTPUTF8, CHUNKING,Two key data points in that output:
mail1.inlanefreight.htb- the server’smyhostnamesetting, an internal-facing hostnameVRFYis in the capability list - user enumeration is possible
The default smtp-commands NSE script does the EHLO/capabilities probe automatically. Other scripts to consider:
nmap -p25 --script smtp-commands,smtp-enum-users,smtp-open-relay,smtp-vuln-* <target>Manual interaction with telnet/nc
Section titled “Manual interaction with telnet/nc”telnet 10.129.14.128 25Trying 10.129.14.128...Connected to 10.129.14.128.220 ESMTP Server
HELO test250 mail1.inlanefreight.htb
EHLO test250-mail1.inlanefreight.htb250-PIPELINING250-SIZE 10240000250-ETRN250-ENHANCEDSTATUSCODES250-8BITMIME250-DSN250-SMTPUTF8250 CHUNKINGThe EHLO response advertises capabilities. If VRFY appears, user enumeration works. If AUTH appears in the list (sometimes after STARTTLS), authenticated submission is supported.
User enumeration via VRFY
Section titled “User enumeration via VRFY”VRFY root252 2.0.0 root
VRFY cry0l1t3252 2.0.0 cry0l1t3
VRFY testuser252 2.0.0 testuser
VRFY xxxxxxxxxxxxxxxxxxxx252 2.0.0 xxxxxxxxxxxxxxxxxxxxNotice the problem here: 252 for everything, including obviously fake usernames. Some servers return 252 (“cannot verify but accept anyway”) for all VRFY queries - making the command useless for enumeration.
When VRFY actually works as designed, you get different codes for valid vs. invalid users:
VRFY postmaster250 2.1.5 [email protected]
VRFY nonexistent_user550 5.1.1 <nonexistent_user>: Recipient address rejected: User unknown in local recipient tableDifferent codes = enumeration primitive. Test with a known-valid user (postmaster and root usually exist) and a known-invalid one before treating any output as authoritative.
User enumeration via RCPT TO
Section titled “User enumeration via RCPT TO”When VRFY is disabled (or returns 252 for everything), try MAIL FROM/RCPT TO:
MAIL FROM:<[email protected]>250 2.1.0 Ok
RCPT TO:<[email protected]>250 2.1.5 Ok
RCPT TO:<[email protected]>550 5.1.1 <[email protected]>: Recipient address rejected: User unknown in local recipient tableSame primitive, different command. Often works when VRFY is locked down because RCPT TO can’t easily be disabled (mail wouldn’t work).
User enumeration via EXPN
Section titled “User enumeration via EXPN”EXPN admins250 2.1.5 [email protected]250 2.1.5 [email protected]EXPN expands a mailing list to its members. Often disabled, but when enabled it’s better than VRFY because you get the membership of group aliases in one shot.
Automated user enumeration
Section titled “Automated user enumeration”The smtp-user-enum tool exists but as the HTB source notes, results are sometimes wrong. The Metasploit module is more reliable:
msfconsole> use auxiliary/scanner/smtp/smtp_enum> set RHOSTS 10.129.14.128> set USER_FILE /usr/share/seclists/Usernames/Names/names.txt> set THREADS 5> runFor a quick manual sweep:
while read user; do echo -e "VRFY $user\nQUIT" | nc -q 1 10.129.14.128 25 | grep -E '^25[02]'done < users.txtOpen-relay test
Section titled “Open-relay test”The dedicated NSE script runs 16 different relay tests:
sudo nmap 10.129.14.128 -p25 --script smtp-open-relay -vPORT STATE SERVICE25/tcp open smtp| smtp-open-relay: Server is an open relay (16/16 tests)| MAIL FROM:<> -> RCPT TO:<[email protected]>| MAIL FROM:<[email protected]> -> RCPT TO:<[email protected]>| MAIL FROM:<antispam@ESMTP> -> RCPT TO:<[email protected]>| ... (13 more variants) ...Each pattern tests a different way of fooling relay-check logic. Some servers reject simple cases but accept <@server:relay@elsewhere>-style source-routed paths. 16/16 = wide open; 1/16 means a single bypass technique works.
The [email protected] recipient is a mailbox actually monitored by the nmap project - if a relay test succeeds, the mail is delivered there. Don’t be surprised when scanme.org gets your test mail; that’s intentional.
Manual mail send
Section titled “Manual mail send”When you want to spoof a sender or test a specific path:
telnet 10.129.14.128 25220 ESMTP Server
EHLO inlanefreight.htb250-mail1.inlanefreight.htb...250 CHUNKING
MAIL FROM: <[email protected]>250 2.1.0 Ok
RCPT TO: <[email protected]> NOTIFY=success,failure250 2.1.5 Ok
DATA354 End data with <CR><LF>.<CR><LF>From: <[email protected]>To: <[email protected]>Subject: Quick questionDate: Tue, 28 Sept 2021 16:32:51 +0200
Hey, can you check on the production DB? My creds aren't working anymore.
.250 2.0.0 Ok: queued as 6E1CF1681AB
QUIT221 2.0.0 ByeThings to note:
MAIL FROMandFrom:header are different things.MAIL FROMis the envelope sender (controls bounce routing).From:is the displayed sender. Forging the latter is trivial.NOTIFY=success,failurerequests DSN - a delivery status notification. Useful for confirming the message landed.- The blank line between headers and body is required by RFC.
- The terminating
.on its own line ends the body.
For repeatable testing, swaks automates all of this:
--server 10.129.14.128:25 \ --header "Subject: Test" \ --body "Test body"Working through a web proxy
Section titled “Working through a web proxy”If you’re tunneling through a CONNECT proxy (corporate proxy with allow-listed destinations):
CONNECT 10.129.14.128:25 HTTP/1.0
HTTP/1.0 200 Connection established
220 ESMTP ServerEHLO test...Works when the proxy permits CONNECT to arbitrary ports (often it does, because HTTPS uses CONNECT to 443).
TLS-wrapped SMTP
Section titled “TLS-wrapped SMTP”For port 465 (SMTPS):
openssl s_client -connect 10.129.14.128:465For port 25 or 587 with STARTTLS:
openssl s_client -connect 10.129.14.128:25 -starttls smtpAfter the TLS handshake, the SMTP conversation continues encrypted but with the same commands.
Header analysis
Section titled “Header analysis”The mail header is intel-rich. When you receive (or capture) a mail from the target, the headers reveal the server chain it traversed:
Received: from mail1.inlanefreight.htb (mail1.internal.inlanefreight.htb [10.129.18.200]) by mail.relay.inlanefreight.com with ESMTPS id ABC123 for <[email protected]>; Tue, 28 Sep 2021 16:33:12 +0200Received: from web01.inlanefreight.htb (unknown [10.129.42.50]) by mail1.inlanefreight.htb (Postfix) with ESMTP id XYZ789 for <[email protected]>; Tue, 28 Sep 2021 16:32:51 +0200What this reveals:
- Internal hostnames (
mail1.internal.inlanefreight.htb,web01.inlanefreight.htb) - Internal IP addresses (
10.129.18.200,10.129.42.50) - The path the mail took through internal infrastructure
- Software versions (
Postfixhere, sometimes with version)
Received: headers are added in reverse order - the bottom one is the original sender, the top one is the last hop before delivery.
RFC 5322 - Internet Message Format
Section titled “RFC 5322 - Internet Message Format”The mail header format is defined by RFC 5322. Worth a skim if you’re going to spoof: many mail systems check things like Date: format compliance and reject obviously malformed messages.
Common chained workflows
Section titled “Common chained workflows”User list → spray → SMTP-auth on 587:
- VRFY/RCPT enumeration → list of valid email addresses
- Try those as usernames against SMTP-AUTH on 587 with common passwords
- Valid creds → can send mail as that user
Open relay → phishing pretexts:
- Confirm open relay
- Send phishing mail with
MAIL FROM:<[email protected]>through the target’s own relay - Mail arrives at internal employees with the company’s own DKIM/SPF (because it was sent from the actual mail server)
Headers → internal recon:
- Get a legitimate email out of the target (signup form, support inquiry, anything)
- Read
Received:chain - Add internal hostnames and IPs to target list
SMTP banner → version → CVE:
- Identify Postfix/Exim/Sendmail version from banner
- Cross-reference for known RCEs (Exim has had several major RCE CVEs)
- Test carefully - SMTP CVEs that fail can DoS the server
Quick reference
Section titled “Quick reference”| Task | Command |
|---|---|
| Service scan | nmap -sV -sC -p25,465,587 <target> |
| All SMTP NSE | nmap -p25 --script "smtp-*" <target> |
| Manual telnet | telnet <target> 25 then EHLO test |
| User enum (VRFY) | VRFY <user> after EHLO |
| User enum (RCPT) | MAIL FROM:<x@x> then RCPT TO:<user@target> |
| Mailing list expand | EXPN <list-name> |
| Open relay test | nmap -p25 --script smtp-open-relay -v <target> |
| Manual send (swaks) | swaks --to <to> --from <from> --server <target>:25 |
| TLS connect | openssl s_client -connect <target>:25 -starttls smtp |
| TLS-wrapped (465) | openssl s_client -connect <target>:465 |
| Metasploit enum | use auxiliary/scanner/smtp/smtp_enum |