Testing for the original POODLE vulnerability was easy because it was an inherent problem with SSLv3, so if you find SSLv3 enabled then you’ve found POODLE (although other factors such as cipher suite preference have a role to play – see my previous post). Like Heartbleed, though, testing for POODLE over TLS is conceptually easy but it falls within a class of flaws that requires bespoke tools as an unpatched version of
openssl, for example, won’t do what you want it to do. This article discusses how the Python tlslite library can be used to test for POODLE_TLS – and so much more.
What is tlslite?
From the source “TLS Lite is an open source python library that implements SSL and TLS”. I’d seen references to it in the original BEAST post written by Thai Duong and an article on TLS Prober by Yngve Pettersen. This gave me some confidence that tlslite would be a good starting point. Obviously it’s not going to be fast but that doesn’t matter. With a SSL/TLS implementation in a high level language, it would be much easier to make the changes required for the sorts of tests I wanted to run, and I thought POODLE_TLS would be a good one to try first.
TLS Prober is in fact where I wanted to be heading. It works on a modified version of tlslite to test for various SSL/TLS bugs. However, the public source code hasn’t been updated since Yngve left Opera in 2013 and thus wouldn’t cover POODLE_TLS. While I could have added that capability, I decided to ignore TLS Prober (for now) and start afresh with the latest tlslite – mainly as it would be a good learning experience.
How to test for POODLE_TLS
I’m not going to re-hash theory that’s already covered elsewhere. Suffice to say that implementations of TLS that are faithful to the RFC shouldn’t be vulnerable to POODLE because the spec states what the contents of the padding bytes should be. Therefore, the way to test for POODLE_TLS is to ignore that rule and see if the connection is terminated by the server. This isn’t the same as performing a full attack but like all testing you have to compromise between accuracy and aggressiveness. I think this test is a good indication. After some rummaging through the source code and a bit of debugging, I found what I wanted.
Changes to tlslite
It seemed a bit crazy to fork the original project as my changes were tiny. I also thought that working through the changes here may be helpful to anyone else who wants to do the same sort of thing.
So to begin with I needed to signal to tlslite that I wanted to send TLS messages with invalid padding. You get things going with tlslite through the
TLSConnection class so I changed how that was instantiated.
TLSConnection inherits from
TLSRecordLayer, which is where the padding code lives, so that needed changing too. Within the “tlslite” folder I made the following changes (obviously line numbers will be version dependent so I’ve added the original code too; my version was 0.4.8):
Line 52 was:
def __init__(self, sock):
def __init__(self, sock, check_poodle_tls=False):
# now i can signal whether or not I want to perform the test
# if you already have tlslite, you can change it safely because
check_poodle_tls defaults to
False so it’s backward-compatible with any existing code that makes use of tlslite
Line 61 was:
TLSRecordLayer.__init__(self, sock, check_poodle_tls)
# I need to pass that signal on to the parent
Line 102 was:
def __init__(self, sock):
def __init__(self, sock, check_poodle_tls):
After line 103
self.sock = sock added new line:
self.check_poodle_tls = check_poodle_tls
After line 600
paddingBytes = bytearray([paddingLength] * (paddingLength+1)) added new lines:
if self.check_poodle_tls == True:
paddingBytes = bytearray(x ^ 42 for x in paddingBytes[0:-1])
# change all but the last of the padding bytes to be invalid (just XOR with 42, the answer to everything)
# make the last byte of padding valid = the number of padding bytes
And that’s it! Remember, as it’s Python, that tabs are important and the new code needs to be properly aligned.
POODLE_TLS test script
I then created the test script (available here), which attempts a normal TLS connection first before testing for POODLE using the invalid padding trick. Place the script within the modified tlslite and run it as
test_poodle_tls.py <hostname>. Remember, it only tests for POODLE over TLS, not SSLv3.
I’ve noticed that sometimes the normal connection fails and one of the reasons for this is that the server does not support any of the small number of cipher suites offered by tlslite. In this case no conclusion can be drawn – and the script catches that.