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)
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
- Branch: main
- Revision: 6164ea4bacaeaed1f617c11911df7fc32f2e6ec2
- Timestamp: 2026-04-02T19:36:24+00:00
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
- 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.
- 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.
- NSC_DeriveKey accepts the params (only sizeof check) and calls sftk_ike_prf().
- sftk_ike_prf computes newInKeySize = (unsigned int)(ulNiLen + ulNrLen) = 32 and PORT_Alloc(32) succeeds.
- 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).
- Heap metadata or an adjacent object is corrupted, enabling memory corruption primitives in the process hosting the softoken.
Steps to Reproduce
- Build NSS with ASan: security/nss/build.sh --asan (gyp/ninja required; e.g. pip3 install --break-system-packages gyp-next ninja).
- Append the test Pkcs11IkePrfOverflow.NiNrLengthTruncation to security/nss/gtests/pk11_gtest/pk11_ike_unittest.cc.
- Run the pk11_gtest binary with --gtest_filter=Pkcs11IkePrfOverflow.NiNrLengthTruncation.
- 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
Updated•5 months ago
|
| Reporter | ||
Comment 1•5 months ago
|
||
| Reporter | ||
Comment 2•5 months ago
|
||
| Reporter | ||
Comment 3•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/b62f369c2b7b
Bound IKE PRF nonce lengths to prevent CK_ULONG to unsigned int truncation. r=rrelyea,nss-reviewers
Updated•4 months ago
|
Updated•4 months ago
|
Updated•2 months ago
|
Updated•13 days ago
|
Description
•