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):
tlsconnection.py
Line 52 was:
def __init__(self, sock):
Now:
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)
Now:
TLSRecordLayer.__init__(self, sock, check_poodle_tls)
# I need to pass that signal on to the parent
tlsrecordlayer.py
Line 102 was:
def __init__(self, sock):
Now:
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])
paddingBytes.append(paddingLength)
# 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.
Pingback: How to test POODLE over TLS? - BlogoSfera
I’d be interested to hear of any examples you can share that you believe are false positives or negatives.
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=184.106.184.118 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?
Thanks,
Ben.
Hi Ben
You’re testing two different IPs there – you tested http://www.finalsite.com which is 23.202.173.5, whereas judging by the URL SSL Labs was testing finalsite.com which is at 184.106.184.118.
Jerome
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 184.106.184.118:443 -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)
~Hubert
Hi
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.
Jerome
Attempting a normal TLS connection
- this failed with the error
Attempting a POODLE-style TLS connection
- this failed with the same error
Check the hostname, or this could be because the server does not support any of the cipher suites we have to offer
Sorry, no definitive result
i have made the require changes but this was showing the error i have posted
Hi – as the output says “no definitive result” and this is covered by both the message and the last line of this article…”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 other words the tlslite library only supports a handful of simple cipher suites and if the server you’re testing doesn’t support any of them, the connection fails. You can check this by comparing the cipher suites offered in the Client Hello issued by the tool with those known to be supported by the server.