Integer overflow in CERT_FormatName length accumulator leads to heap-buffer-overflow write
Categories
(NSS :: Libraries, defect, P3)
Tracking
(nss 3.125, firefox-esr115 wontfix, firefox-esr140 affected, firefox151 wontfix, firefox152 wontfix, firefox153 fixed)
People
(Reporter: bugmon, Assigned: beurdouche)
Details
(5 keywords, Whiteboard: [nss-nofx][adv-main153-])
Attachments
(4 files)
CERT_FormatName() in security/nss/lib/certhigh/certhtml.c accumulates the byte lengths of all decoded Distinguished Name attribute values (CN, O, up to 20 OU, up to 20 DC, etc.) plus HTML separator lengths into a 32-bit unsigned int len (line 80). No overflow check is performed on any of the additions. When the cumulative decoded length exceeds 2^32, len wraps to a small value; PORT_Alloc(len) at line 216 then allocates an undersized buffer and the subsequent unconditional PORT_Memcpy loops (lines 224-285) copy the full attacker-controlled attribute contents into it, producing a massive out-of-bounds heap write.
With 20 organizationalUnit and 20 domainComponent AVAs (the maximum the function will collect) each carrying a valid DER UTF8String of 107,374,179 bytes, the sum is 40107374179 + 414 = 4,294,967,324 = 2^32 + 28. len wraps to 28, a 28-byte buffer is allocated, and the first OU memcpy at line 236 writes ~102 MiB of attacker-supplied bytes past the allocation.
CERT_FormatName is a publicly exported NSS symbol (nss.def) intended to render certificate Subject/Issuer names as HTML and is callable on attacker-supplied X.509 names by any NSS consumer (S/MIME UI, certificate viewers, third-party applications). Practical exploitation requires the consumer to accept and decode a certificate whose Subject or Issuer DN totals roughly 4 GiB, which is unlikely in browser TLS paths but may be reachable in applications that import certificates from disk or that lack size caps on PKCS#7/CMS or LDAP-delivered certificates.
Build Info
- Branch: main
- Revision: 6164ea4bacaeaed1f617c11911df7fc32f2e6ec2
- Timestamp: 2026-04-02T19:36:24+00:00
Affected Code
File: security/nss/lib/certhigh/certhtml.c, line 80
unsigned len = 0;
File: security/nss/lib/certhigh/certhtml.c, line 171-191
case SEC_OID_AVA_ORGANIZATIONAL_UNIT_NAME:
if (ou_count < MAX_OUS) {
orgunit[ou_count] = CERT_DecodeAVAValue(&ava->value);
if (!orgunit[ou_count]) {
goto loser;
}
len += orgunit[ou_count++]->len; /* no overflow check */
// each ou will have BREAK after it
len += BREAKLEN;
}
break;
case SEC_OID_AVA_DC:
if (dc_count < MAX_DC) {
dc[dc_count] = CERT_DecodeAVAValue(&ava->value);
if (!dc[dc_count]) {
goto loser;
}
len += dc[dc_count++]->len; /* no overflow check */
// each dc will have BREAK after it
len += BREAKLEN;
}
break;
File: security/nss/lib/certhigh/certhtml.c, line 212-240
// there may be a final BREAK
len += BREAKLEN;
/* allocate buffer */
buf = (char *)PORT_Alloc(len); /* len has wrapped to 28 */
if (!buf) {
goto loser;
}
tmpbuf = buf;
...
for (i = ou_count - 1; i >= 0; i--) {
PORT_Memcpy(tmpbuf, orgunit[i]->data, orgunit[i]->len); /* writes 107374179 bytes into 28-byte buf */
tmpbuf += (orgunit[i]->len);
PORT_Memcpy(tmpbuf, BREAK, BREAKLEN);
tmpbuf += BREAKLEN;
}
The 32-bit len accumulator sums up to 47 attacker-controlled SECItem lengths plus constant separator sizes with no overflow guard. After wrap, PORT_Alloc returns a tiny buffer and the per-field PORT_Memcpy calls write the full (un-wrapped) byte counts into it.
Exploit Chain
- Attacker crafts an X.509 certificate (or other DER Name source) whose Subject contains 20 OU and 20 DC AttributeTypeAndValue entries, each a valid UTF8String of ~107,374,179 bytes, so that the sum of decoded lengths plus 41*BREAKLEN equals 2^32 + k for small k.
- Victim application using NSS loads/decodes the certificate into a CERTCertificate, populating cert->subject as a CERTName whose CERTAVA value SECItems reference the large DER strings.
- Victim application calls the exported CERT_FormatName(&cert->subject) (e.g., to render the certificate subject in a UI).
- CERT_FormatName iterates the RDNs/AVAs, calls CERT_DecodeAVAValue on each, and adds each decoded length into the 32-bit
unsigned len; after the 40th addition plus separators,lenwraps modulo 2^32 to a small value (28 in the PoC). - PORT_Alloc(28) returns a 28-byte heap chunk.
- The OU copy loop at line 236 executes PORT_Memcpy(tmpbuf, orgunit[19]->data, 107374179), writing ~102 MiB of attacker-controlled bytes past the 28-byte allocation, corrupting adjacent heap metadata and objects.
- Heap corruption with fully attacker-controlled content and length can be leveraged for control-flow hijack / arbitrary code execution in the process that called CERT_FormatName.
Steps to Reproduce
- Apply the test
CERT_FormatNameUnitTest.LenIntegerOverflowto /firefox/security/nss/gtests/certhigh_gtest/certhigh_unittest.cc. - Build NSS with AddressSanitizer: cd /firefox/security/nss && ./build.sh --asan
- Run: GTEST_FILTER='CERT_FormatNameUnitTest.LenIntegerOverflow' /firefox/security/dist/Debug/bin/certhigh_gtest (host needs >~5 GiB free RAM for the 40 decoded copies).
- Observe ASAN heap-buffer-overflow WRITE of 107374179 bytes at certhtml.c:236 into a 28-byte region allocated at certhtml.c:216.
Security Impact
- Severity: Moderate
- Attacker capability: Heap-buffer-overflow write of attacker-controlled data and attacker-chosen (huge) length past a small heap allocation inside the process calling CERT_FormatName. If reachable, this is sufficient for heap corruption leading to potential remote code execution in that process.
- Preconditions: An NSS consumer must (a) accept and DER-decode a certificate / Name structure whose Subject or Issuer DN cumulatively contains ~4 GiB of attribute-value content (20 OU + 20 DC of ~102 MiB each, or equivalent), and (b) subsequently call CERT_FormatName on that name. The host must have enough memory for CERT_DecodeAVAValue to materialize ~4 GiB of decoded copies. Browser TLS paths are unlikely to accept multi-gigabyte certificates, so the realistic attack surface is third-party NSS consumers or local certificate-import / S-MIME flows without input-size limits.
ASAN Report
==10835==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x50300000c71c at pc 0x7b876405b303 bp 0x7fffc4c31030 sp 0x7fffc4c307d8
WRITE of size 107374179 at 0x50300000c71c thread T0
#0 0x7b876405b302 in memcpy ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors_memintrinsics.inc:115
#1 0x7b8763d9b325 in CERT_FormatName ../../lib/certhigh/certhtml.c:236
#2 0x5c7b3165c505 in nss_test::CERT_FormatNameUnitTest_LenIntegerOverflow_Test::TestBody() (/firefox/security/dist/Debug/bin/certhigh_gtest+0x1b505)
...
0x50300000c71c is located 0 bytes after 28-byte region [0x50300000c700,0x50300000c71c)
allocated by thread T0 here:
#0 0x7b876405d9c7 in malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:69
#1 0x7b8763a306e7 in PR_Malloc ../../../../pr/src/malloc/prmem.c:425
#2 0x7b8763cf29fa in PORT_Alloc_Util ../../lib/util/secport.c:87
#3 0x7b8763d9b087 in CERT_FormatName ../../lib/certhigh/certhtml.c:216
#4 0x5c7b3165c505 in nss_test::CERT_FormatNameUnitTest_LenIntegerOverflow_Test::TestBody() (/firefox/security/dist/Debug/bin/certhigh_gtest+0x1b505)
...
SUMMARY: AddressSanitizer: heap-buffer-overflow ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors_memintrinsics.inc:115 in memcpy
| Reporter | ||
Comment 1•5 months ago
|
||
| Reporter | ||
Comment 2•5 months ago
|
||
| Reporter | ||
Comment 3•5 months ago
|
||
Updated•5 months ago
|
Updated•5 months ago
|
| Assignee | ||
Updated•5 months ago
|
| Assignee | ||
Comment 5•5 months ago
|
||
Pushed by bbeurdouche@mozilla.com:
https://hg.mozilla.org/projects/nss/rev/ef0357bce0b9
Widen CERT_FormatName length accumulator from unsigned to size_t. r=nss-reviewers,rrelyea
Updated•4 months ago
|
Updated•4 months ago
|
Updated•2 months ago
|
Updated•13 days ago
|
Description
•