impalabs space base graphics
Huawei TrustZone Tee_Fido_Main Vulnerability
This advisory contains information about the following vulnerabilities:

Stack Buffer Overflow in UnwrapKeyHandle

The function UnwrapKeyHandle parses TLV objects stored in pKeyHandlesIn. After the parsing of the expected objects is finished, if there are remaining objects, they are stored into the pTags variable of type ak_tlvs_t.

cal_handle_t UnwrapKeyHandle(
        createKeyHandleArgs_t *pArgs,
        cal_blob_t *pTransactionContent,
        cal_blob_t *pKeyHandlesIn,
        ak_byte_t nKeyHandlesInNum,
        ak_byte_t *usernamesin,
        cal_blob_t *usernamesOut,
        cal_blob_t **pKeyHandlesOut,
        ak_byte_t *nKeyHandlesOutNum,
        ak_tlvs_t *pTags)
{
    // ...
    InBLOB = pKeyHandlesIn[i];
    OutBLOB.pData = 0;
    OutBLOB.length = 0;
    UnwrapData(&InBLOB, &OutBLOB, 0, 1);
    pTemp = OutBLOB.pData;
    remainingLen = OutBLOB.length;
    // ...some parsing...
    pTags->numTags = 0;
    while (remainingLen >= 4) {
        tag = &pTags->tlvs[pTags->numTags];
        pTemp = AK_GetWord(tag->tag, pTemp, &remainingLen);
        pTemp = AK_GetWord(tag->length, pTemp, &remainingLen);
        if (!tag->tag) { /* ... */ }
        if (remainingLen < tag->length) { /* ... */ }
        tag->value = nnl_malloc(tag->length);
        if (!tag->value) { /* ... */ }
        pTemp = AK_GetBytes(tag->value, tag->length, pTemp, &remainingLen);
        ++pTags->numTags;
    }
    // ...
}

The ak_tlvs_t structure can contain a fixed number (10) of extra objects. However, the function will continue storing objects even if there are more than 10, resulting in a buffer overflow of the pTags->tlvs array.

struct ak_tlvs_t
{
  ak_word_t numTags;
  ak_tlv_t tlvs[10];
};

struct ak_tlv_t
{
  ak_word_t tag;
  ak_word_t length;
  ak_byte_t *value;
};

The UnwrapKeyHandle function is called from the Sign function where the ak_tlvs_t structure is declared on the stack.

ak_result_t Sign(
        ak_internal_info_t *pAKInfo,
        input_args_t *pInputArgs,
        ak_byte_t *pResponse,
        ak_word_t *pResponseLength)
{
    // ...
    ak_tlvs_t pKHTags;
    // ...
    hUauthKey = UnwrapKeyHandle(
                    &createKHArgs,
                    &pInputArgs->transactionContent,
                    pInputArgs->keyHandles,
                    pInputArgs->keyHandlesNum,
                    usernames,
                    signRespArgs.usernames,
                    signRespArgs.pKeyHandles,
                    &signRespArgs.keyHandleNum,
                    &pKHTags);
    // ...
}

The pKeyHandlesIn parameter of UnwrapKeyHandle is fully user-controlled and comes from the ExtractInputArgs function. This function will process TLV objects in the first TEE_Param input buffer. The only limitation is that the key handle is smaller than or equal to 0x200 bytes.

int ta_cmd_handler(
        uint32_t *ibuf0_addr,
        uint32_t ibuf0_size,
        uint32_t *obuf3_addr,
        uint32_t obuf3_size)
{
    // ...
    UAF_AK_Process(ibuf0_addr, request_length, khaccesstoken, obuf3_addr, &obuf3_size);
    // ...
}
ak_result_t UAF_AK_Process(
        int pRequest,
        int requestLength,
        int a3,
        ak_byte_t *pResponse,
        ak_word_t *pResponseLength)
{
    // ...
    ExtractInputArgs(pRequest, requestLength, &inputArgs);
    switch (inputArgs.operationType) {
        case 0x3403:
            // ...
            Info = Sign(&gAKInfo, &inputArgs, pResponse, pResponseLength);
            break;
    }
    // ...
}
ak_result_t ExtractInputArgs(const ak_byte_t *pRequest, ak_word_t requestLength, input_args_t *pInputArgs) {
    // ...
    ptr = pRequest;
    remainder = requestLength;
    // ...some parsing...
    while (remainder > 0) {
        // ...
        ptr = AK_GetTlv(&tlv, ptr, &remainder);
        // ...
        if (tlv.tag == 0x2801) {
            if (tlv.length > 0x200) { /* ... */ }
            if (pInputArgs->keyHandlesNum > 0xF) { /* ... */ }
            keyHandlesNum = pInputArgs->keyHandlesNum;
            pInputArgs->keyHandles[keyHandlesNum].length = tlv.length;
            pInputArgs->keyHandles[keyHandlesNum].pData = tlv.value;
            pInputArgs->keyHandlesNum = keyHandlesNum + 1;
        }
        // ...
    }
    // ...
}

Because the stack frame of Sign is too large compared to the size we can overflow, we did not find a way to exploit this vulnerability. As such, we did not attempt to trigger it.

Affected Devices

We have verified that the vulnerability impacted the following device(s):

  • Kirin 990: P40 Pro (ELS)

Please note that other models might have been affected.

Patch

Name Severity CVE Patch
Stack Buffer Overflow in UnwrapKeyHandle Low N/A Fixed

Timeline

  • Dec. 14, 2021 - A vulnerability report is sent to Huawei PSIRT.
  • Jan. 12, 2022 - Huawei PSIRT acknowledges the vulnerability report.
  • From Nov. 30, 2022 to Jul, 19 2023 - We exchange regularly about the release of our advisories.