Summary
A regression was introduced into the ssl application of OTP starting at OTP-25.3.2.8, OTP-26.2, and OTP-27.0, resulting in a server or client verifying the peer when incorrect extended key usage is presented (i.e., a server will verify a client if they have server auth ext key usage and vice versa).
Affected versions
>= OTP-25.3.2.8
>= OTP-26.2
>= OTP-27.0
Fixed in
OTP 27.1.3
OTP 26.2.5.6
OTP 25.3.2.16
Details
The change that introduced this regression was : e7cd7fc#diff-ad2e52bd3adefb3dc79ab09e6124161de02878ff04d0e27c09fa0d548f0e21a7
Per the change, incorrect ext key usage (i.e., the client has ext key usage as server auth, or vice versa) would lead to a silent error and if one were not using a verify_fun with explicit matching such that an unknown
reason would result in the termination of a session attempt, the server would verify the peer (or vice versa).
Such a scenario requires the user of the ssl application to misuse certificates and keys for unintended purposes, such as server to server auth, where by one server acts as client, and the server grants permissions to the client based off the extended key usage.
PoC
Below is a set of scripts to first exhibit how openssl s_server and s_client behave in this situation and then how Erlang SSL behaves. In the case of OpenSSL, the server will disconnect the client with following error :
verify error:unsupported certificate purpose
8544580160:error:1417C086:SSL routines:tls_process_client_certificate:certificate verify failed:ssl/statem/statem_srvr.c:3713:
shutting down SSL
CONNECTION CLOSED
While, Erlang ssl will verify the peer and create the session.
openssl behavior
The following is configuration and scripts to replicate the bug using openssl, you will see that the server disconnects the client :
openssl.cnf
[ca]
default_ca = root_ca
[ root_ca ]
dir = .
certificate = $dir/ca_certificate.pem
database = $dir/index.txt
new_certs_dir = $dir/certs
private_key = $dir/private/ca_private_key.pem
serial = $dir/serial
default_crl_days = 7
default_days = 365
default_md = sha256
policy = root_ca_policy
x509_extensions = certificate_extensions
[ root_ca_policy ]
commonName = supplied
stateOrProvinceName = optional
countryName = optional
emailAddress = optional
organizationName = optional
organizationalUnitName = optional
domainComponent = optional
[ certificate_extensions ]
basicConstraints = CA:false
[ req ]
default_bits = 2048
default_keyfile = ./private/ca_private_key.pem
default_md = sha256
prompt = yes
distinguished_name = root_ca_distinguished_name
x509_extensions = root_ca_extensions
[ root_ca_distinguished_name ]
commonName = elixir-mllp-root-ca
[ root_ca_extensions ]
basicConstraints = CA:true
keyUsage = keyCertSign, cRLSign
[ client_ca_extensions ]
basicConstraints = CA:false
keyUsage = digitalSignature,keyEncipherment
extendedKeyUsage = 1.3.6.1.5.5.7.3.2
[ server_ca_extensions ]
basicConstraints = CA:false
keyUsage = digitalSignature,keyEncipherment
extendedKeyUsage = 1.3.6.1.5.5.7.3.1
cert generation script
#!/bin/bash
cd tls
mkdir root-ca server
cd root-ca
rm index.txt
touch index.txt
echo 01 > serial
mkdir certs private
openssl req -x509 -sha256 -days 1825 -newkey rsa:2048 -config ../openssl.cnf -out ca_certificate.pem -outform PEM -subj /CN=my-root-ca/ -nodes
openssl x509 -in ca_certificate.pem -out ca_certificate.cer -outform DER
cd ../server
openssl genrsa -out private_key.pem 2048
openssl req -new -key private_key.pem -out req.pem -outform PEM \
-subj /CN=localhost/O=server/ -nodes
cd ../root-ca
openssl ca -config ../openssl.cnf -in ../server/req.pem -out \
../server/server_certificate.pem -notext -batch -extensions server_ca_extensions
cd ../../
mkdir -p priv/certs
cp -f tls/root-ca/ca_certificate.pem priv/certs/root_ca.pem
cp -f tls/server/server_certificate.pem priv/certs/server_cert.pem
cp -f tls/server/private_key.pem priv/certs/server_key.pem
Server start up
openssl s_server -debug -CAfile priv/certs/root_ca.pem -verify_return_error -Verify 1 -key priv/certs/server_key.pem -cert priv/certs/server_cert.pem -accept 4433
Client startup
openssl s_client -CAfile priv/certs/root-ca.pem -key priv/certs/server_key.pem -cert priv/certs/server_cert.pem -servername localhost -connect 127.0.0.1:4433
Erlang module
The certs generated to exhibit the openssl behavior should be used with the module below :
-module(ssl_example).
-export([start/0, init_connect/1]).
start() ->
{ok, StartedApps} = application:ensure_all_started(ssl),
{ok, LSock} = ssl:listen(0, mk_opts(listen)),
{ok, {_, LPort}} = ssl:sockname(LSock),
io:fwrite("Listen: port = ~w.~n", [LPort]),
spawn(?MODULE, init_connect, [LPort]),
{ok, ASock} = ssl:transport_accept(LSock),
{ok, SslSocket} = ssl:handshake(ASock),
io:fwrite("Accept: accepted.~n"),
{ok, Cert} = ssl:peercert(SslSocket),
io:fwrite("Accept: peer cert:~n~p~n", [public_key:pkix_decode_cert(Cert, plain)]),
io:fwrite("Accept: sending \"hello\".~n"),
ssl:send(SslSocket, "hello"),
{error, closed} = ssl:recv(SslSocket, 0),
io:fwrite("Accept: detected closed.~n"),
ssl:close(SslSocket),
io:fwrite("Listen: closing and terminating.~n"),
ssl:close(LSock),
lists:foreach(fun application:stop/1, lists:reverse(StartedApps)).
init_connect(LPort) ->
{ok, CSock} = ssl:connect("localhost", LPort, mk_opts(connect)),
io:fwrite("Connect: connected.~n"),
{ok, Cert} = ssl:peercert(CSock),
io:fwrite("Connect: peer cert:~n~p~n", [public_key:pkix_decode_cert(Cert, plain)]),
{ok, Data} = ssl:recv(CSock, 0),
io:fwrite("Connect: got data: ~p~n", [Data]),
io:fwrite("Connect: closing and terminating.~n"),
ssl:close(CSock).
mk_opts(listen) ->
mk_opts("server");
mk_opts(connect) ->
[{server_name_indication, disable}] ++ mk_opts("client");
mk_opts(_Role) ->
Dir = filename:join([code:lib_dir(ssl_example), "priv", "certs"]),
CACert = filename:join([Dir, "root_ca.pem" ]),
public_key:cacerts_load(CACert),
[{active, false},
{log_level, debug},
{verify, verify_peer},
{fail_if_no_peer_cert, true},
{depth, 0},
{cacerts, public_key:cacerts_get()},
{versions, ['tlsv1.3']},
{certs_keys, [#{certfile => filename:join([Dir, "server_cert.pem"]), keyfile => filename:join([Dir, "server_key.pem"])}]}].
Impact
Severity : Moderate
Vulnerability type : CWE-295: Improper Certificate Validation
Affected applications : All applications using >= OTP-25.3.2.8, >= OTP-26.2, and >= OTP-27.0 and potentially misusing certificates with extended key usage to both protect and escalate access on endpoints in a mutual TLS setup.
Summary
A regression was introduced into the ssl application of OTP starting at OTP-25.3.2.8, OTP-26.2, and OTP-27.0, resulting in a server or client verifying the peer when incorrect extended key usage is presented (i.e., a server will verify a client if they have server auth ext key usage and vice versa).
Affected versions
>= OTP-25.3.2.8
>= OTP-26.2
>= OTP-27.0
Fixed in
OTP 27.1.3
OTP 26.2.5.6
OTP 25.3.2.16
Details
The change that introduced this regression was : e7cd7fc#diff-ad2e52bd3adefb3dc79ab09e6124161de02878ff04d0e27c09fa0d548f0e21a7
Per the change, incorrect ext key usage (i.e., the client has ext key usage as server auth, or vice versa) would lead to a silent error and if one were not using a verify_fun with explicit matching such that an
unknown
reason would result in the termination of a session attempt, the server would verify the peer (or vice versa).Such a scenario requires the user of the ssl application to misuse certificates and keys for unintended purposes, such as server to server auth, where by one server acts as client, and the server grants permissions to the client based off the extended key usage.
PoC
Below is a set of scripts to first exhibit how openssl s_server and s_client behave in this situation and then how Erlang SSL behaves. In the case of OpenSSL, the server will disconnect the client with following error :
While, Erlang ssl will verify the peer and create the session.
openssl behavior
The following is configuration and scripts to replicate the bug using openssl, you will see that the server disconnects the client :
openssl.cnf
cert generation script
Server start up
Client startup
Erlang module
The certs generated to exhibit the openssl behavior should be used with the module below :
Impact
Severity : Moderate
Vulnerability type : CWE-295: Improper Certificate Validation
Affected applications : All applications using >= OTP-25.3.2.8, >= OTP-26.2, and >= OTP-27.0 and potentially misusing certificates with extended key usage to both protect and escalate access on endpoints in a mutual TLS setup.