Recent Python Security Improvements

Recent Python Security Improvements

Like all other popular programming languages, Python is constantly adding features and fixing bugs.  Let's take an in-depth look at two recently closed security holes because of the valuable lessons we can learn from them.

SMTP.startTLS() No longer Silently Fails due to Missing Responses

One example of Python's “batteries included” design is the smtplib module, which simplifies the code needed to implement a mail client using the Simple Mail Transport Protocol (SMTP).  One of the conveniences of using the smtplib module is the ease of establishing an encrypted connection for sending email using Transport Layer Security (TLS) via the included startTLS() function.  Until versions 2.7.12 and 3.4, this critical function had a programming oversight which could have been exploited by a malicious “Man in the Middle” (MITM).  According to the original report of this vulnerability:

“. . smtplib does not seem to raise an exception when the remote end (smtp server) is capable of negotiating starttls but fails to respond with 220 (ok) to an explicit call of SMTP.starttls(). This may allow a malicious MITM to perform a startTLS stripping attack if the client code does not explicitly check the response code for startTLS.”

Since an exception isn't raised when no response is received by the server, the rest of the client code is completely unaware that any mail sent from the client code to this server will not be encrypted.  The entry in Red Hat's bugzilla for this bug explains how this flaw could be exploited by an attacker:

“A man in the middle attacker could strip out the STARTTLS command without generating an exception on the Python SMTP client application, preventing the establishment of the TLS layer.”

This means is that if an attacker can intercept all the communication between the Python SMTP client code and the SMTP server, that attacker could remove the packet(s) containing the startTLS command from the client to the server.  The server would never receive this command, and therefore not send a response.  As we saw above, no response not only means no encryption of email, it also means the rest of the code actually assumes that the encrypted connection is ready to safely send mail through, when in fact all emails sent will be in plain text.  This could potentially be disastrous, because if a MITM can strip out information from packets between two systems, he could also read all unencrypted information within those packets too.

Expat Patches an Overflow

Python versions 2.7.12 and 3.4.5rc1  include an updated version of expat, which is a non-validating XML parser written in C.  A recently disclosed bug allowed maliciously crafted XML data to cause a heap-based-overflow, resulting in a denial of service.  This kind of vulnerability in Python's interface to expat isn't surprising: the official documentation for this API  begins with a warning about malicious XML data and eventually directs developers to use a safer, modified module.

The patched version of expat now shipping with Python is 2.1.1.  As of this writing, however the current version of expat is 2.2.0, and one of the four security fixes in this version is an improvement to the “insufficient fix” which expat 2.1.1 implemented.  Python developers should expect this re-fix to eventually flow downstream into both Python 2.7 and 3.X.

The Takeaway

These two security bug-fixes in Python illustrate three important lessons for developers:
1. In your code, take the time to consider how to properly handle remote system responses beyond the standard Yes/No or ACK/NAK (or their equivalents in the protocol you are using).  Note that the inadequate handling of missing responses in smtp.startTLS() could have led to unencrypted email transmission.

2. Don't always assume that no exception means no problem.  Whenever possible (especially in security-related code), check function results.  As bad as the smtp.startTLS() bug was, explicitly checking the startTLS() result in the client SMTP code would have revealed the nonexistent TLS encryption, as the original bug disclosure noted.

3. If you are parsing untrusted data, do not use a module well-known for not being secure against, well  untrusted data.  Expat is a fast XML parser and is great for trusted, authenticated and validated XML data, but just as with every other aspect of software development, make sure you use the right tool for the job.

Copyright © Python People