Defending Against SMTP Smuggling: Hardening Postfix, Exim, and Gateway MTAs

Defending Against SMTP Smuggling: Hardening Postfix, Exim, and Gateway MTAs

Problem

SMTP request smuggling is a class of email injection vulnerability first described in detail by SEC Consult in late 2023, though the underlying protocol ambiguity has existed since the early SMTP RFCs. It exploits a discrepancy in how different mail transfer agents (MTAs) interpret the end-of-DATA sequence — the marker that signals the end of a message body in an SMTP session.

RFC 5321 specifies that the DATA terminator is \r\n.\r\n (CR LF period CR LF). An MTA receiving a message is supposed to strip any leading dots from lines within the message body (dot-stuffing) and recognize the exact five-byte sequence as end-of-message. The smuggling attack works because some MTAs are more permissive: they treat \n.\r\n, \r\n.\n, or \n.\n as equivalent end-of-DATA markers while others do not.

When two MTAs in a sequence disagree on where a message ends, an attacker can craft a single SMTP transaction that the sending MTA treats as one message but the receiving MTA interprets as two. The second, injected message can have arbitrary headers — including a From: address in the domain of the first legitimate sender, complete with an envelope that satisfies SPF alignment and, in some cases, a valid DKIM signature over the injected content.

The practical impact is email spoofing that bypasses SPF, DKIM, and DMARC. An attacker who can send mail through any compliant SMTP relay — including a shared hosting provider or a legitimate customer account on an ESP — can inject forged messages appearing to originate from trusted domains that have strict DMARC p=reject policies. Recipients see a sender address and authentication results that indicate a trusted origin.

SEC Consult demonstrated the attack against:

  • Cisco Secure Email Gateway — accepted permissive line endings, enabling injection through outbound relay from legitimate Cisco customers.
  • Postfix — vulnerable in default configurations prior to 3.8.4 and specific earlier patchsets.
  • Sendmail — permissive end-of-DATA interpretation in several releases.
  • GMX / Web.de — vulnerable as receiving MTAs, accepting injected messages from compliant senders.

The vulnerability is distinct from traditional email spoofing in that it does not require the attacker to control any aspect of the target domain’s DNS records. The forged message genuinely transits through an authorized mail server for the spoofed domain’s SPF record. DMARC p=reject provides no protection when the first hop of a multi-hop relay path is the one that processes the injected message content.

As of early 2026, patches exist for the major open-source MTAs but deployment is uneven. Gateway appliances (both hardware and cloud) vary widely; several commercial email security gateways remain vulnerable in default configurations. The attack is particularly relevant for organizations that route outbound mail through third-party relay services, where the gateway MTA and the downstream receiving MTA may have different lineage.

Target systems: Postfix ≤3.8.3 and ≤3.7.8 (LTS) on all Linux distributions; Exim ≤4.97.1; Sendmail ≤8.17.1; Cisco Secure Email Gateway pre-2024 firmware; any environment routing mail through a multi-hop MTA chain including a cloud email security gateway.


Threat Model

Adversary 1 — Third-party relay user. Access level: a legitimate account on any shared mail relay or email service provider (ESP) that routes through a vulnerable gateway MTA. Objective: inject forged messages appearing to originate from a different customer’s domain, bypassing that domain’s DMARC policy.

Adversary 2 — Business email compromise (BEC) actor. Access level: ability to send SMTP from any internet-connected host. Objective: exploit a vulnerable receiving gateway to inject spoofed executive emails that appear to pass email authentication, enabling financial fraud or credential phishing.

Adversary 3 — Phishing campaign operator. Access level: bulk email sending infrastructure. Objective: send phishing emails at scale appearing to originate from high-reputation domains (financial institutions, SaaS providers) whose DMARC policies would otherwise block forgeries.

Adversary 4 — Insider threat or compromised account. Access level: an authenticated account on an organization’s outbound mail relay. Objective: inject forged messages into inbound mail streams to frame other users or create false audit trails.

Without hardening: a single SMTP transaction through a misconfigured relay injects a forged email. SPF and DMARC checks pass. The message is delivered to the inbox. With hardening: strict line-ending enforcement causes the receiving MTA to reject or quarantine the second injected message because the DATA terminator does not match the RFC-compliant \r\n.\r\n sequence.


Configuration / Implementation

Step 1 — Identify your MTA version and vulnerability status

# Postfix
postconf mail_version
postconf smtpd_forbid_bare_newline

# Exim
exim --version | head -2

