Lucene search

K
hackeroneMingtaoH1:1129529
HistoryMar 17, 2021 - 6:30 p.m.

curl: CVE-2021-22890: TLS 1.3 session ticket proxy host mixup

2021-03-1718:30:33
mingtao
hackerone.com
34

3.7 Low

CVSS3

Attack Vector

NETWORK

Attack Complexity

HIGH

Privileges Required

NONE

User Interaction

NONE

Scope

UNCHANGED

Confidentiality Impact

NONE

Integrity Impact

LOW

Availability Impact

NONE

CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:L/A:N

4.3 Medium

CVSS2

Access Vector

NETWORK

Access Complexity

MEDIUM

Authentication

NONE

Confidentiality Impact

NONE

Integrity Impact

PARTIAL

Availability Impact

NONE

AV:N/AC:M/Au:N/C:N/I:P/A:N

0.003 Low

EPSS

Percentile

61.6%

Summary:

(I don’t think that this can be easily exploitable, but I am submitting it as a security issue for precaution. I am not looking for a bounty.)

Commit 549310e907e82e44c59548351d4c6ac4aaada114 enables session resumption with TLS 1.3. Curl connections maintain two SSL contexts, one for the proxy and one for the destination. However, curl incorrectly stores session tickets issued by an TLS 1.3 HTTPS proxy under the non proxy context.

The issue is that the logic inside Curl_ssl_addsessionid that chooses which context to store the tickets under is incorrect under TLS 1.3.

const bool isProxy = CONNECT_PROXY_SSL();
struct ssl_primary_config * const ssl_config = isProxy ?
  &conn->proxy_ssl_config :
  &conn->ssl_config;
const char *hostname = isProxy ? conn->http_proxy.host.name :
  conn->host.name;
#define CONNECT_PROXY_SSL()\
  (conn->http_proxy.proxytype == CURLPROXY_HTTPS &&\
  !conn->bits.proxy_ssl_connected[sockindex])

One of the major differences between how TLS session tickets are issued between TLS 1.3 and prior versions of TLS is that TLS 1.3 issues session tickets in a post handshake message. What this means in practice is that TLS 1.3 tickets are delivered in the first call to SSL_read(), rather than being issued as part of SSL_connect(). Consequently, CONNECT_PROXY_SSL() will see that the proxy has already been connected (since the call to SSL_connect() to the proxy was completed), so the call to Curl_ssl_addsessionid believes the isProxy is false, and it stores the ticket under the non proxy context.

After the CONNECT call returns successfully, a connection to the original destination will be made through the established TCP tunnel. If the original destination uses https, another TLS handshake will be made. During this TLS handshake, the curl client offers the session ticket of the proxy to the destination.

If the proxy is malicious, at this point it could decide to terminate the TLS handshake to the upstream. Since the proxy has the corresponding session ticket key (it was the entity that issued the ticket, after all), it can complete the client -> destination TLS handshake through a resumption. Normally, this would result in a full man in the middle, as TLS certificates are not exchanged as part of a resumed connection. However, curl already performs some of its own certificate validation outside of OpenSSL in ossl_connect_step3, which largely mitigates this vulnerability.

The certificate validation that curl performs includes steps such as (1) checking if the certificate was self signed and (2) ensuring that the certificate contains a subject that matches the destination. The certificate of the proxy is stored in the SSL_SESSION that was used for resumption, so curl will attempt to perform these validations against the proxy certificate.

Steps To Reproduce:

I’ve attached a reproducer in this report.

  • server_that_fails_on_ticket.c is a simple TLS server (listening on port 12345) that will send an alert if it receives a session resumption attempt. Under normal circumstances, curl should never be sending a ticket when connecting through a proxy, since it has never connected to this destination before. With this bug, you should be able to observe that the server receives a ticket on the first connection regardless.
  • https_proxy.c is a extremely rudimentary implementation of a HTTPS proxy (listening on port 12346), that only uses TLS 1.3. If a special proxy header Mitm: 1 is passed, then the proxy will attempt to terminate the TLS connection itself, acting as a man in the middle.
  • proxy_ca.pem is the CA file that signs the proxy cert, haxx.se.pem
  • haxx.se.pem is the TLS certificate that the proxy uses. Notice that it has the identities: localhost andhaxx.se.

Demonstrating that curl sends the proxy ticket to the original destination.

  1. Run server_that_fails_on_ticket. This will listen on port 12345
  2. Run https_proxy. This will listen on port 12346
  3. Run curl --proxy-cacert proxy_ca.pem -x 'https://localhost:12346' 'https://localhost:12345'
  4. Notice that the curl client receives a TLS alert, and that “Received a TLS 1.3 ticket resumption attempt” is printed on the server.

Demonstrating the very limited MiTM possibility.

  1. Run https_proxy. This will listen on port 12346
  2. Run curl --proxy-cacert proxy_ca.pem --proxy-header 'Mitm: 1' -x 'https://localhost:12346' 'https://haxx.se'
  3. Notice that “MITM” is returned, and no certificate error is thrown.

The MITM is only possible because haxx.se is listed as one of the subjects in the proxy certificate. Curl’s certificate validation passes: (1) the proxy cert is not self signed and (2) the name haxx.se is present in the certificate is “presented” by the original destination.

Impact

In a very specific environment (perhaps a corporate environment where all access to the internet requires going through an HTTPS proxy), an attacker that can issue a trusted proxy certificate may be able to man in the middle connections established with libcurl, even if curl explicitly does not include the proxy CA in the trust store for normal destinations.

3.7 Low

CVSS3

Attack Vector

NETWORK

Attack Complexity

HIGH

Privileges Required

NONE

User Interaction

NONE

Scope

UNCHANGED

Confidentiality Impact

NONE

Integrity Impact

LOW

Availability Impact

NONE

CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:L/A:N

4.3 Medium

CVSS2

Access Vector

NETWORK

Access Complexity

MEDIUM

Authentication

NONE

Confidentiality Impact

NONE

Integrity Impact

PARTIAL

Availability Impact

NONE

AV:N/AC:M/Au:N/C:N/I:P/A:N

0.003 Low

EPSS

Percentile

61.6%