Testing for POODLE_TLS Manually

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)
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.

6 thoughts on “Testing for POODLE_TLS Manually

  1. Pingback: How to test POODLE over TLS? - BlogoSfera

    1. Jerome Post author

      I’d be interested to hear of any examples you can share that you believe are false positives or negatives.

      1. Ben

        Hi Jerome, I do not know if you can see this, but this is something I’d still like to ask.

        After running a test on http://www.finalsite.com , your script told me that it is not vulnerable. However, after executing the Qualys SSL Test: https://www.ssllabs.com/ssltest/analyze.html?d=finalsite.com&s= one of the IPs for this site resulted to be shown as “vulnerable to TLS POODLE attack”. I do not know whether it is a misdetection on the Qualys test or your script. Any insights?


          1. Hubert

            I tested both IPs with test_poodle_tls.py and the result is “The host does NOT appear to be vulnerable to POODLE_TLS” for both.
            Qualys marks one as vulnerable and one not.
            The one that is marked vulnerable supports TLS_FALLBACK_SCSV:
            openssl s_client -connect -fallback_scsv -no_tls1_2

            If I get the theory right, TLS implementations that re-use the SSLv3 code for the padding bytes check can be vulnerable to poodle even on a TLS connection.

            The question is: Does test_poodle_tls.py work and only trigger when a TLS-implementation is using the SSLv3 padding bytes check and does Qualys not check for the actual vulnerability but just for the possibility of downgrade attacks (which make the connection cryptographically less strong but don’t enable an attack by themselves).

            It would be nice to have a test server somewhere which exposes this poodle-tls issue (all servers I tested – the ones that Qualys complains about too – have been tested as not vulnerable by test_poodle_tls.py)


          2. Jerome Post author

            You do get the theory right: the problem is that, while the TLS standard resolves the problem that plagued SSLv3, if the same code is re-used in the TLS implementation then the bug remains. In this case, whether or not TLS_FALLBACK_SCSV is supported is irrelevant as the attacker doesn’t have to knock the connection down to SSLv3.
            My motivation for the post was just to figure out how to do a test – but it’s only one test, there may be other and/or better ways of doing it (and the library I’m using is much more restricted in terms of ciphers it knows about). Ivan from SSL Labs states that his check will “negotiate a connection with the server, but using broken CBC padding. If the handshake succeeds, the server is considered vulnerable. We also submit a HTTP request and expect a response. So it should be pretty accurate”. My script doesn’t make a HTTP request but it does attempt to send a message with corrupted padding. On buggy implementations, perhaps the result depends on the TLS version or cipher suite that’s negotiated even if in theory it shouldn’t make a difference. Overall I would favour SSL Lab’s result, it should be more thorough, but you could always post a question on the community forum about this specific example – Ivan is usually very responsive. Do let me know if you decide to put a question up.

Leave a Reply

Your email address will not be published. Required fields are marked *


You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>