# Sendmail
sendmail -d0.1 -bv root 2>&1 | grep version

Postfix versions where smtpd_forbid_bare_newline defaults to normalize or yes:

  • 3.9.0+: default yes (rejects bare newlines)
  • 3.8.4+: backport available (check your distro’s package)
  • Older: vulnerable by default

Step 2 — Harden Postfix

Postfix introduced the smtpd_forbid_bare_newline configuration option as the primary defence:

# /etc/postfix/main.cf

# Reject SMTP commands and message content that contains bare \n (not \r\n)
# "yes" rejects the session; "normalize" replaces bare \n with \r\n before processing
smtpd_forbid_bare_newline = yes
smtpd_forbid_bare_newline_exclusions = $mynetworks

# Reject DATA content with bare newlines in message headers
# Protects against header injection variants
smtpd_reject_unlisted_recipient = yes

# Require RFC-compliant HELO
smtpd_helo_required = yes

# Strict RFC 5321 compliance for DATA termination
# Postfix 3.9+ enforces this automatically when forbid_bare_newline = yes

For Postfix 3.7.x (current Ubuntu LTS ships 3.6/3.7), apply the backported fix:

# Ubuntu 22.04 LTS
sudo apt update && sudo apt install --only-upgrade postfix

# RHEL 9 (postfix 3.5.x — use the smtpd_forbid_bare_newline backport)
sudo dnf update postfix

# Verify the option is recognized
postconf -d smtpd_forbid_bare_newline

After updating main.cf:

postfix check
systemctl reload postfix

# Test that bare newlines are rejected
# Using swaks (SMTP testing tool)
swaks --to test@yourdomain.com \
      --server localhost \
      --data "Subject: test\nBody line with bare LF\r\n.\r\n" \
      --quit-after DATA
# Expected: 550 or 500 rejection

Step 3 — Harden Exim

Exim 4.97 and later include the allow_insecure_tainted_data and related fixes. For the smuggling-specific fix:

# /etc/exim4/exim4.conf.template or /etc/exim4/conf.d/main/

# Reject DATA sections containing bare \n (not preceded by \r)
# Exim 4.97.1+ fix: smtp_receive_timeout and data verification
# The key fix is in the SMTP server's handling of DATA command termination

# For Exim 4.96 and below, add this ACL:
acl_smtp_data = acl_check_data

begin acl
acl_check_data:
  # Reject messages with bare LF in body (SMTP smuggling indicator)
  deny  condition = ${if match{$message_body}{\N(^|[^\r])\n\.\r\n\N}{yes}{no}}
        message = 550 Message contains suspicious line endings indicative of SMTP smuggling

  accept

Upgrade path:

# Debian/Ubuntu
apt install --only-upgrade exim4

# Verify version
exim --version | head -1
# Should show 4.97.1 or later for full fix

Step 4 — Harden receiving gateway / cloud email relay

For organizations using a cloud email security gateway (SEG) in front of on-premises or cloud MTAs, verify the gateway’s handling:

# Test using a crafted SMTP transaction
# This sends a message with an embedded bare-LF DATA terminator
# Expected: the second injected message should NOT be accepted

cat <<'EOF' | nc -q 5 mail.yourdomain.com 25
EHLO attacker.example.com
MAIL FROM:<sender@legitimate.example.com>
RCPT TO:<victim@yourdomain.com>
DATA
From: sender@legitimate.example.com
To: victim@yourdomain.com
Subject: Real message

Real message body
.
MAIL FROM:<forged@legitimate.example.com>
RCPT TO:<victim@yourdomain.com>
DATA
From: forged@legitimate.example.com
To: victim@yourdomain.com
Subject: Injected message

This should not be delivered if the gateway is patched.

.
QUIT
EOF

If you receive two separate messages, your receiving MTA is vulnerable to the injection. If you receive only one, the second transaction was rejected.

Step 5 — Configure SPF, DKIM, and DMARC as layered controls

While SMTP smuggling can bypass DMARC on vulnerable receiving MTAs, strengthening the email authentication posture limits other spoofing vectors and ensures maximum visibility:

; Strict SPF — explicit fail for everything not listed
yourdomain.com. IN TXT "v=spf1 include:_spf.google.com ip4:203.0.113.10 -all"

; DMARC — reject (not quarantine) for strict protection
_dmarc.yourdomain.com. IN TXT "v=DMARC1; p=reject; rua=mailto:dmarc-reports@yourdomain.com; ruf=mailto:dmarc-forensics@yourdomain.com; adkim=s; aspf=s; pct=100"

