j3iiifn’s blog

ネットワーク、インフラ、プログラミングについての備忘録

Ubuntu 18.04にしたらcurlで"sslv3 alert handshake failure"エラーが出るようになった

環境

事象

とあるダイナミックDNSサービスを長年愛用している。 自宅のIPアドレスが変更されたらcurlコマンドを叩き、そのダイナミックDNSサービスに通知する仕組みにしていた。 ところが、Ubuntu 18.04に上げてから error:14094410:SSL routines:ssl3_read_bytes:sslv3 alert handshake failurecurlコマンドが失敗するようになった。

$ curl -v -s -o /dev/null https://XXXXXX.net/
*   Trying XX.XX.XX.XX...
* TCP_NODELAY set
* Connected to XXXXXX.net (XX.XX.XX.XX) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*   CAfile: /etc/ssl/certs/ca-certificates.crt
  CApath: /etc/ssl/certs
} [5 bytes data]
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
} [210 bytes data]
* TLSv1.2 (IN), TLS alert, Server hello (2):
{ [2 bytes data]
* error:14094410:SSL routines:ssl3_read_bytes:sslv3 alert handshake failure
* stopped the pause stream!
* Closing connection 0

curlとOpenSSLのバージョン情報

$ curl --version
curl 7.58.0 (arm-unknown-linux-gnueabihf) libcurl/7.58.0 OpenSSL/1.1.0g zlib/1.2.11 libidn2/2.0.4 libpsl/0.19.1 (+libidn2/2.0.4) nghttp2/1.30.0 librtmp/2.3
Release-Date: 2018-01-24
Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtmp rtsp smb smbs smtp smtps telnet tftp
Features: AsynchDNS IDN IPv6 Largefile GSS-API Kerberos SPNEGO NTLM NTLM_WB SSL libz TLS-SRP HTTP2 UnixSockets HTTPS-proxy PSL

$ openssl version
OpenSSL 1.1.0g  2 Nov 2017

原因

Webサーバ側が対応しているCipher Suiteが古くてセキュアではないものだった。 Ubuntu 18.04のcurl/OpenSSLでは、それらのCipher Suiteが非対応になっていた。

解決策

Webクライアント側でできることは何もない。 Ubuntu 18.04のcurl/OpenSSLで利用可能なCipher Suiteの中に、目的のWebサーバが対応しているものはなかったから。 SSLv3や脆弱な暗号化方式を有効にしてOpenSSLをコンパイルし直す手もあるが、セキュリティ的に良くないし、そこまで頑張る必要性もない。

だから、別のダイナミックDNSサービスに乗り換えよう…

原因究明の過程

いろいろなOSで試してみた

次の3通りのOS/バージョンで試してみたところ、すべて接続可能だった。

  • Ubuntu 16.04.5 / curl 7.47.0 / OpenSSL 1.0.2g
    • SSL connection using TLS1.0 / RSA_3DES_EDE_CBC_SHA1
  • macOS Sierra 10.12.6 / curl 7.54.0 / OpenSSL 0.9.8zh
    • TLS 1.0 connection using TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA
  • macOS Mojave 10.14.4 / curl 7.54.0 / LibreSSL 2.6.5
    • SSL connection using TLSv1.0 / EDH-RSA-DES-CBC3-SHA

Ubuntu 16.04.5 / curl 7.47.0 / OpenSSL 1.0.2g

$ curl -v -s -o /dev/null https://XXXXXX.net/
*   Trying XX.XX.XX.XX...
* Connected to XXXXXX.net (XX.XX.XX.XX) port 443 (#0)
* found 148 certificates in /etc/ssl/certs/ca-certificates.crt
* found 592 certificates in /etc/ssl/certs
* ALPN, offering http/1.1
* SSL connection using TLS1.0 / RSA_3DES_EDE_CBC_SHA1
*    server certificate verification OK
*    server certificate status verification SKIPPED
*    common name: XXXXXX.net (matched)
*    server certificate expiration date OK
*    server certificate activation date OK
*    certificate public key: RSA
*    certificate version: #3
*    subject: CN=XXXXXX.net
*    start date: Fri, 21 Dec 2018 00:00:00 GMT
*    expire date: Thu, 18 Feb 2021 12:00:00 GMT
*    issuer: C=US,O=DigiCert Inc,OU=www.digicert.com,CN=RapidSSL RSA CA 2018
*    compression: NULL
* ALPN, server did not agree to a protocol
> GET / HTTP/1.1
> Host: XXXXXX.net
> User-Agent: curl/7.47.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Fri, 03 May 2019 06:15:29 GMT
< Server: Apache
< Transfer-Encoding: chunked
< Content-Type: text/html
<
{ [6 bytes data]
* Connection #0 to host XXXXXX.net left intact

$ curl --version
curl 7.47.0 (x86_64-pc-linux-gnu) libcurl/7.47.0 GnuTLS/3.4.10 zlib/1.2.8 libidn/1.32 librtmp/2.3
Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtmp rtsp smb smbs smtp smtps telnet tftp
Features: AsynchDNS IDN IPv6 Largefile GSS-API Kerberos SPNEGO NTLM NTLM_WB SSL libz TLS-SRP UnixSockets

$ openssl version
OpenSSL 1.0.2g  1 Mar 2016

macOS Sierra 10.12.6 / curl 7.54.0 / OpenSSL 0.9.8zh

$ curl -v -s -o /dev/null https://XXXXXX.net
* Rebuilt URL to: https://XXXXXX.net/
*   Trying XX.XX.XX.XX...
* TCP_NODELAY set
* Connected to XXXXXX.net (XX.XX.XX.XX) port 443 (#0)
* TLS 1.0 connection using TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA
* Server certificate: XXXXXX.net
* Server certificate: RapidSSL RSA CA 2018
* Server certificate: DigiCert Global Root CA
> GET / HTTP/1.1
> Host: XXXXXX.net
> User-Agent: curl/7.54.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Fri, 03 May 2019 05:40:26 GMT
< Server: Apache
< Transfer-Encoding: chunked
< Content-Type: text/html
<
{ [6 bytes data]
* Connection #0 to host XXXXXX.net left intact

$ curl --version
curl 7.54.0 (x86_64-apple-darwin16.0) libcurl/7.54.0 SecureTransport zlib/1.2.8
Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtsp smb smbs smtp smtps telnet tftp
Features: AsynchDNS IPv6 Largefile GSS-API Kerberos SPNEGO NTLM NTLM_WB SSL libz UnixSockets

$ openssl version
OpenSSL 0.9.8zh 14 Jan 2016

macOS Mojave 10.14.4 / curl 7.54.0 / LibreSSL 2.6.5

$ curl -v -s -o /dev/null https://XXXXXX.net/
*   Trying XX.XX.XX.XX...
* TCP_NODELAY set
* Connected to XXXXXX.net (XX.XX.XX.XX) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* Cipher selection: ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH
* successfully set certificate verify locations:
*   CAfile: /etc/ssl/cert.pem
  CApath: none
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
} [218 bytes data]
* TLSv1.2 (IN), TLS handshake, Server hello (2):
{ [74 bytes data]
* TLSv1.0 (IN), TLS handshake, Certificate (11):
{ [3917 bytes data]
* TLSv1.0 (IN), TLS handshake, Server key exchange (12):
{ [525 bytes data]
* TLSv1.0 (IN), TLS handshake, Server finished (14):
{ [4 bytes data]
* TLSv1.0 (OUT), TLS handshake, Client key exchange (16):
} [134 bytes data]
* TLSv1.0 (OUT), TLS change cipher, Client hello (1):
} [1 bytes data]
* TLSv1.0 (OUT), TLS handshake, Finished (20):
} [16 bytes data]
* TLSv1.0 (IN), TLS change cipher, Client hello (1):
{ [1 bytes data]
* TLSv1.0 (IN), TLS handshake, Finished (20):
{ [16 bytes data]
* SSL connection using TLSv1.0 / EDH-RSA-DES-CBC3-SHA
* ALPN, server did not agree to a protocol
* Server certificate:
*  subject: CN=XXXXXX.net
*  start date: Dec 21 00:00:00 2018 GMT
*  expire date: Feb 18 12:00:00 2021 GMT
*  subjectAltName: host "XXXXXX.net" matched cert's "XXXXXX.net"
*  issuer: C=US; O=DigiCert Inc; OU=www.digicert.com; CN=RapidSSL RSA CA 2018
*  SSL certificate verify ok.
> GET / HTTP/1.1
> Host: XXXXXX.net
> User-Agent: curl/7.54.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Fri, 03 May 2019 05:41:36 GMT
< Server: Apache
< Transfer-Encoding: chunked
< Content-Type: text/html
<
{ [6 bytes data]
* Connection #0 to host XXXXXX.net left intact

$ curl --version
curl 7.54.0 (x86_64-apple-darwin18.0) libcurl/7.54.0 LibreSSL/2.6.5 zlib/1.2.11 nghttp2/1.24.1
Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtsp smb smbs smtp smtps telnet tftp
Features: AsynchDNS IPv6 Largefile GSS-API Kerberos SPNEGO NTLM NTLM_WB SSL libz HTTP2 UnixSockets HTTPS-proxy

$ openssl version
LibreSSL 2.6.5

SSL/TLSバージョンを指定してみた

TLS 1.0

オプションを付けてTLS 1.0を強制してみたが、結果は変わらず error:14094410:SSL routines:ssl3_read_bytes:sslv3 alert handshake failure が出てしまった。

$ curl --tlsv1.0 -v -s -o /dev/null https://XXXXXX.net/
*   Trying XX.XX.XX.XX...
* TCP_NODELAY set
* Connected to XXXXXX.net (XX.XX.XX.XX) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*   CAfile: /etc/ssl/certs/ca-certificates.crt
  CApath: /etc/ssl/certs
} [5 bytes data]
* TLSv1.0 (OUT), TLS handshake, Client hello (1):
} [136 bytes data]
* TLSv1.0 (IN), TLS alert, Server hello (2):
{ [2 bytes data]
* error:14094410:SSL routines:ssl3_read_bytes:sslv3 alert handshake failure
* stopped the pause stream!
* Closing connection 0

SSLv3

SSLv3はcurlコンパイル時に無効にされている模様。

$ curl --sslv3 -v -s -o /dev/null https://XXXXXX.net/
*   Trying XX.XX.XX.XX...
* TCP_NODELAY set
* Connected to XXXXXX.net (XX.XX.XX.XX) port 443 (#0)
* OpenSSL was built without SSLv3 support
* Closing connection 0

パケットキャプチャを見てみた

Ubuntu 18.04.2 / curl 7.58.0 / OpenSSL 1.1.0g

特にオプションを付けずに叩いたとき curl -v -s -o /dev/null https://XXXXXX.net

Client Helloの後、Webサーバから Handshake Failure のAlertが返ってきていた。

f:id:j3iiifn:20190503192133p:plain

Client HelloではTLS 1.2と28個のCipher Suiteを提示していた。

f:id:j3iiifn:20190503192131p:plain

TLSv1.0を強制して叩いたとき curl --tlsv1.0 -v -s -o /dev/null https://XXXXXX.net/

Client Helloの後、Webサーバから Handshake Failure のAlertが返ってきたのは同じ。

Client HelloではTLS 1.0と9個のCipher Suiteを提示していた。

f:id:j3iiifn:20190503192120p:plain

Ubuntu 16.04.5 / curl 7.47.0 / OpenSSL 1.0.2g

特にオプションを付けずに curl -v -s -o /dev/null https://XXXXXX.net を実行したときのパケットキャプチャを見る。

Client Helloの後、ちゃんとServer Helloが返ってきていた。

f:id:j3iiifn:20190503192124p:plain

Client HelloではTLS 1.2と54個のCipher Suiteを提示していた。Ubuntu 18.04よりも多い!

f:id:j3iiifn:20190503192137p:plain

Server HelloではTLS 1.0とCipher Suite TLS_RSA_WITH_3DES_EDE_CBC_SHA が指定されていた。

f:id:j3iiifn:20190503192128p:plain

OpenSSL 1.1.0g が対応しているCipher Suiteの一覧

Ubuntu 18.04.2のOpenSSL 1.1.0gが対応しているCipher Suiteは全部で57個ある。 Ubuntu 16.04.5のOpenSSL 1.0.2gでは97個だったので、だいぶ減った模様。

Ubuntu 16.04.5とmacOS Sierraで選択された SRP-RSA-3DES-EDE-CBC-SHA と、 macOS Mojaveで選択された EDH-RSA-DES-CBC3-SHA はこのリストの中にはない。

$ openssl ciphers -v
ECDHE-ECDSA-AES256-GCM-SHA384 TLSv1.2 Kx=ECDH     Au=ECDSA Enc=AESGCM(256) Mac=AEAD
ECDHE-RSA-AES256-GCM-SHA384 TLSv1.2 Kx=ECDH     Au=RSA  Enc=AESGCM(256) Mac=AEAD
DHE-RSA-AES256-GCM-SHA384 TLSv1.2 Kx=DH       Au=RSA  Enc=AESGCM(256) Mac=AEAD
ECDHE-ECDSA-CHACHA20-POLY1305 TLSv1.2 Kx=ECDH     Au=ECDSA Enc=CHACHA20/POLY1305(256) Mac=AEAD
ECDHE-RSA-CHACHA20-POLY1305 TLSv1.2 Kx=ECDH     Au=RSA  Enc=CHACHA20/POLY1305(256) Mac=AEAD
DHE-RSA-CHACHA20-POLY1305 TLSv1.2 Kx=DH       Au=RSA  Enc=CHACHA20/POLY1305(256) Mac=AEAD
ECDHE-ECDSA-AES128-GCM-SHA256 TLSv1.2 Kx=ECDH     Au=ECDSA Enc=AESGCM(128) Mac=AEAD
ECDHE-RSA-AES128-GCM-SHA256 TLSv1.2 Kx=ECDH     Au=RSA  Enc=AESGCM(128) Mac=AEAD
DHE-RSA-AES128-GCM-SHA256 TLSv1.2 Kx=DH       Au=RSA  Enc=AESGCM(128) Mac=AEAD
ECDHE-ECDSA-AES256-SHA384 TLSv1.2 Kx=ECDH     Au=ECDSA Enc=AES(256)  Mac=SHA384
ECDHE-RSA-AES256-SHA384 TLSv1.2 Kx=ECDH     Au=RSA  Enc=AES(256)  Mac=SHA384
DHE-RSA-AES256-SHA256   TLSv1.2 Kx=DH       Au=RSA  Enc=AES(256)  Mac=SHA256
ECDHE-ECDSA-AES128-SHA256 TLSv1.2 Kx=ECDH     Au=ECDSA Enc=AES(128)  Mac=SHA256
ECDHE-RSA-AES128-SHA256 TLSv1.2 Kx=ECDH     Au=RSA  Enc=AES(128)  Mac=SHA256
DHE-RSA-AES128-SHA256   TLSv1.2 Kx=DH       Au=RSA  Enc=AES(128)  Mac=SHA256
ECDHE-ECDSA-AES256-SHA  TLSv1 Kx=ECDH     Au=ECDSA Enc=AES(256)  Mac=SHA1
ECDHE-RSA-AES256-SHA    TLSv1 Kx=ECDH     Au=RSA  Enc=AES(256)  Mac=SHA1
DHE-RSA-AES256-SHA      SSLv3 Kx=DH       Au=RSA  Enc=AES(256)  Mac=SHA1
ECDHE-ECDSA-AES128-SHA  TLSv1 Kx=ECDH     Au=ECDSA Enc=AES(128)  Mac=SHA1
ECDHE-RSA-AES128-SHA    TLSv1 Kx=ECDH     Au=RSA  Enc=AES(128)  Mac=SHA1
DHE-RSA-AES128-SHA      SSLv3 Kx=DH       Au=RSA  Enc=AES(128)  Mac=SHA1
RSA-PSK-AES256-GCM-SHA384 TLSv1.2 Kx=RSAPSK   Au=RSA  Enc=AESGCM(256) Mac=AEAD
DHE-PSK-AES256-GCM-SHA384 TLSv1.2 Kx=DHEPSK   Au=PSK  Enc=AESGCM(256) Mac=AEAD
RSA-PSK-CHACHA20-POLY1305 TLSv1.2 Kx=RSAPSK   Au=RSA  Enc=CHACHA20/POLY1305(256) Mac=AEAD
DHE-PSK-CHACHA20-POLY1305 TLSv1.2 Kx=DHEPSK   Au=PSK  Enc=CHACHA20/POLY1305(256) Mac=AEAD
ECDHE-PSK-CHACHA20-POLY1305 TLSv1.2 Kx=ECDHEPSK Au=PSK  Enc=CHACHA20/POLY1305(256) Mac=AEAD
AES256-GCM-SHA384       TLSv1.2 Kx=RSA      Au=RSA  Enc=AESGCM(256) Mac=AEAD
PSK-AES256-GCM-SHA384   TLSv1.2 Kx=PSK      Au=PSK  Enc=AESGCM(256) Mac=AEAD
PSK-CHACHA20-POLY1305   TLSv1.2 Kx=PSK      Au=PSK  Enc=CHACHA20/POLY1305(256) Mac=AEAD
RSA-PSK-AES128-GCM-SHA256 TLSv1.2 Kx=RSAPSK   Au=RSA  Enc=AESGCM(128) Mac=AEAD
DHE-PSK-AES128-GCM-SHA256 TLSv1.2 Kx=DHEPSK   Au=PSK  Enc=AESGCM(128) Mac=AEAD
AES128-GCM-SHA256       TLSv1.2 Kx=RSA      Au=RSA  Enc=AESGCM(128) Mac=AEAD
PSK-AES128-GCM-SHA256   TLSv1.2 Kx=PSK      Au=PSK  Enc=AESGCM(128) Mac=AEAD
AES256-SHA256           TLSv1.2 Kx=RSA      Au=RSA  Enc=AES(256)  Mac=SHA256
AES128-SHA256           TLSv1.2 Kx=RSA      Au=RSA  Enc=AES(128)  Mac=SHA256
ECDHE-PSK-AES256-CBC-SHA384 TLSv1 Kx=ECDHEPSK Au=PSK  Enc=AES(256)  Mac=SHA384
ECDHE-PSK-AES256-CBC-SHA TLSv1 Kx=ECDHEPSK Au=PSK  Enc=AES(256)  Mac=SHA1
SRP-RSA-AES-256-CBC-SHA SSLv3 Kx=SRP      Au=RSA  Enc=AES(256)  Mac=SHA1
SRP-AES-256-CBC-SHA     SSLv3 Kx=SRP      Au=SRP  Enc=AES(256)  Mac=SHA1
RSA-PSK-AES256-CBC-SHA384 TLSv1 Kx=RSAPSK   Au=RSA  Enc=AES(256)  Mac=SHA384
DHE-PSK-AES256-CBC-SHA384 TLSv1 Kx=DHEPSK   Au=PSK  Enc=AES(256)  Mac=SHA384
RSA-PSK-AES256-CBC-SHA  SSLv3 Kx=RSAPSK   Au=RSA  Enc=AES(256)  Mac=SHA1
DHE-PSK-AES256-CBC-SHA  SSLv3 Kx=DHEPSK   Au=PSK  Enc=AES(256)  Mac=SHA1
AES256-SHA              SSLv3 Kx=RSA      Au=RSA  Enc=AES(256)  Mac=SHA1
PSK-AES256-CBC-SHA384   TLSv1 Kx=PSK      Au=PSK  Enc=AES(256)  Mac=SHA384
PSK-AES256-CBC-SHA      SSLv3 Kx=PSK      Au=PSK  Enc=AES(256)  Mac=SHA1
ECDHE-PSK-AES128-CBC-SHA256 TLSv1 Kx=ECDHEPSK Au=PSK  Enc=AES(128)  Mac=SHA256
ECDHE-PSK-AES128-CBC-SHA TLSv1 Kx=ECDHEPSK Au=PSK  Enc=AES(128)  Mac=SHA1
SRP-RSA-AES-128-CBC-SHA SSLv3 Kx=SRP      Au=RSA  Enc=AES(128)  Mac=SHA1
SRP-AES-128-CBC-SHA     SSLv3 Kx=SRP      Au=SRP  Enc=AES(128)  Mac=SHA1
RSA-PSK-AES128-CBC-SHA256 TLSv1 Kx=RSAPSK   Au=RSA  Enc=AES(128)  Mac=SHA256
DHE-PSK-AES128-CBC-SHA256 TLSv1 Kx=DHEPSK   Au=PSK  Enc=AES(128)  Mac=SHA256
RSA-PSK-AES128-CBC-SHA  SSLv3 Kx=RSAPSK   Au=RSA  Enc=AES(128)  Mac=SHA1
DHE-PSK-AES128-CBC-SHA  SSLv3 Kx=DHEPSK   Au=PSK  Enc=AES(128)  Mac=SHA1
AES128-SHA              SSLv3 Kx=RSA      Au=RSA  Enc=AES(128)  Mac=SHA1
PSK-AES128-CBC-SHA256   TLSv1 Kx=PSK      Au=PSK  Enc=AES(128)  Mac=SHA256
PSK-AES128-CBC-SHA      SSLv3 Kx=PSK      Au=PSK  Enc=AES(128)  Mac=SHA1

SRP-RSA-3DES-EDE-CBC-SHAEDH-RSA-DES-CBC3-SHA について調べてみた

共通鍵暗号方式の一種であるDESと、その後継の3DES (Triple DES)にはSweet32という脆弱性があるらしい。

TLSSSHIPSec およびその他プロトコルの製品で使用される DES および Triple DES 暗号は、約 40 億ブロックの birthday bound を持っているため、平文のデータを取得される脆弱性が存在します。

脆弱性は、"Sweet32" 攻撃と呼ばれています。

JVNDB-2016-004511 - JVN iPedia - 脆弱性対策情報データベース より引用

そして、3DES (DES-CBC3) はOpenSSL 1.1.0からデフォルトでコンパイルされなくなったようだ。

For the 1.1.0 release, which we expect to release tomorrow, we will treat triple-DES just like we are treating RC4. It is not compiled by default; you have to use “enable-weak-ssl-ciphers” as a config option. Even when those ciphers are compiled, triple-DES is only in the “MEDIUM” keyword. In addition, because this is a new release, we also removed it from the “DEFAULT” keyword.

The SWEET32 Issue, CVE-2016-2183 - OpenSSL Blog より引用

だからOpenSSL 1.1.0を使っているUbuntu 18.04ではこれらのCipher Suiteが利用できない。