nmap ssl-enum-ciphers / haproxy / tls / no results
When doing an nmap ssl-enum-ciphers scan on a Haproxy machine, I got zero working ciphers. But connecting with TLS worked just fine. What is up?
While updating TLS ciphers to disable certain unsafe ciphers, we wanted to test the current offers. nmap with the ssl-enum-ciphers script should work fine for this, and it is reasonably fast:
$ nmap -Pn --script=ssl-enum-ciphers -p 443 google.com
...
443/tcp open https
| ssl-enum-ciphers:
| TLSv1.0:
...
| TLSv1.1:
...
| TLSv1.2:
| ciphers:
| TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA (ecdh_x25519) - A
...
| warnings:
| 64-bit block cipher 3DES vulnerable to SWEET32 attack
|_ least strength: C
Nmap done: 1 IP address (1 host up) scanned in 1.27 seconds
Okay, that is convenient. On to the relevant host.
On the host, we've configured TLSv1.2 and TLSv1.3 with a limited set of ciphers. Let's go:
$ nmap -Pn --script=ssl-enum-ciphers -p 443 x.x.x.x
...
443/tcp open https
Nothing?!
Apparently the nmap 7.91 scripts on Ubuntu/Jammy are a bit outdated and have no TLSv1.3 support. It was added in nmap commit 7c61f7c.
We can patch our Ubuntu/Jammy nmap with this adapted patch:
nmap-ssl-enum-ciphers-tls13-for-ubuntu-jammy.patch
$ cd /usr/share/nmap
$ sudo patch -p1 < /PATH/TO/nmap-ssl-enum-ciphers-tls13-for-ubuntu-jammy.patch
Try again:
$ nmap -Pn --script=ssl-enum-ciphers -p 443 $haproxy
...
443/tcp open https
| ssl-enum-ciphers:
| TLSv1.3:
| ciphers:
| TLS_AKE_WITH_AES_128_GCM_SHA256 (ecdh_x25519) - A
| TLS_AKE_WITH_AES_256_GCM_SHA384 (ecdh_x25519) - A
| TLS_AKE_WITH_CHACHA20_POLY1305_SHA256 (ecdh_x25519) - A
| cipher preference: client
|_ least strength: A
Nice! TLSv1.3 results. But where is the TLSv1.2?
We know that the script supports TLSv1.2, as seen with the google.com example above. In fact, rerunning it now shows that google.com also does TLSv1.3, as you'd expect.
The Haproxy config looks sane:
global
# "server ciphers"
ssl-default-bind-ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305
ssl-default-bind-ciphersuites TLS_AES_256_GCM_SHA384:TLS_AES_128_GCM_SHA256:TLS_CHACHA20_POLY1305_SHA256
ssl-default-bind-options prefer-client-ciphers no-tls-tickets ssl-min-ver TLSv1.2 ssl-max-ver TLSv1.3
And openssl s_client -no_tls1_3 -connect
will happily connect with
TLSv1.2.
If we try a kube-apiserver with the same set of ciphers configured, it does yield both TLSv1.2 and TLSv1.3:
# kube-apiserver [...] --tls-cipher-suites=\
TLS_AES_256_GCM_SHA384,TLS_AES_128_GCM_SHA256,TLS_CHACHA20_POLY1305_SHA256,\
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256
$ nmap -Pn --script=ssl-enum-ciphers -p 443 $kube-apiserver
...
443/tcp open https
| ssl-enum-ciphers:
| TLSv1.2:
| ciphers:
| TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 - unknown
| TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 - unknown
| TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 - unknown
| compressors:
| NULL
| cipher preference: server
| TLSv1.3:
| ciphers:
| TLS_AKE_WITH_CHACHA20_POLY1305_SHA256 (ecdh_x25519) - A
| TLS_AKE_WITH_AES_128_GCM_SHA256 (ecdh_x25519) - A
| TLS_AKE_WITH_AES_256_GCM_SHA384 (ecdh_x25519) - A
| cipher preference: server
|_ least strength: unknown
So, something is different with Haproxy.
The openssl s_client
output did not give many clues, but there was
one:
Client Certificate Types: RSA sign, ECDSA sign
for the
kube-apiserver and
Client Certificate Types: RSA sign, DSA sign, ECDSA sign
for
Haproxy.
And indeed, there is a related fix in nmap commit 034ea73.
Apply the following patch as well:
nmap-ssl-enum-ciphers-tls12-ecdsa-in-ubuntu-jammy.patch
$ cd /usr/share/nmap
$ sudo patch -p1 < /PATH/TO/nmap-ssl-enum-ciphers-tls12-ecdsa-in-ubuntu-jammy.patch
And now a rescan finally shows both TLS protocols on Haproxy as well.
$ nmap -Pn --script=ssl-enum-ciphers -p 443 $haproxy
...
443/tcp open https
| ssl-enum-ciphers:
| TLSv1.2:
| ciphers:
| TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 (secp256r1) - A
| TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 (secp256r1) - A
| TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 (secp256r1) - A
| compressors:
| NULL
| cipher preference: client
| TLSv1.3:
| ciphers:
| TLS_AKE_WITH_AES_128_GCM_SHA256 (ecdh_x25519) - A
| TLS_AKE_WITH_AES_256_GCM_SHA384 (ecdh_x25519) - A
| TLS_AKE_WITH_CHACHA20_POLY1305_SHA256 (ecdh_x25519) - A
| cipher preference: client
|_ least strength: A
(Aha! That “unknown” is now gone too.)