Description
## Summary:
In curl’s OpenSSL backend, `ossl_get_channel_binding` retains a new reference to the server’s X509 certificate via `SSL_get1_peer_certificate` and never releases it. When Negotiate (SPNEGO) over TLS is in use, this path is invoked and leaks one X509 object per trigger. Over many requests in a long‑running libcurl client, this leads to unbounded memory growth and potential denial‑of‑service.
This occurs only with the OpenSSL backend (>= 1.1.0 gate present), when curl/libcurl is built with GSSAPI (`HAVE_GSSAPI`), over HTTPS when a Negotiate challenge is processed.
Notes:
- The Negotiate challenge over HTTPS is sufficient to trigger channel binding retrieval; a functional Kerberos setup is not required for this leak to manifest.
- Each trigger leaks one X509 reference.
## Supporting Material/References:
- Code location showing missing X509_free on all paths:
```5650:5679:curl/lib/vtls/openssl.c
cert = SSL_get1_peer_certificate(octx->ssl);
if(!cert) {
/* No server certificate, don't do channel binding */
return CURLE_OK;
}
if(!OBJ_find_sigid_algs(X509_get_signature_nid(cert), &algo_nid, NULL)) {
failf(data,
"Unable to find digest NID for certificate signature algorithm");
return CURLE_SSL_INVALIDCERTSTATUS;
}
/* https://datatracker.ietf.org/doc/html/rfc5929#section-4.1 */
if(algo_nid == NID_md5 || algo_nid == NID_sha1) {
algo_type = EVP_sha256();
}
else {
algo_type = EVP_get_digestbynid(algo_nid);
if(!algo_type) {
algo_name = OBJ_nid2sn(algo_nid);
failf(data, "Could not find digest algorithm %s (NID %d)",
algo_name ? algo_name : "(null)", algo_nid);
return CURLE_SSL_INVALIDCERTSTATUS;
}
}
if(!X509_digest(cert, algo_type, buf, &length)) {
failf(data, "X509_digest() failed");
return CURLE_SSL_INVALIDCERTSTATUS;
}
```
```5681:5688:curl/lib/vtls/openssl.c
/* Append "tls-server-end-point:" */
if(curlx_dyn_addn(binding, prefix, sizeof(prefix) - 1) != CURLE_OK)
return CURLE_OUT_OF_MEMORY;
/* Append digest */
if(curlx_dyn_addn(binding, buf, length))
return CURLE_OUT_OF_MEMORY;
return CURLE_OK;
Statement on AI usage: AI was used in parts of detection, triage, fix generation and reporting writing in combination with classical tooling and manual human input.
## Impact
## Impact
A remote HTTPS server that advertises `WWW-Authenticate: Negotiate` can cause a libcurl client (built with OpenSSL and GSSAPI) to leak one X509 certificate reference per authentication attempt when TLS channel binding is retrieved.
Over many requests in a long-lived process, this leads to unbounded memory growth and a denial-of-service condition due to resource exhaustion.
The leak occurs because `ossl_get_channel_binding` calls `SSL_get1_peer_certificate` (which increments the X509 reference count) but never calls `X509_free` on any return path—success or error.
### Constraints:
- Requires libcurl built with OpenSSL (>=1.1.0) and GSSAPI support
- Only affects clients using Negotiate authentication over HTTPS
- Linear leak rate (not amplified), but unbounded over time
Severity: **Low** (limited scope, requires specific build configuration and authentication method, but genuine DoS risk for affected deployments)
In curl’s OpenSSL backend, `ossl_get_channel_binding` retains a new reference to the server’s X509 certificate via `SSL_get1_peer_certificate` and never releases it. When Negotiate (SPNEGO) over TLS is in use, this path is invoked and leaks one X509 object per trigger. Over many requests in a long‑running libcurl client, this leads to unbounded memory growth and potential denial‑of‑service.
This occurs only with the OpenSSL backend (>= 1.1.0 gate present), when curl/libcurl is built with GSSAPI (`HAVE_GSSAPI`), over HTTPS when a Negotiate challenge is processed.
Notes:
- The Negotiate challenge over HTTPS is sufficient to trigger channel binding retrieval; a functional Kerberos setup is not required for this leak to manifest.
- Each trigger leaks one X509 reference.
## Supporting Material/References:
- Code location showing missing X509_free on all paths:
```5650:5679:curl/lib/vtls/openssl.c
cert = SSL_get1_peer_certificate(octx->ssl);
if(!cert) {
/* No server certificate, don't do channel binding */
return CURLE_OK;
}
if(!OBJ_find_sigid_algs(X509_get_signature_nid(cert), &algo_nid, NULL)) {
failf(data,
"Unable to find digest NID for certificate signature algorithm");
return CURLE_SSL_INVALIDCERTSTATUS;
}
/* https://datatracker.ietf.org/doc/html/rfc5929#section-4.1 */
if(algo_nid == NID_md5 || algo_nid == NID_sha1) {
algo_type = EVP_sha256();
}
else {
algo_type = EVP_get_digestbynid(algo_nid);
if(!algo_type) {
algo_name = OBJ_nid2sn(algo_nid);
failf(data, "Could not find digest algorithm %s (NID %d)",
algo_name ? algo_name : "(null)", algo_nid);
return CURLE_SSL_INVALIDCERTSTATUS;
}
}
if(!X509_digest(cert, algo_type, buf, &length)) {
failf(data, "X509_digest() failed");
return CURLE_SSL_INVALIDCERTSTATUS;
}
```
```5681:5688:curl/lib/vtls/openssl.c
/* Append "tls-server-end-point:" */
if(curlx_dyn_addn(binding, prefix, sizeof(prefix) - 1) != CURLE_OK)
return CURLE_OUT_OF_MEMORY;
/* Append digest */
if(curlx_dyn_addn(binding, buf, length))
return CURLE_OUT_OF_MEMORY;
return CURLE_OK;
Statement on AI usage: AI was used in parts of detection, triage, fix generation and reporting writing in combination with classical tooling and manual human input.
## Impact
## Impact
A remote HTTPS server that advertises `WWW-Authenticate: Negotiate` can cause a libcurl client (built with OpenSSL and GSSAPI) to leak one X509 certificate reference per authentication attempt when TLS channel binding is retrieved.
Over many requests in a long-lived process, this leads to unbounded memory growth and a denial-of-service condition due to resource exhaustion.
The leak occurs because `ossl_get_channel_binding` calls `SSL_get1_peer_certificate` (which increments the X509 reference count) but never calls `X509_free` on any return path—success or error.
### Constraints:
- Requires libcurl built with OpenSSL (>=1.1.0) and GSSAPI support
- Only affects clients using Negotiate authentication over HTTPS
- Linear leak rate (not amplified), but unbounded over time
Severity: **Low** (limited scope, requires specific build configuration and authentication method, but genuine DoS risk for affected deployments)
Basic Information
ID
H1:3373640
Published
Oct 6, 2025 at 21:39
Modified
Oct 8, 2025 at 06:38