Closed Bug 2029748 Opened 5 months ago Closed 4 months ago

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)

RESOLVED FIXED
Tracking Status
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

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

  1. 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.
  2. 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.
  3. Victim application calls the exported CERT_FormatName(&cert->subject) (e.g., to render the certificate subject in a UI).
  4. 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, len wraps modulo 2^32 to a small value (28 in the PoC).
  5. PORT_Alloc(28) returns a 28-byte heap chunk.
  6. 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.
  7. 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

  1. Apply the test CERT_FormatNameUnitTest.LenIntegerOverflow to /firefox/security/nss/gtests/certhigh_gtest/certhigh_unittest.cc.
  2. Build NSS with AddressSanitizer: cd /firefox/security/nss && ./build.sh --asan
  3. Run: GTEST_FILTER='CERT_FormatNameUnitTest.LenIntegerOverflow' /firefox/security/dist/Debug/bin/certhigh_gtest (host needs >~5 GiB free RAM for the 40 decoded copies).
  4. 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
Attached file certhigh_unittest.cc —
Attached patch fix.patch — — Splinter Review
Attached file crash_stack.txt —
Group: core-security → crypto-core-security
Severity: -- → S3
Status: UNCONFIRMED → NEW
Ever confirmed: true
Priority: -- → P3
Whiteboard: [nss-nofx]
Assignee: nobody → bbeurdouche
Status: NEW → ASSIGNED
Attached file (secure) —

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

Status: ASSIGNED → RESOLVED
Closed: 4 months ago
Resolution: --- → FIXED
Group: crypto-core-security → core-security-release
Whiteboard: [nss-nofx] → [nss-nofx][adv-main153-]
Group: core-security-release
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: