I'm trying to understand SMTP header injection. I'm using Python's SMTPLIB library to proto-type this vulnerability. Here is my code:

import smtplib

# create variables
server = 'smtp.zoho.com'
port = 587
to = '[email protected]'
user = '[email protected]'
passwd = 'pwd'
smtpserver = smtplib.SMTP(server, port)

def mail():
    smtpserver.login(user, passwd)
    header = 'To:' + to + '\n' + 'From: ' + user + '\ncc:[email protected]\n' + 'Subject:testing \n'
    msg = header + '\n test 5 \n\n'
    smtpserver.sendmail(user, to, msg)
    print header + 'done!'

# call mail method

I've tried using the Zoho and Gmail SMTP server. The email is successfully sent to the address in the "to" variable, but it is not sent to the "[email protected]" email address. When I view the message in Gmail or Zoho I do see the "[email protected]" in the CC field, but it never gets sent to the second email address. I've also tried to inject the Subject field with the same results.

Can someone explain this to me? Is this some filtering done on Gmail/Zoho's end?

Thanks, Johnny_v

3 Answers 3


Simple case: You do not send the email to [email protected].

The headers of the email are not used by the SMTP server.

You add a CC: to the email header but the SMTP server does not read this header.

The SMTP server only sees what you transmit with your line smtpserver.sendmail(user, to, msg).

Basically the communication is as follows (S: server, C: client) (the TLS and authentication part is left out) (parts in brackets [] are comments by me) based on your script:

S: 220 smtp.zoho.com
C: MAIL FROM: <[email protected]> [the address user]
S: 250 OK
C: RCPT TO: <[email protected]> [the address to]
S: 250 OK
C: DATA [your msg follows]
   To: [email protected]
   From: [email protected]
   cc:[email protected]
    test 5 

S: 250 OK
S: 221 Good Bye

The mail server only respects the part RCPT TO:. Everthing else (between DATA and .) is only data and no scanned/parsed by the SMTP server.

To send the email to someone else you have to add a second command with RCPT TO:. This must be done with another call to smtpserver.sendmail(). As I don't know the library used I cannot tell you if you have to create a second call or if there is a call with a list of recipients.

Note also that you can send the email to someone without adding the recipient to the header (this is used for BCC for example).

  • Hi Uwe, Thanks for the information. I was referencing the information found here: hpenterprisesecurity.com/vulncat/en/vulncat/python/…. It states that we can add recipients by submitting CRLF characters in the subject field.
    – Johnny_v
    May 13, 2015 at 8:05
  • 2
    @Johnny_v I read the article and would say that there is little understanding of the protocols involved. The only problem is when the inbox of the receiver ([email protected] in your case) automatically makes a Reply all . This would lead to an possible attack. But as it stands there is no possibility to inject the CC without modifying the direct communication. Also they reference an article on the OWASP site that explains the attack scenario other (as I explained it).
    – Uwe Plonus
    May 13, 2015 at 9:20
  • What would actually happen is that the headers of the message (not anything in the SMTP conversation) would end, and when a MUA displayed the message, all later headers would appear in the body (and if there were multiple parts, it would be a mess). The only way this is an issue is if someone is extracting the envelope recipient from the message, and really screws up their code (because you can't embed the new line in a header, because new lines terminate headers or make a continuation).
    – Tony Meyer
    May 13, 2015 at 11:28

The most common form of SMTP header injection is adding a To:, CC:, or BCC: header to send the email to an unintended recipient. This works with SMTP libraries that take a complete email with headers and parse it to figure out the recipients (most notably, the command-line sendmail invoked with the -t option and some invocations of the PHP mail() function).

Python's SMTPLIB is resistant to this: SMTP.sendmail() requires an explicit list of recipients, so changing the headers will only change the apparent list of recipients. This is because the email delivery process (apart from possibly the originating client) ignores the headers, and delivers to the "envelope To:" address (RCPT TO: in the SMTP conversation).


As Uwe Plonius explained, the article seems wrong in its explanation, however to me the concept remains valid.

Instead of the 20 sources cited in this article, I will rely only on one: RFC 821, section 3, defining the mail sending procedure.

Two things need to be noted there:

  • Each command must end with a <CRLF>, and not just <LF> as in your document. The correct end of line will therefore be "\r\n",
  • The mail server must accept at least up to 100 recipient for a single mail (the number 100 being imposed section 4.5.2 SIZES defining the "required minimum maximum" size for the recipients buffer), each on will be specified using a separate "RCPT TO:" command.

On a vulnerable server, you may therefore achieve the requested exploit by forging an email address, either as principal recipient or copy, containing an injected "RCPT TO:" command as follow:

to = "[email protected]>\r\nRCPT TO:<[email protected]"

For this vulnerability to be effective, you need at least the two prerequisites:

  • Your recipient address will not be filtered before reaching the server, the carriage return and line feed characters being invalid for a destination address,
  • The SMTP server implementation tolerates that you already send the next "RCPT TO:" command without waiting the server feedback.
  • 2
    Note that SMTP is RFC 5321. 821 is very obsolete. Also, addresses in MAIL FROM and RCPT TO should be in angle brackets (some MTAs will allow not doing this). Python's smtplib adds the brackets for the user, so you'd need to also close the bracket and open the next. You'd then find that you get an error because you're out of sequence (RCPT can't come after DATA unless it's a new message, and you'd need to terminate data as well, and smtplib will quote that, so you can't anyway). The article is quite simply wrong.
    – Tony Meyer
    May 13, 2015 at 11:21
  • 1
    @TonyMeyer Thanks for pointing out the brackets. I've corrected my answer with it.
    – Uwe Plonus
    May 13, 2015 at 11:23
  • @TonyMeyer: Thanks for your comment, I have added the missing brackets. The issue here is that Python's SMTPlib silently filters the addresses, so this matches my first point ("recipient address must not be filtered"). However, I've just tested manually and as I expected my SMTP server reads input line by line, and therefore accepts blindly when there is two "RCPT TO:" coming in a row as a result of a successful injection. And the point to such injection is less to add more recipient, but merely to get the capability to inject arbitrary SMTP commands when some field are not properly filtered. May 13, 2015 at 13:22
  • 1
    You can't infer that from what's written. All I can find on that page is that it says "For details of SMTP and ESMTP operation, consult RFC 821 (Simple Mail Transfer Protocol) and RFC 1869 (SMTP Service Extensions)." RFC 821 is horribly out of date as already stated (it was written back in 1982, for a very different Internet) and was obsoleted by RFC 2821 (in 2001, obsoleting also RFC 1869) which in turn was obsoleted by RFC 5321 (in 2008). RFC 5321 is the current standards track document describing SMTP.
    – user
    May 13, 2015 at 14:34
  • 1
    I'm not sure you understand how SMTP works. The message headers are entirely separate from the envelope data used in SMTP. To have a header end up being used as a SMTP command, the header would need to terminate DATA (and smtplib won't let you do that) and you will be in the post DATA stage where you cannot add another recipient (you could start a new message). The envelope recipient and the headers in a message are entirely separate things.
    – Tony Meyer
    May 13, 2015 at 21:07

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .