Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 29d97d1

Browse filesBrowse files
miss-islingtonambv
andauthored
[3.6] bpo-43124: Fix smtplib multiple CRLF injection (GH-25987) (GH-28038)
Co-authored-by: Miguel Brito <5544985+miguendes@users.noreply.github.com> Co-authored-by: Łukasz Langa <lukasz@langa.pl> (cherry picked from commit 0897253)
1 parent da9d6c5 commit 29d97d1
Copy full SHA for 29d97d1

File tree

3 files changed

+65
-3
lines changed
Filter options

3 files changed

+65
-3
lines changed

‎Lib/smtplib.py

Copy file name to clipboardExpand all lines: Lib/smtplib.py
+8-3Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -361,10 +361,15 @@ def send(self, s):
361361
def putcmd(self, cmd, args=""):
362362
"""Send a command to the server."""
363363
if args == "":
364-
str = '%s%s' % (cmd, CRLF)
364+
s = cmd
365365
else:
366-
str = '%s %s%s' % (cmd, args, CRLF)
367-
self.send(str)
366+
s = f'{cmd} {args}'
367+
if '\r' in s or '\n' in s:
368+
s = s.replace('\n', '\\n').replace('\r', '\\r')
369+
raise ValueError(
370+
f'command and arguments contain prohibited newline characters: {s}'
371+
)
372+
self.send(f'{s}{CRLF}')
368373

369374
def getreply(self):
370375
"""Get a reply from the server.

‎Lib/test/test_smtplib.py

Copy file name to clipboardExpand all lines: Lib/test/test_smtplib.py
+55Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,16 @@ def testEXPNNotImplemented(self):
286286
self.assertEqual(smtp.getreply(), expected)
287287
smtp.quit()
288288

289+
def test_issue43124_putcmd_escapes_newline(self):
290+
# see: https://bugs.python.org/issue43124
291+
smtp = smtplib.SMTP(HOST, self.port, local_hostname='localhost',
292+
timeout=10) # support.LOOPBACK_TIMEOUT in newer Pythons
293+
self.addCleanup(smtp.close)
294+
with self.assertRaises(ValueError) as exc:
295+
smtp.putcmd('helo\nX-INJECTED')
296+
self.assertIn("prohibited newline characters", str(exc.exception))
297+
smtp.quit()
298+
289299
def testVRFY(self):
290300
smtp = smtplib.SMTP(HOST, self.port, local_hostname='localhost', timeout=3)
291301
expected = (252, b'Cannot VRFY user, but will accept message ' + \
@@ -355,6 +365,51 @@ def testSendNeedingDotQuote(self):
355365
mexpect = '%s%s\n%s' % (MSG_BEGIN, m, MSG_END)
356366
self.assertEqual(self.output.getvalue(), mexpect)
357367

368+
def test_issue43124_escape_localhostname(self):
369+
# see: https://bugs.python.org/issue43124
370+
# connect and send mail
371+
m = 'wazzuuup\nlinetwo'
372+
smtp = smtplib.SMTP(HOST, self.port, local_hostname='hi\nX-INJECTED',
373+
timeout=10) # support.LOOPBACK_TIMEOUT in newer Pythons
374+
self.addCleanup(smtp.close)
375+
with self.assertRaises(ValueError) as exc:
376+
smtp.sendmail("hi@me.com", "you@me.com", m)
377+
self.assertIn(
378+
"prohibited newline characters: ehlo hi\\nX-INJECTED",
379+
str(exc.exception),
380+
)
381+
# XXX (see comment in testSend)
382+
time.sleep(0.01)
383+
smtp.quit()
384+
385+
debugout = smtpd.DEBUGSTREAM.getvalue()
386+
self.assertNotIn("X-INJECTED", debugout)
387+
388+
def test_issue43124_escape_options(self):
389+
# see: https://bugs.python.org/issue43124
390+
# connect and send mail
391+
m = 'wazzuuup\nlinetwo'
392+
smtp = smtplib.SMTP(
393+
HOST, self.port, local_hostname='localhost',
394+
timeout=10) # support.LOOPBACK_TIMEOUT in newer Pythons
395+
396+
self.addCleanup(smtp.close)
397+
smtp.sendmail("hi@me.com", "you@me.com", m)
398+
with self.assertRaises(ValueError) as exc:
399+
smtp.mail("hi@me.com", ["X-OPTION\nX-INJECTED-1", "X-OPTION2\nX-INJECTED-2"])
400+
msg = str(exc.exception)
401+
self.assertIn("prohibited newline characters", msg)
402+
self.assertIn("X-OPTION\\nX-INJECTED-1 X-OPTION2\\nX-INJECTED-2", msg)
403+
# XXX (see comment in testSend)
404+
time.sleep(0.01)
405+
smtp.quit()
406+
407+
debugout = smtpd.DEBUGSTREAM.getvalue()
408+
self.assertNotIn("X-OPTION", debugout)
409+
self.assertNotIn("X-OPTION2", debugout)
410+
self.assertNotIn("X-INJECTED-1", debugout)
411+
self.assertNotIn("X-INJECTED-2", debugout)
412+
358413
def testSendNullSender(self):
359414
m = 'A test message'
360415
smtp = smtplib.SMTP(HOST, self.port, local_hostname='localhost', timeout=3)
+2Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Made the internal ``putcmd`` function in :mod:`smtplib` sanitize input for
2+
presence of ``\r`` and ``\n`` characters to avoid (unlikely) command injection.

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.