[security] smtplib multiple CRLF injection #87290
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
assignee = None closed_at = <Date 2021-08-29.15:09:19.891> created_at = <Date 2021-02-04.13:05:34.075> labels = ['type-security', 'deferred-blocker', '3.8', '3.9', '3.10', '3.11', '3.7'] title = '[security] smtplib multiple CRLF injection' updated_at = <Date 2021-08-30.19:22:04.988> user = 'https://bugs.python.org/martinortner'
activity = <Date 2021-08-30.19:22:04.988> actor = 'ned.deily' assignee = 'none' closed = True closed_date = <Date 2021-08-29.15:09:19.891> closer = 'lukasz.langa' components =  creation = <Date 2021-02-04.13:05:34.075> creator = 'martin.ortner' dependencies =  files =  hgrepos =  issue_num = 43124 keywords = ['patch'] message_count = 17.0 messages = ['386481', '390048', '393206', '393208', '393209', '394160', '397421', '397423', '397433', '397806', '400535', '400540', '400541', '400543', '400546', '400661', '400663'] nosy_count = 8.0 nosy_names = ['barry', 'christian.heimes', 'ned.deily', 'r.david.murray', 'lukasz.langa', 'miss-islington', 'martin.ortner', 'miguendes'] pr_nums = ['25987', '28034', '28035', '28036', '28037', '28038'] priority = 'deferred blocker' resolution = 'fixed' stage = 'resolved' status = 'closed' superseder = None type = 'security' url = 'https://bugs.python.org/issue43124' versions = ['Python 3.6', 'Python 3.7', 'Python 3.8', 'Python 3.9', 'Python 3.10', 'Python 3.11']
The text was updated successfully, but these errors were encountered:
// reported via PSRT email (see timeline; last contact: Alex/PSRT)
# Vulnerability Note
Two CR-LF injection points have been discovered in the Python standard library for
The root cause of this is likely to be found in the design of the
It is recommended to reject or encode
The root cause of this (and probably also some earlier reported CR-LF injections) is the method
def putcmd(self, cmd, args=""): """Send a command to the server.""" if args == "": str = '%s%s' % (cmd, CRLF) else: str = '%s %s%s' % (cmd, args, CRLF) self.send(str)
However, the issue was initially found in
self.putcmd("mail", "FROM:%s%s" % (quoteaddr(sender), optionlist))
A similar issue was found with
Here's a snipped of
def helo(self, name=''): """SMTP 'helo' command. Hostname to send for this command defaults to the FQDN of the local host. """ self.putcmd("helo", name or self.local_hostname) (code, msg) = self.getreply() self.helo_resp = msg return (code, msg)
We highly recommend, fixing this issue once and for all directly in
## Proof of Concept
import smtplib server = smtplib.SMTP('localhost', 10001, "hi\nX-INJECTED") # localhostname CRLF injection server.set_debuglevel(1) server.sendmail("email@example.com", "firstname.lastname@example.org", "wazzuuup\nlinetwo") server.mail("email@example.com",["X-OPTION\nX-INJECTED-1","X-OPTION2\nX-INJECTED-2"]) # options CRLF injection
### Proposed Fix
diff --git a/Lib/smtplib.py b/Lib/smtplib.py index e2dbbbc..9c16e7d 100755 --- a/Lib/smtplib.py +++ b/Lib/smtplib.py @@ -365,10 +365,10 @@ class SMTP: def putcmd(self, cmd, args=""): """Send a command to the server.""" if args == "": - str = '%s%s' % (cmd, CRLF) + str = cmd else: - str = '%s %s%s' % (cmd, args, CRLF) - self.send(str) + str = '%s %s' % (cmd, args) + self.send('%s%s' % (str.replace('\n','\\n'), CRLF))
## Vendor Response
Vendor response: gone silent
This bug report starts with "a malicious user with direct access to `smtplib.SMTP(..., local_hostname, ..)", which is a senseless supposition. Anyone with "access to" the SMTP object could just as well be talking directly to the SMTP server and do anything they want that SMTP itself allows.
The concern here is that data a program might obtain *from unsanitized user input* could be used to do header injection. The "proof of concept" does not address this at all. We'd need to see a scenario under which data that could reasonably be derived from user input ends up being passed as arguments to an smtplib method that calls putcmd with arguments.
So, I would rate this as *very* low impact issue, unless someone has an *actual example* of code using smtplib that passes user input through to smtplib commands in an exploitable way.
That said, it is perfectly reasonable to be proactive here and prevent scenarios we haven't yet thought of, by doing as recommended (and a bit more) by raising a ValueError if 'args' in the putcmd call contain either \n or \r characters. I don't think we need to check 'cmd', because I can't see any scenario in which the SMTP command would be derived from user input. If you want to be *really* paranoid you could check cmd too, and since it will always be a short string the additional performance impact will be minor.
Let's not argue about the phrasing and settle on the fact that I am not a native English speaker which might be the root cause of the confusion. The core of the issue is that this *unexpected side-effect* may be security-relevant. Fixing it probably takes less time than arguing about phrasing, severity, or spending time describing exploitation scenarios for a general-purpose library that should protect the underlying protocol from injections.
Be kind, I come in peace.
My apologies, I did not think about the possibility of an English issue. I was reacting to the "security report speak", which I find often makes a security issue sound worse than it is :) Thank you for reporting this problem, and I do think we should fix it.
My posting was directed at the severity of the issue, since it was potentially holding up a release. My point about the example is that without an example of code that could reasonably be expected to use user input in a call that could inject newlines, we can treat this as a low priority issue. If we had a proposed example of such code, then the priority would be higher. If it was an example of such code "in the wild", then it would be quite high :)
The reason I'm saying we should have an example in order to consider it higher priority is that I cannot see *any* likelihood that this would be a problem in practice. Let me explain.
putcmd is an *internal* interface. If we look at the commands that call putcmd or docmd, the only ones that pass extra data that aren't pretty obviously safe (ie: not clearly sanitized data) are rcpt and mail. In both cases the item of concern is optionslist. optionslist is a list of *SMTP server options. This is not data that is reasonably taken from user input, it is data provided *by the programmer*.
[*] I did double check to make sure that email.utils.parseaddr sanitizes both \r and \r, just to be sure :)
Therefore this is *not* a significant security issue. But as I said, we should take the "defense in depth" approach and apply the check in putcmd as you recommend. I just don't think it needs to hold up a release.