8000 [3.7] bpo-43124: Fix smtplib multiple CRLF injection (GH-25987) (GH-2… · python/cpython@d2cc04c · GitHub
[go: up one dir, main page]

Skip to content

Commit d2cc04c

Browse files
miss-islingtonambv
andauthored
[3.7] bpo-43124: Fix smtplib multiple CRLF injection (GH-25987) (GH-28037)
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 e9b85af commit d2cc04c

File tree

3 files changed

+65
-3
lines changed

3 files changed

+65
-3
lines changed

Lib/smtplib.py

Lines 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

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,16 @@ def testEXPNNotImplemented(self):
282282
self.assertEqual(smtp.getreply(), expected)
283283
smtp.quit()
284284

285+
def test_issue43124_putcmd_escapes_newline(self):
286+
# see: https://bugs.python.org/issue43124
287+
smtp = smtplib.SMTP(HOST, self.port, local_hostname='localhost',
288+
timeout=10) # support.LOOPBACK_TIMEOUT in newer Pythons
289+
self.addCleanup(smtp.close)
290+
with self.assertRaises(ValueError) as exc:
291+
smtp.putcmd('helo\nX-INJECTED')
292+
self.assertIn("prohibited newline characters", str(exc.exception))
293+
smtp.quit()
294+
285295
def testVRFY(self):
286296
smtp = smtplib.SMTP(HOST, self.port, local_hostname='localhost', timeout=3)
287297
expected = (252, b'Cannot VRFY user, but will accept message ' + \
@@ -351,6 +361,51 @@ def testSendNeedingDotQuote(self):
351361
mexpect = '%s%s\n%s' % (MSG_BEGIN, m, MSG_END)
352362
self.assertEqual(self.output.getvalue(), mexpect)
353363

364+
def test_issue43124_escape_localhostname(self):
365+
# see: https://bugs.python.org/issue43124
366+
# connect and send mail
367+
m = 'wazzuuup\nlinetwo'
368+
smtp = smtplib.SMTP(HOST, self.port, local_hostname='hi\nX-INJECTED',
369+
timeout=10) # support.LOOPBACK_TIMEOUT in newer Pythons
370+
self.addCleanup(smtp.close)
371+
with self.assertRaises(ValueError) as exc:
372+
smtp.sendmail("hi@me.com", "you@me.com", m)
373+
self.assertIn(
374+
"prohibited newline characters: ehlo hi\\nX-INJECTED",
375+
str(exc.exception),
376+
)
377+
# XXX (see comment in testSend)
378+
time.sleep(0.01)
379+
smtp.quit()
380+
381+
debugout = smtpd.DEBUGSTREAM.getvalue()
382+
self.assertNotIn("X-INJECTED", debugout)
383+
384+
def test_issue43124_escape_options(self):
385+
# see: https://bugs.python.org/issue43124
386+
# connect and send mail
387+
m = 'wazzuuup\nlinetwo'
388+
smtp = smtplib.SMTP(
389+
HOST, self.port, local_hostname='localhost',
390+
timeout=10) # support.LOOPBACK_TIMEOUT in newer Pythons
391+
392+
self.addCleanup(smtp.close)
393+
smtp.sendmail("hi@me.com", "you@me.com", m)
394+
with self.assertRaises(ValueError) as exc:
395+
smtp.mail("hi@me.com", ["X-OPTION\nX-INJECTED-1", "X-OPTION2\nX-INJECTED-2"])
396+
msg = str(exc.exception)
397+
self.assertIn("prohibited newline characters", msg)
398+
self.assertIn("X-OPTION\\nX-INJECTED-1 X-OPTION2\\nX-INJECTED-2", msg)
399+
# XXX (see comment in testSend)
400+
time.sleep(0.01)
401+
smtp.quit()
402+
403+
debugout = smtpd.DEBUGSTREAM.getvalue()
404+
self.assertNotIn("X-OPTION", debugout)
405+
self.assertNotIn("X-OPTION2", debugout)
406+
self.assertNotIn("X-INJECTED-1", debugout)
407+
self.assertNotIn("X-INJECTED-2", debugout)
408+
354409
def testSendNullSender(self):
355410
m = 'A test message'
356411
smtp = smtplib.SMTP(HOST, self.port, local_hostname='localhost', timeout=3)
Lines 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