It’s often important to know which SSL/TLS cipher suite is preferred by a server to alter the risk rating of a particular issue. For POODLE, if the server prefers RC4 ciphers over SSLv3 connections then it’s very unlikely that a connection will be vulnerable to POODLE. Similarly, if a server prefers block ciphers then reporting RC4 support should be appropriately adjusted. Occasionally tools conflict over which cipher suite is preferred so I thought I’d write up how to resolve this manaully in the spirit of the SSL/TLS manual cheatsheet.
How is a cipher suite chosen?
Quick overview: the connection starts with a Client Hello in which the client advertises which cipher suites it supports in order of preference (most preferred first). This list will be tailored according to any local configuration, as well as to the SSL/TLS protocol version the client is hoping to use, which is also advertised in the Client Hello:
The protocol version is the highest the client supports – unless the browser has gone down the fallback route, which is the mechanism abused by POODLE to make the SSLv3 attack more practical. Cipher suites can vary with protocol version simply because older protocols can’t always meet the needs of newer cipher suites. For example, only TLSv1.2 supports cipher suites that use SHA-256 for message integrity.
In receipt of the Client Hello, the server now has two options: it can either (a) opt for the client’s most preferred cipher suite that it too supports, or (b) ignore the client’s preference and opt for the cipher suite nearest the top of its own list that the client supports. For example, say the client has sent up a list of cipher suites which we’ll just call 1,2,3,4,5,6,7 and the server supports 8,3,4,2,6. In the case of (a) the server’s order is unimportant and it chooses 2; in the case of (b) the server chooses 3. The choice the server makes is returned in the Server Hello message:
Something to note in the above example is that, in the case of the server having a preference, you would never find out that cipher suite 8 is in fact the preferred choice because it isn’t supported by the client and thus it’s never offered in the Client Hello. Server preference is thus not only dictated by the server: it depends on what the client knows too.
On my last test I had a conflict between SSLyze and SSLscan over which cipher suite was preferred over SSLv3. SSLyze thought it was RC4-SHA (I’m using the OpenSSL notation here)…
…whereas SSLscan went for DES-CBC3-SHA:
Manually testing for preference
To resolve this was simple. I ran:
openssl s_client -ssl3 -connect <host>:443
OpenSSL reported that DES-CBC3-SHA had been chosen. Just to be sure – which I explain below – I let the two cipher suites in question compete with one another using the
-cipher switch, which allows you to put specific cipher suites in the Client Hello. OpenSSL orders them exactly how you list them according to the scheme set out in
man ciphers. So I ran:
openssl s_client -ssl3 -cipher DES-CBC3-SHA:RC4-SHA -connect <host>:443
and then switched the order of the cipher suites:
openssl s_client -ssl3 -cipher RC4-SHA:DES-CBC3-SHA -connect <host>:443.
In both cases DES-CBC3-SHA was chosen so I was confident that SSLscan was right.
Why did SSLyze get it wrong this time?
Up to now I had tried SSLyze versions 0.9 and 1.0dev and both had reported RC4-SHA as the preferred cipher suite. I then tried an earlier 0.6beta version and found it correctly reported DES-CBC3-SHA. Rather than delve into the code I first took the easy option and fired up Wireshark while running one of the later versions of SSLyze. When enumerating the supported cipher suites, I could see that DES-CBC3-SHA was tested individually; however, when it came to checking for preference, DES-CBC3-SHA was left out of the list in the Client Hello. Obviously the server couldn’t choose it in this case, hence the preference was misreported. I reported this as a bug and Alban Diquet explained that:
The reason why DES-CBC3-SHA isn’t sent within the preference test is that specific servers will not reply at all if the client hello is larger than 255 bytes (due to a bug in a specific brand of load balancers). To reduce the size of the hello, I had to disable some stuff including specific cipher suites.
In this case the server only supported 3 cipher suites over SSLv3 so this misidentification could have been avoided. And this got me thinking…
Algorithm for testing preference
For each supported SSL/TLS protocol version, this is my version 0.1 of a method a tool could use to work out cipher suite preference:
- Determine which cipher suites are supported individually (i.e. repeatedly send a Client Hello with just one cipher suite and see if it’s accepted).
- Once you know which suites are supported, send them all up in one Client Hello and see which one is picked. If you’re worried about the buggy load balancers mentioned above then use a subset for now.
- If the chosen cipher suite is the one that was at the top of the list then there are two alternative explanations: either (a) the server picked the client’s preferred suite as it has no preference of its own, or (b) the server really does prefer that cipher suite and it just happened to be at the top. (This is why I ran more than one test above.) So repeat the test in step 2, this time changing the most preferred cipher suite at the top of the order. If the same cipher suite is chosen then it’s a case of (b) and the server definitely has a preference; otherwise, the first cipher suite should be chosen and it’s case of (a) where the server is happy to be guided by the client’s preference1.
- If the cipher suite list has been cut short to appease buggy load balancers, repeat step 2 with the next set of cipher suites. If a preference has been expressed so far, that cipher suite should be included with the next set to allow it to compete.
- If a preference has been found and you really wanted to go the whole hog, you could determine the order in full by starting again at step 2, missing out the cipher suite previously identified as preferred.
I put some of this to Alban Diquet (as part of the bug report) and he replied “yes I thought of adding the exact check you described but that’s pretty much at the bottom of my TODO list”. I think he was referring to step 3 but, anyway, if you ever have conflicting output from your tools over cipher suite preference, hopefully this posting will help you to resolve the issue.
1 Actually, there is a possibility of “no preference” being misreported if you consider a server that supports cipher suite “wildcards” in such as way that there is no preference within a set of cipher suites that match a wildcard. I don’t think any popular implementation features this (hence the footnote) but for the sake of completeness imagine a server that prefers AES-* then RC4-*. The test tool sends up AES-SHA, AES-MD5, RC4-SHA, RC4-MD5 and AES-SHA is chosen. As per step 3, the tool then sends up AES-MD5, RC4-SHA, RC4-MD5, AES-SHA. This time AES-MD5 is chosen, giving the illusion of no server preference but in fact the server does have a preference, it’s just by groups. To cover this, if no server preference has been detected after step 3, repeat step 2 rotating the cipher suite at the top of the list each time; if at any point the cipher suite selected is not the first on the list then the server does have a preference. Admittedly this could add a fair bit of overhead!