0

using OPENSSL_VERSION : OpenSSL 1.1.0j and trying to connect to a host that seem to only support TLS 1.0 cyphers and getting an error in _sslobj.do_handshake().

import OpenSSL
import requests
from urllib.request import urlopen
import ssl
...

url = 'https://slpin.universalservice.org/'
urlopen(url).read()

1 Answers1

1

As you can see from this SSLLabs report the server you are trying to access is terrible broken. It gets a grade of F (worst) which is mainly due to its terrible insecure ciphers:

enter image description here

The only not terrible insecure but only weak cipher uses 3DES. Because of this weakness this cipher is likely not included in the openssl build on your platform (for example Debian and Debian based systems like Ubuntu don't have this cipher included).

This means the only way to access the server from your Python script would be to use a version of Python linked to an older version of OpenSSL or linked to a modern version but with this cipher explicitly included. Even then you would likely need to specifically enable 3DES since this is disabled by urllib for a while. Thus, when Python is build with an OpenSSL which has 3DES support included the following should work:

import ssl
from urllib.request import urlopen

url = 'https://slpin.universalservice.org/'
ctx = ssl.create_default_context()
ctx.set_ciphers('3DES')
urlopen(url, context = ctx).read()

In my case this gives a 403 Forbidden which matches what I get when I visit this URL with the browser.

Steffen Ullrich
  • 114,247
  • 10
  • 131
  • 172
  • Thanks for your reply. I saw that the host is not configured well, only supporting insecure ciphers. Instead of 403 Forbidden error, I get ssl.SSLError: ('No cipher can be selected.',) when I try to run it your way. Would you be able to try with url = 'https://slpin.universalservice.org/DRT/' - that should get you an HTML page back. – MchaelTocho Apr 18 '19 at 20:15
  • @MchaelTocho: correct, that gives me HTML. But to repeat: there are actually two issues. The first is that newer builds of OpenSSL has no support for 3DES build in, which means it can also not be enabled. Instead you'll need a Python build against an OpenSSL library which has 3DES support. Only then is the second problem relevant: that urllib by default has 3DES enabled. This is fixed by the code I've included. But the code will not make 3DES magically be available if the underlying OpenSSL library does not support it (i.e. the first problem). – Steffen Ullrich Apr 18 '19 at 20:38
  • @MchaelTocho: *"ssl.SSLError: ('No cipher can be selected.',) "* means that the OpenSSL linked with your Python has no support for 3DES. In this case the script will not help. The script can only enable what is disabled, it cannot magically make 3DES support occur when it is not compiled in. – Steffen Ullrich Apr 18 '19 at 20:52
  • Steffen, Would you happen to know if there is a way to perhaps install the older version of OpenSSL that still had support for 3DES? Or is there a different library to use instead that deals with the establishing the SSL handshake using ciphers that the host we're dealing with accepts? – MchaelTocho Apr 18 '19 at 21:18
  • urllib didn't need to change; it leaves in place the libssl default cipherlist, which changed in OpenSSL 1.1.0 up to exclude all 64-bit (1DES, 3DES, IDEA) (although some?/many? builds already excluded IDEA because non-free), and also RC4 SEED Camellia AESCCM and DSS(!). @MchaelTocho: worst case if you can't get requests/urllib to work, try running an external curl or wget or similar and capturing the data from it. – dave_thompson_085 Apr 19 '19 at 00:41
  • @dave_thompson_085: `urllib.request` uses by default `ssl.create_default_context` which uses by default the cipher set in [_ssl.c](https://github.com/python/cpython/blob/master/Modules/_ssl.c#L283) which explicitly excludes 3DES. Thus, even if the underlying openssl has still 3DES builtin and enabled Python has it explicitly disabled by default. The code in my answer explicitly overrides this default. – Steffen Ullrich Apr 19 '19 at 05:00
  • @MchaelTocho: Usually OpenSSL 1.0.2 is build with 3DES included (but disabled in the default cipher settings). Newer OpenSSL versions can be build with 3DES enabled too, but this is not the default. Still, it is not enough to just install a different OpenSSL version but Python must also use it (i.e. built against it). You can try to install an older version of [Anaconda Python](https://repo.anaconda.com/archive/) parallel to the system Python - I think 5.1.0 might still work. – Steffen Ullrich Apr 19 '19 at 05:06
  • Darn -- I had a memory of checking this but I must have mixed it up with something else. Sorry. – dave_thompson_085 Apr 19 '19 at 21:10
  • Depending on your use case, you could for example route the traffic through a proxy, e.g. Burp. Unlike Python ( as mentioned it is linked to a specific OpenSSL version) Burp uses Java's native javax.net.ssl. In some situation, you might even want to put OWASP Zap Proxy in between since it uses BouncyCastle, a library that provides greater support for SSL/TLS implementations than Java's native javax.net.ssl. Here's additional information on how to make your Python script proxy aware https://www.th3r3p0.com/random/python-requests-and-burp-suite.html 1/2 – cyzczy Apr 13 '21 at 12:58
  • By using one the above tools, or both depending on your use case, you are no longer limited by the OpenSSL package version restriction in regards to SSL/TLS protocol version and cipher suites. I'm using this often in pentesting activities, where the target supports protocol versions / ciphers no longer supported in the OpenSSL package installed on my machines. 2/2 – cyzczy Apr 13 '21 at 12:59