Use-after-free in tls13_HandleCertificateRequest via repeated post-handshake CertReq
Categories
(NSS :: Libraries, defect)
Tracking
(Not tracked)
People
(Reporter: social, Assigned: djackson)
Details
(4 keywords, Whiteboard: [client-bounty-form])
Attachments
(1 file, 1 obsolete file)
|
18.20 KB,
application/gzip
|
Details |
A malicious TLS 1.3 server can crash any NSS client — including Firefox — by sending repeated post-handshake CertificateRequest messages. The attack takes 2.9 KB on the wire and under 100 ms. No MITM, no credentials, no special positioning. Embedded in an ad network or CDN endpoint, this would hit every connecting Firefox or PKCS#11 client. The bug has been present since NSS 3.43 (March 2019).
Reproduction
tstclnt crash (requires libnss3-tools, python3-cryptography):
python3 poc_nss_certreq_memleak_standalone.py
Firefox crash (requires firefox-esr):
python3 poc_nss_certreq_firefox.py --headless
Firefox crashes with SIGSEGV in milliseconds. Post-handshake authentication is configured automatically by the PoC; no client cert or PKCS#11 module required. Full report in attached ADVISORY.md.
What happens
The PoC completes a real TLS 1.3 handshake (X25519 + ECDSA-P256), then sends repeated encrypted CertificateRequest messages. Each one leaks a PK11 hash context (~200-300 bytes) by overwriting ss->ssl3.hs.shaPostHandshake without freeing the previous value. After 73 re-entries, heap metadata corruption causes xtnData->sigSchemes to point into a previously freed SQLite cert DB buffer. PORT_Free receives a stale, misaligned pointer (0x5555555f000d), crashing the process.
This is heap corruption, not resource exhaustion. On allocators without pointer validation (embedded systems, older libraries), this corruption pattern could potentially be leveraged beyond a crash.
Root cause
tls13_HandleCertificateRequest() in lib/ssl/tls13con.c clones a transcript hash context (PK11_CloneContext at line 3138) on each post-handshake CertificateRequest but never frees the previous clone. The cleanup block (lines 3149-3168) handles five other fields — clientCertChain, clientCertificate, clientPrivateKey, clientAuthSignatureSchemes, certReqContext — but omits shaPostHandshake. PORT_Assert(shaPostHandshake == NULL) at line 3137 confirms the developers intended this to be NULL on re-entry. The assert fires on the second CertReq in debug builds but is compiled out in release.
Fix: free the previous shaPostHandshake before cloning. Design options in ADVISORY.md.
Affected
NSS 3.43 through 3.110 (current). Any NSS consumer with SSL_ENABLE_POST_HANDSHAKE_AUTH: PKCS#11/HSM middleware, smartcard clients, enterprise Firefox with security.tls.enable_post_handshake_auth. CWE-416 (Use After Free). CVSS 3.1: AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H = 7.5 (High).
URL: (not applicable — NSS library vulnerability, not a web page)
Attachment: VULN-NSS-POSTHANDSHAKE-CERTREQ-HEAPCORRUPT.tar.gz (contains ADVISORY.md, poc_nss_certreq_memleak_standalone.py, poc_nss_certreq_firefox.py)
Updated•7 months ago
|
Comment 1•7 months ago
|
||
Comment 2•7 months ago
•
|
||
Keep in mind that SSL_ENABLE_POST_HANDSHAKE_AUTH is off by default. Ref: https://nss-crypto.org/reference/security/nss/legacy/nss_releases/nss_3.43_release_notes/index.html and https://searchfox.org/firefox-main/source/modules/libpref/init/StaticPrefList.yaml#18069
Updated•7 months ago
|
Updated•7 months ago
|
Updated•7 months ago
|
| Assignee | ||
Comment 3•7 months ago
•
|
||
[Comment made in error, deleted]
| Assignee | ||
Comment 4•7 months ago
|
||
Thank you for the detailed report and the reproduction scripts. This issue was reported to us 2 days before your report in bug 2026089.
The crash you're reporting is a real bug, however, the root cause analysis is incorrect and I don't think ss->ssl3.hs.shaPostHandshake is involved.
Regarding the shaPostHandshake analysis: PHA exchanges are serialized by the
restartTarget guard in ssl3_GatherCompleteHandshake (ssl3gthr.c:482-486). While a PHA
response is pending -- whether synchronously in-flight or waiting for the async
SSL_ClientCertCallbackComplete callback -- the gather loop returns PR_WOULD_BLOCK_ERROR
without processing further records. A second CertificateRequest cannot reach
tls13_HandleCertificateRequest until the first PHA completes, at which point
tls13_SendPostHandshakeCertificate (tls13con.c:3244-3245) has already destroyed and NULLed
shaPostHandshake. The PORT_Assert(shaPostHandshake == NULL) is always valid, and the leak
described in the report is not reachable.
Please let me know if you disagree with this analysis.
| Assignee | ||
Updated•7 months ago
|
Updated•7 months ago
|
Updated•7 months ago
|
Updated•2 months ago
|
Description
•