Closed Bug 2029796 Opened 5 months ago Closed 4 months ago

Heap buffer overflow write in [@ sftk_ike_prf] due to CK_ULONG → unsigned int truncation of ulNiLen+ulNrLen (CKM_IKE_PRF_DERIVE)

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)

In the NSS softoken implementation of the PKCS#11 CKM_IKE_PRF_DERIVE mechanism, sftk_ike_prf() concatenates the caller-supplied Ni and Nr nonces into a freshly allocated buffer when bDataAsKey is set. The size of that buffer is computed as newInKeySize = params->ulNiLen + params->ulNrLen; where newInKeySize is a 32-bit unsigned int but ulNiLen and ulNrLen are CK_ULONG (64-bit on LP64). If the 64-bit sum is at least 2^32, it is silently truncated, so PORT_Alloc(newInKeySize) returns an undersized buffer. The immediately following PORT_Memcpy(newInKey, params->pNi, params->ulNiLen) (and the second memcpy for pNr) still use the full 64-bit lengths, producing a controllable-length heap buffer overflow with caller-controlled data.

The code path is reached via the exported C_DeriveKey / NSC_DeriveKey entry point (and the higher-level PK11_Derive wrapper). NSC_DeriveKey only checks that pMechanism->ulParameterLen == sizeof(CK_IKE_PRF_DERIVE_PARAMS); it does not bound ulNiLen or ulNrLen. Any application that links the NSS softoken and forwards attacker-influenced nonce lengths into CK_IKE_PRF_DERIVE_PARAMS can be driven to corrupt the heap. Even on 32-bit builds the same arithmetic wraps in unsigned int, yielding the same undersized allocation.

This is a memory-safety issue (heap OOB write with attacker-controlled contents and size) at a documented PKCS#11 API boundary. Practical remote exploitability depends on the embedding application: well-formed IKE wire payloads carry 16-bit length fields, so a network peer cannot directly supply ~4 GiB nonce lengths, but any caller that reads a wider length field from untrusted input, or any cross-trust-boundary PKCS#11 client, can trigger the overflow.

Build Info

Affected Code

