The Small Print for OpenSSL legacy_renegotiation

The other day my attention was drawn to a switch in OpenSSL called -legacy_rengotation. I pulled up the built-in help for s_client and, sure enough, there it was. So I trawled back through the release notes and it looked to have been there since version 0.9.8m. I couldn’t believe that I hadn’t spotted this before: it looked like the perfect way to test for insecure renegotiation without the faff of having to recompile OpenSSL or use an older version. But after a bit of testing this proved to be a red herring…

The first thing I wanted to do was prove the negative – that is, if the -legacy_rengotation switch did what it seemed to promise, then without it renegotiation should fail. Using OpenSSL 1.0.1i I connected to a server that was missing the secure renegotiation patch and ran the test (more information here):

# openssl s_client -connect insecure.example.com:443
...
New, TLSv1/SSLv3, Cipher is DHE-RSA-AES256-SHA
Server public key is 2048 bit
Secure Renegotiation IS NOT supported
Compression: NONE
...
HEAD / HTTP/1.0
R
RENEGOTIATING
depth=2 C = US, O = GeoTrust Inc., CN = GeoTrust Primary Certification Authority
verify error:num=20:unable to get local issuer certificate
verify return:0

It worked. Wait a minute, that shouldn’t have happened! So I tried OpenSSL 1.0.1e and then another vulnerable server – and it always connected. After some digging around I found an article on the OpenSSL site. It stated that:

If the option SSL_OP_LEGACY_SERVER_CONNECT or SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION is set then initial connections and renegotiation between patched OpenSSL clients and unpatched servers succeeds. If neither option is set then initial connections to unpatched servers will fail.

The option SSL_OP_LEGACY_SERVER_CONNECT is currently set by default even though it has security implications: otherwise it would be impossible to connect to unpatched servers (i.e. all of them initially) and this is clearly not acceptable. Renegotiation is permitted because this does not add any additional security issues: during an attack clients do not see any renegotiations anyway.

There was the small print. So as far as s_client is concerned -legacy_renegotiation makes no difference by default because it will renegotiate with insecure servers anyway. To double-check that -legacy_renegotiation and SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION were in fact related, I took a quick look at the source code for s_client.c and the following lines shone out:

else if (strcmp(*argv,"-legacy_renegotiation") == 0)
  off|=SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION;
else if (strcmp(*argv,"-legacy_server_connect") == 0)
  { off|=SSL_OP_LEGACY_SERVER_CONNECT; }
else if (strcmp(*argv,"-no_legacy_server_connect") == 0)
  { clr|=SSL_OP_LEGACY_SERVER_CONNECT; }

As expected, the first line sees -legacy_renegotiation controlling SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION, which we now know has no effect if SSL_OP_LEGACY_SERVER_CONNECT is set. The code also suggests that SSL_OP_LEGACY_SERVER_CONNECT can be controlled with switches, which aren’t listed in the built-in help. Using the switch -no_legacy_server_connect, as the OpenSSL doc states, stops you from connecting to the server at all:

# openssl s_client -no_legacy_server_connect -connect insecure.example.com:443
CONNECTED(00000003)
140264951338664:error:1412F152:SSL routines:SSL_PARSE_SERVERHELLO_TLSEXT:unsafe legacy renegotiation disabled:t1_lib.c:1732:
140264951338664:error:140920E3:SSL routines:SSL3_GET_SERVER_HELLO:parse tlsext:s3_clnt.c:1053:
---
no peer certificate available
---
No client certificate CA names sent
---
SSL handshake has read 63 bytes and written 7 bytes
---
New, (NONE), Cipher is (NONE)

If you skimmed over the OpenSSL quote above, you may now be thinking “why is it so black and white; why can’t I connect to an unpatched server but s_client refuse renegotiation?” As the OpenSSL doc notes – and if you think back to the attack details – the victim client doesn’t actually initiate a renegotiation, it’s all the attacker’s doing. OpenSSL isn’t leaving you vulnerable by letting you renegotiate to unpatched servers, it’s the very act of connecting to them that leaves you exposed. That’s where the -no_legacy_server_connect switch comes in: it gives you the option of terminating connections to unpatched servers if you don’t want to take any risks (and you can understand why they’ve not made that the default). From a pentest viewpoint, ‑legacy_renegotiation should be avoided when testing for insecure renegotiation.

I pinged Ivan Ristic (of SSL Labs fame) about this for a sanity check, since he was nice enough to get in touch following the release of my cheatsheet. (Quick trailer: it turns out that Ivan is planning to release some of the manual testing aspects from his book Bulletproof SSL and TLS as freeware in the near future.) He agreed that -legacy_renegotiation was something of a red herring as far as manual testing using OpenSSL s_client was concerned – and I think that’s now going to make it into the next version of his book!

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>