; DKIM — 2048-bit key minimum
mail._domainkey.yourdomain.com. IN TXT "v=DKIM1; k=rsa; p=MIIBIjAN..."

Enable DMARC reporting to detect if forged messages are being attempted:

# Parse DMARC aggregate reports to spot smuggling attempts
# Reports show messages that failed alignment — if forged messages are being sent
# through your authorized relays, reports will show them with source IPs

Step 6 — Monitor for smuggling indicators in mail logs

# Postfix: look for rejection messages from smtpd_forbid_bare_newline
grep "bare newline" /var/log/mail.log | tail -20

# Exim: look for ACL rejection hits
grep "SMTP smuggling" /var/log/exim4/mainlog | tail -20

# Alert on sudden increase in DATA-phase rejections (may indicate active exploitation attempt)
# Add to your log monitoring / SIEM:
# Pattern: "smtpd.*reject.*550.*bare newline" (Postfix)
# Pattern: "SMTP data: bare LF rejected" (Exim 4.97.1+)

Create a structured alert rule:

# Example: Prometheus alerting rule for Postfix rejection rate
- alert: SMTPSmugglingSuspectedAttempt
  expr: |
    rate(postfix_smtpd_rejects_total{reason="bare_newline"}[5m]) > 0.1
  for: 2m
  labels:
    severity: warning
  annotations:
    summary: "SMTP bare-newline rejections detected — possible smuggling attempt"
    description: "{{ $value }} rejections/sec on {{ $labels.instance }}"

Expected Behaviour

Signal Before hardening After hardening
SMTP session with bare \n in DATA Accepted; injected message delivered Rejected with 5xx response
postconf smtpd_forbid_bare_newline normalize or absent yes
SMTP smuggling test: two messages delivered Two separate messages in inbox One message; second transaction rejected
Exim version 4.96 or earlier (vulnerable) 4.97.1+ or ACL rule applied
DMARC report showing forged-domain alignment failures High volume, many sources Volume drops; surviving attempts come from novel attack vectors

Verification after applying Postfix fix:

postconf smtpd_forbid_bare_newline
# smtpd_forbid_bare_newline = yes

# Send a test with swaks using bare LF
swaks --server 127.0.0.1:25 \
      --from test@yourdomain.com \
      --to test@yourdomain.com \
      --body $'line1\ninjected\r\n.\r\n' \
      2>&1 | grep "^<"
# Expected: < 550 5.5.0 Error: bare newline received

Trade-offs

Aspect Benefit Cost Mitigation
smtpd_forbid_bare_newline = yes Eliminates smuggling via bare LF Rejects some legacy mail clients that send bare LF (uncommon in 2026 but some IoT/embedded senders) Use normalize mode first to convert rather than reject; monitor rejection rate for 1 week then switch to yes
DMARC p=reject Prevents most domain spoofing May reject legitimate forwarded mail, mailing lists Use pct=25 initially; roll to 100 over 30 days; configure SRS (Sender Rewriting Scheme) for forwarders
Strict ACL on Exim DATA phase Blocks injection at DATA acceptance CPU overhead of regex match on message body Scope to inbound-only ACL; skip for trusted relay chains
Alerting on DATA-phase rejections Early warning of active exploitation Alert fatigue if thresholds are too low Baseline normal rejection rate first; set threshold at 5× normal

Failure Modes

Failure Symptom Detection Recovery
Postfix rejects legitimate mail from IoT sender with bare LF Monitoring alert; sender reports undeliverable Check mail logs for 550 bare newline; identify source IP Temporarily add source IP to smtpd_forbid_bare_newline_exclusions; notify sender to fix MUA
Cloud SEG not patched — injected messages bypass gateway Injected message delivered to inbox; DMARC report shows unexpected alignment pass Enable DMARC forensic reports (ruf=); check SPF pass with unexpected source IPs Contact SEG vendor for patch; add inbound ACL on internal MTA as second line of defence
Exim ACL causes false positive on multipart message boundary Legitimate message with certain MIME boundaries rejected Exim log shows ACL deny with suspicious line endings for known-good sender Refine regex to exclude MIME boundary contexts; update Exim to 4.97.1+ for the proper fix
Postfix smtpd_forbid_bare_newline option not recognized on old version postfix check returns unknown parameter warning postconf smtpd_forbid_bare_newline returns error Upgrade Postfix; use distro backport package