File: security/nss/lib/softoken/sftkike.c, line 467-508

    unsigned char *newInKey = NULL;
    unsigned int newInKeySize = 0;          /* 32-bit */
    ...
    if (params->bDataAsKey) {
        /* The key is Ni || Np, so we need to concatenate them together first */
        newInKeySize = params->ulNiLen + params->ulNrLen;   /* CK_ULONG sum truncated */
        newInKey = PORT_Alloc(newInKeySize);                /* undersized */
        if (newInKey == NULL) {
            crv = CKR_HOST_MEMORY;
            goto fail;
        }
        PORT_Memcpy(newInKey, params->pNi, params->ulNiLen);                 /* OOB write */
        PORT_Memcpy(newInKey + params->ulNiLen, params->pNr, params->ulNrLen);
        crv = prf_init(&context, newInKey, newInKeySize);

File: security/nss/lib/softoken/pkcs11c.c, line 8519-8527

        case CKM_NSS_IKE_PRF_DERIVE:
        case CKM_IKE_PRF_DERIVE:
            if (pMechanism->ulParameterLen !=
                sizeof(CK_IKE_PRF_DERIVE_PARAMS)) {
                crv = CKR_MECHANISM_PARAM_INVALID;
                break;
            }
            crv = sftk_ike_prf(hSession, att,
                               (CK_IKE_PRF_DERIVE_PARAMS *)pMechanism->pParameter, key);

newInKeySize is a 32-bit unsigned int but is assigned the sum of two 64-bit CK_ULONG values supplied verbatim from the C_DeriveKey mechanism parameter. The truncated value sizes the allocation while the untruncated ulNiLen/ulNrLen drive the memcpy lengths. NSC_DeriveKey validates only the outer parameter-struct size, not the inner nonce lengths.

Exploit Chain

  1. Application (or PKCS#11 client) calls C_DeriveKey / PK11_Derive with mechanism CKM_IKE_PRF_DERIVE (or CKM_NSS_IKE_PRF_DERIVE) on the NSS softoken.
  2. CK_IKE_PRF_DERIVE_PARAMS has bDataAsKey = CK_TRUE and ulNiLen + ulNrLen >= 2^32 (e.g. ulNiLen = 64, ulNrLen = 0xFFFFFFE0); pNi points to attacker-controlled bytes.
  3. NSC_DeriveKey accepts the params (only sizeof check) and calls sftk_ike_prf().
  4. sftk_ike_prf computes newInKeySize = (unsigned int)(ulNiLen + ulNrLen) = 32 and PORT_Alloc(32) succeeds.
  5. PORT_Memcpy(newInKey, pNi, ulNiLen=64) writes 64 attacker-controlled bytes into the 32-byte heap buffer, overflowing by 32 bytes (size and contents fully controllable by choice of ulNiLen/ulNrLen/pNi).
  6. Heap metadata or an adjacent object is corrupted, enabling memory corruption primitives in the process hosting the softoken.

Steps to Reproduce

  1. Build NSS with ASan: security/nss/build.sh --asan (gyp/ninja required; e.g. pip3 install --break-system-packages gyp-next ninja).
  2. Append the test Pkcs11IkePrfOverflow.NiNrLengthTruncation to security/nss/gtests/pk11_gtest/pk11_ike_unittest.cc.
  3. Run the pk11_gtest binary with --gtest_filter=Pkcs11IkePrfOverflow.NiNrLengthTruncation.
  4. Observe AddressSanitizer heap-buffer-overflow WRITE of size 64 into a 32-byte region at sftkike.c:506, allocated at sftkike.c:501.

Security Impact

  • Severity: Moderate
  • Attacker capability: Heap buffer overflow write of attacker-chosen length and contents inside the process hosting the NSS softoken (libsoftokn3), potentially leading to code execution in that process.
  • Preconditions: The calling application must invoke C_DeriveKey/PK11_Derive with CKM_IKE_PRF_DERIVE and pass CK_IKE_PRF_DERIVE_PARAMS whose ulNiLen + ulNrLen reaches 2^32 with bDataAsKey set. This requires either a hostile/untrusted PKCS#11 client at the C_DeriveKey boundary or an IKE-using application that forwards attacker-controlled nonce length fields without bounding them; standard IKE wire payloads use 16-bit lengths, so a pure network IKE peer typically cannot reach the wrap directly.

ASAN Report

==14885==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x50300068dfc0 at pc 0x7dbece632303 bp 0x7ffca5696dc0 sp 0x7ffca5696568
WRITE of size 64 at 0x50300068dfc0 thread T0
    #0 0x7dbece632302 in memcpy ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors_memintrinsics.inc:115
    #1 0x7dbecd2ba0c0 in sftk_ike_prf ../../lib/softoken/sftkike.c:506
    #2 0x7dbecd287b55 in NSC_DeriveKey ../../lib/softoken/pkcs11c.c:8526
    #3 0x7dbece43e8a5 in PK11_DeriveWithTemplate ../../lib/pk11wrap/pk11skey.c:1783
    #4 0x7dbece43d701 in PK11_Derive ../../lib/pk11wrap/pk11skey.c:1631
    #5 0x56836eb69780 in nss_test::Pkcs11IkePrfOverflow_NiNrLengthTruncation_Test::TestBody() ../../gtests/pk11_gtest/pk11_ike_unittest.cc:239

0x50300068dfc0 is located 0 bytes after 32-byte region [0x50300068dfa0,0x50300068dfc0)
allocated by thread T0 here:
    #0 0x7dbece6349c7 in malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:69
    #1 0x7dbece0076e7 in PR_Malloc ../../../../pr/src/malloc/prmem.c:425
    #2 0x7dbece2c99fa in PORT_Alloc_Util ../../lib/util/secport.c:87
    #3 0x7dbecd2ba027 in sftk_ike_prf ../../lib/softoken/sftkike.c:501
    #4 0x7dbecd287b55 in NSC_DeriveKey ../../lib/softoken/pkcs11c.c:8526
    #5 0x7dbece43e8a5 in PK11_DeriveWithTemplate ../../lib/pk11wrap/pk11skey.c:1783
    #6 0x7dbece43d701 in PK11_Derive ../../lib/pk11wrap/pk11skey.c:1631
    #7 0x56836eb69780 in nss_test::Pkcs11IkePrfOverflow_NiNrLengthTruncation_Test::TestBody() ../../gtests/pk11_gtest/pk11_ike_unittest.cc:239

SUMMARY: AddressSanitizer: heap-buffer-overflow ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors_memintrinsics.inc:115 in memcpy
==14885==ABORTING
Group: core-security → crypto-core-security
Attached file pk11_ike_unittest.cc —
Attached patch fix.patch — — Splinter Review
Attached file crash_stack.txt —
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/b62f369c2b7b
Bound IKE PRF nonce lengths to prevent CK_ULONG to unsigned int truncation. r=rrelyea,nss-reviewers

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: