impalabs space base graphics
Huawei TrustZone HW_KEYMASTER Vulnerabilities
This advisory contains information about the following vulnerabilities:

We identified 3 vulnerabilities affecting Huawei's HW_KEYMASTER trusted application that can be used to achieve code execution at S-EL0:

  • write of arbitrary data to sec_storage_data/PKI/ leading to arbitrary read of memory (and potentially even an arbitrary write);
  • write of controlled params set to an arbitrary memory address in generate_keyblob (name is our own);
  • integer overflow in ber_pop_front (name is our own) leading to an OOB read access to an arbitrary memory address.

We also identified 5 additional bugs affecting Huawei's HW_KEYMASTER trusted application:

  • a stack address leak in cmd_verify_key (name is our own);
  • an integer overflow in ber_init (name is our own);
  • a logic issue in verify_root_cert (name is our own);
  • a stack buffer overflow in get_soter_cpuid (name is our own);
  • an OOB access in get_soter_cpuid (name is our own).

Write of Arbitrary Data to sec_storage_data/PKI/

The cmd_unwrap function at 0x2C0F8 and the unwrap_asset_data_and_write_it_to_file function at 0x2A1E8 can be used to write arbitrary data to any files in the sec_storage_data/PKI/ SFS folder. As the user can specify the key, nonce, and aad, they can send an input and tag that will result in a plaintext of their choosing. Thus, the filename and file data are fully controlled.

int cmd_unwrap(int paramTypes, TEE_Param params[4]) {
    // ...
    // wrap_key_type, associated_data, account_uid and assetstore_base_pwd
    // are extracted from the params set contained in params[0]
    params_from_key_params = get_params_from_key_params(
        params[0].memref.buffer,
        &wrap_key_type,
        &associated_data,
        &account_uid,
        &assetstore_base_pwd);
    // ...
    // fst_alias and snd_alias (2 strings), and wrap_key_type (integer)
    // are extracted as TLVs from params[1]
    key_aliases = get_key_aliases(
        params[1].memref.buffer,
        params[1].memref.size,
        &fst_alias,
        &snd_alias,
        wrap_key_type);
    // ...
    // we can specify wrap_key_type == 1
    switch (wrap_key_type) {
        case 1:
            // if fst_alias is the alias of an AES key, its keymaterial
            // will be retrieved from the SFS and put into key_param
            get_aes_keymaterial_from_alias(&key_param, fst_alias.buf, fst_alias.len);
            // ...
            // a buffer of size 0x1000 is allocated to contain the plaintext
            decrypt_data.buf = TEE_Malloc(0x1000, 0);
            decrypt_data.len = 0x1000;
            // ...
            // the ciphertext contained in params[2] (as a TLV, alongside the nonce
            // and tag) is decrypted using the aforementioned AES key, using the
            // AES-256-CCM crypto algorithm (the AAD is derived from associated_data)
            unwrap_asset_data_and_aes_decrypt(
                key_param.buf,
                key_param.len,
                uid.buf,
                uid.len,
                &params[2].memref.buffer,
                &params[2].memref.size,
                &decrypt_data);
            // ...
            // unwrap_asset_data_and_write_it_to_file is called with the 
            // user-controlled plaintext and snd_alias
            unwrap_asset_data_and_write_it_to_file(
                decrypt_data.buf,
                decrypt_data.len,
                snd_alias.buf,
                snd_alias.len,
                &params[3]);
            // ...
    }
    // ...
}

int unwrap_asset_data_and_write_it_to_file(
        int *decrypt_buf,
        int decrypt_len,
        int snd_alias_buf,
        int snd_alias_len,
        TEE_Param *param3) {
    // ...
    tlv_buffer = decrypt_buf;
    tlv_val_p = &tlv_val;
    tlv_val.buf = 0;
    tlv_val.len = 0;
    tlv_size = decrypt_len;
    expected_tags[0] = 2;
    // ...
    // tlv_val is extracted as an TLV value from the plaintext data
    parse_unwrap_asset_data_tlv(&expected_tags, 1, &tlv_buffer, &tlv_size, &tlv_val_p);
    // ...
    // tlv_val is written to the SFS to the sec_storage_data/PKI/<snd_alias> file
    write_data_to_file_by_alias(snd_alias_buf, snd_alias_len, tlv_val.buf, tlv_val.len);
    // ...
}

This vulnerability has security implications as the sec_storage_data/PKI/ folder contains files of various types used by Keymaster (in particular keyblobs). Faking the content of keyblobs can lead to arbitrary reads, and potentially arbitrary writes as well, as we will see in the exploitation section.

Write of Controlled Params Set in generate_keyblob

The generate_keyblob function at 0xB0CC expects params[3] to be a memref, as it will fill it with the params set of the keyblob that was just generated (using the resort_key_characteristics function at 0xA1C8). However, there exists a code path reported below, that starts from cmd_begin and allows reaching generate_keyblob with a params[1] that is a value and not a memref. This means that the params set data will be written to an arbitrary address. Furthermore, as the params set comes from params[1], the data written is also partially user-controlled.

The vulnerable code path is as follows:

  • cmd_begin at 0xC540
  • get_key_blob at 0xBE60
  • process_begin_for_keychain_new at 0xBCC0
  • generate_symmetric_keymaterial_special at 0xBA98
  • generate_keyblob at 0xB0CC
  • resort_key_characteristics at 0xA1C8
int cmd_begin(unsigned int paramTypes, TEE_Param params[4]) {
    // ...
    get_key_blob(params, params_enforced, &key_blob, &vendor_type);
    // ...
}

int get_key_blob(
        TEE_Param *params,
        unsigned int *params_enforced,
        keyblob_t **keyblob_p,
        int *vendor_type_p) {
    // ...
    return process_begin_for_keychain_new(v10, v13, v12, v11);
    // ...
}

int process_begin_for_keychain_new(
        int access_limit,
        TEE_Param *params,
        unsigned int *params_enforced,
        keyblob_t **keyblob_p) {
    // ...
    generate_symmetric_keymaterial_special(key_data, 0x100u, &derive_factor, params, keyblob_p);
    // ...
}

int generate_symmetric_keymaterial_special(
        char *key_data,
        unsigned int key_size,
        key_param_t *derive_factor,
        TEE_Param *params,
        keyblob_t **keyblob_p) {
    // ...
    generate_keyblob(&derived_key, 0, params, 1, keyblob_p);
    // ...
}

int generate_keyblob(
        key_param_t *derived_key,
        int a2,
        TEE_Param *params,
        int use_second_param,
        keyblob_t **keyblob_p) {
    // ...
    if (use_second_param == 1)
        params_set = params[1].memref.buffer;
    else
        params_set = params[0].memref.buffer;
    // ...
    keyblob = TEE_Malloc(0x1000u, 0);
    keyblob->magic = 'HIKM';
    keyblob->version = 0x12C;
    memcpy_s(keyblob->blob, 0xFA0, derived_key->buf, derived_key->len);
    keyblob->key_len = derived_key->len;
    keyblob->key_off = 0x60;
    // ...
    hw_params_off = (derived_key->len + 0x63) & 0xFFFFFFFC;
    hw_params_count = *(uint32_t *)params_set;
    *(uint32_t *)(keyblob + hw_params_off) = hw_params_count;
    memcpy_s(keyblob + hw_params_off + 4, 0xFFC - hw_params_off, params_set + 1, 0x10 * hw_params_count);
    keyblob->hw_params_off = hw_params_off;
    // ...
    // copying of the sw_params in a similar fashion
    // ...
    // copying of the out-of-line params set data in a similar fashion
    // ...
    sort_params_set = params[3].memref.buffer;
    if (!sort_params_set) // ...
    sort_params_set_size = params[3].memref.size;
    if (!sort_params_set_size) // ...
    total_size = extra_end_offset - keyblob_->hw_params_off;
    if (sort_params_set_size < total_size) // ...
    resort_key_characteristics(sort_params_set, hw_params_ptr, total_size);
    // ...
}

We demonstrate this arbitrary write in the exploitation section of this report.

Integer Overflow in ber_pop_front

int ber_pop_front(ber_t *out_ber, ber_t *in_ber, int expected_type, int expected_class, int allowed_flag) {
    // ...
    read_ptr = in_ber->read_ptr;
    // ...
    tag = ber_read_byte(in_ber);
    // ...
    first_length = ber_read_byte(in_ber);
    // ...
    length = first_length;
    if (first_length & 0x80) {
        length = 0;
        for (int i = 0; i < first_length & 0x7F; ++i)
            length = ber_read_byte(in_ber) | (length << 8);
    }
    // ...
    new_end = in_ber->read_ptr + length;
    if (new_end <= in_ber->buf_end) {
        in_ber->read_ptr = new_end;
        out_ber->read_ptr = read_ptr;
        out_ber->buf_end = new_end;
    } else {
        // ...
    }
    // ...
}

The ber_pop_front function at 0x1D54 contains an integer overflow on the in_ber->read_ptr + length addition. By specifying a negative value for length, it is possible to make new_end point before the start of the buffer. This pointer will then be used the next time ber_pop_front is called to read a BER encoded value, which will result in a OOB read access.

We triggered this bug with a proof of concept and obtained the following crash at 0x2ebefec9 = 0x7000400a - 0x41414141:

[HM] [ERROR][2171]vmem_as_ondemand_prepare failed
[HM] [ERROR][2496]process 2200000028 (tid: 40) data abort:
[HM] [ERROR][2498]Bad memory access on address: 0x2ebefec9, fault_code: 0x92000006
[HM]
[HM] Dump task states for tcb
[HM] ----------
[HM]     name=[HW_KEYMASTER] tid=40 is-idle=0 is-curr=0
[HM]     state=BLOCKED@MEMFAULT sched.pol=0 prio=46 queued=1
[HM]     aff[0]=ff
[HM]     flags=1000 smc-switch=0 ca=9358 prefer-ca=9358
[HM] Registers dump:
[HM] ----------
[HM] 32 bits userspace stack dump:
[HM] ----------
[HM] <?>+0x0/0x0
[HM] <TA_InvokeCommandEntryPoint>+0x64/0xd8
[HM] <tee_task_entry>+0x398/0xcd4
[HM] Dump task states END
[HM]
[HM] [TRACE][1212]pid=51 exit_status=130

Stack Address Leak in cmd_verify_key

The cmd_verify_key function at 0x39E80 contains a call to the SLog function that will print the address of a stack variable. This log message can be retrieved using logcat. We did not manage to trigger the call which is located in an error path.

.text:0003A3F4  LDR  R1, =(aError - 0x3A408) ; "[Error]"
.text:0003A3F8  SUB  R3, R11, #-a2
.text:0003A3FC  LDR  R0, =(aSCurrentCertIt - 0x3A40C) ; "%s: current cert iterator[%u] > count[%"...
.text:0003A400  ADD  R1, PC, R1 ; "[Error]"
.text:0003A404  ADD  R0, PC, R0 ; "%s: current cert iterator[%u] > count[%"...
.text:0003A408  BL   SLog

Integer Overflow in ber_init

The ber_init function at 0x1FB8 contains an integer overflow on the buf + size addition. We did not find a way to trigger this bug as the size comes from one of the TEE_Params and their size is limited to 1 MB on the kernel side.

ber_t *ber_init(ber_t *ber, uint8_t *buf, uint32_t size, ber_state_t *state) {
    state->error = 0;
    state->error_ptr = 0;
    ber->read_ptr = buf;
    ber->buf_end = buf + size;
    ber->state = state;
    return ber;
}

int cmd_sop_decrypt(unsigned int paramTypes, TEE_Param params[4]) {
    // ...
    ibuf1_addr = params[1].memref.buffer;
    if (!ibuf1_addr) // ...
    // ...
    ibuf1_size = params[1].memref.size;
    if (!buf1_size) // ...
    // ...
    ber_init(&ber, ibuf1_addr, ibuf1_size, &state);
    // ...
}

Logic Issue in verify_root_cert

The verify_root_cert function at 0x34700 is used to verify that the root certificate currently installed (saved on the SFS) is the same as the root certificate in the user-provided certificate chain. There is a logic issue on the comparison, as the error case is only taken if the sizes differ AND the contents differ (instead of OR). We did not evaluate the security impact of this bug.

unsigned int verify_root_cert(cert_entry_t *certs) {
    // ...
    cert_entry_t root_certs[3];
    memset(root_certs, 0, sizeof(root_certs));

    for (int i = 0; i < 3; ++i) {
        cert_entry = get_cert_entry(1, 1, i, &root_certs[i]);
        // ...
    }

    if (root_certs[2].len != certs[2].len
            && TEE_MemCompare(root_certs[2].buf, certs[2].buf, root_certs[2].len)) {
        ret = -1;
        SLog("%s: verifyRootCert fail\n\n", "[Error]");
    }

    free_certs(root_certs, 3);
    return ret;
}

Stack Buffer Overflow in get_soter_cpuid

The function get_soter_cpuid at 0x18510 can overflow the buffer it is given as argument. If the size of the common_name (extracted from a certificate that the user can set) is equal to the size of the buffer, the random hexadecimal string hexbuf of size 9 will be written out of the buffer's bounds.

This function is called, via the get_soter_cntr_or_cpuid function at 0x18794, in 3 places:

  • in the build_json_for_attestation function at 0x18C1C
  • in the build_json_message_to_sign function at 0x18DCC
  • in the build_soter_json_AK function at 0x19BF8

In each of these cases, the overflowed buffer is allocated on the stack (and of size 0x80). We did not attempt to trigger this bug.

int build_json_for_attestation(int a1, int a2, int a3, int a4) {
    // ...
    char s[128];
    // ...
    memset(out_buf, 0, sizeof(out_buf));
    out_buf_len = 0x80;
    get_soter_cntr_or_cpuid(0, out_buf, &out_buf_len);
    // ...
}

int get_soter_cntr_or_cpuid(int a1, char *out_buf, uint32_t *out_buf_len_p) {
    get_soter_cpuid(out_buf, out_buf_len_p);
}

int get_soter_cpuid(char *out_buf, uint32_t *out_buf_len_p) {
    char hexbuf[12];
    // ...
    cert_entry = get_cert_entry(1, 1, 0, &cert);
    // ...
    get_cert_dn_component_maybe(&component, 5u, cert.buf + 4, cert.len - 4);

    while (component[0] != 6 || component[1] != 3 || TEE_MemCompare(component + 2, &g_common_name_oid, 3))
        ++component;

    cpuid = component[5];
    common_name_len = component[6];
    common_name = component + 7;
    out_buf_len = *out_buf_len_p;
    component += 7;
    if (out_buf_len < common_name_len) {
        SLog("%s: out_buf_len:%u is less than cert_entry'CN:%d\n", "[Error]", out_buf_len, common_name_len);
        goto LABEL_3;
    }
    TEE_MemMove(out_buf, common_name, common_name_len);
    *out_buf_len_p = common_name_len;
    // ...

    rndbuf = TEE_Malloc(4, 0);
    TEE_GenerateRandom(rndbuf, 4);
    hexbuf[0] = 0x2D;
    to_hex_string(rndbuf, 4, &hexbuf[1], 8u);
    TEE_MemMove(&out_buf[*out_buf_len_p], hexbuf, 9u);
    *out_buf_len_p += 9;
    // ...
}

OOB Access in get_soter_cpuid

The function get_soter_cpuid at 0x18510 can also trigger an OOB access on component if the certificate does not contain a common name. The while loop will continue incrementing component indefinitely as long as the common name OID is not found.

int get_soter_cpuid(char *out_buf, uint32_t *out_buf_len_p) {
    char hexbuf[12];
    // ...
    cert_entry = get_cert_entry(1, 1, 0, &cert);
    // ...
    get_cert_dn_component_maybe(&component, 5u, cert.buf + 4, cert.len - 4);

    while (component[0] != 6 || component[1] != 3 || TEE_MemCompare(component + 2, &g_common_name_oid, 3))
        ++component;
    // ...
}

Exploitation

We are going to exploit two of the vulnerabilities we mentioned to demonstrate that we can gain arbitrary read/write from these vulnerabilities. The vulnerabilities in question are the write of arbitrary data to sec_storage_data/PKI/ and the write of controlled params set in generate_keyblob.

Our Device Setup

The device we have developed an exploit for is a P40 Pro running the firmware update ELS-LGRP4-OVS_11.0.0.223.

The trustlet binary MD5 checksum is as follows:

HWELS:/ # md5sum /vendor/bin/86310d18-5659-47c9-b212-841a3ca4f814.sec
e5b15daf991407ee3c440b98b7e5cb23  /vendor/bin/86310d18-5659-47c9-b212-841a3ca4f814.sec

Huawei's TEE OS iTrustee implements a whitelist mechanism that only allows specific client applications (native binaries or APKs) to talk to a trusted application.

In our case, the HW_KEYMASTER TA can only be called by the following CAs:

  • /system/bin/keystore (uid 1017)
  • /system/bin/vold (uid 0)
  • /vendor/bin/atcmdserver (uid 0)
  • /vendor/bin/hw_cert_service (uid 0)
  • /vendor/bin/hw/vendor.huawei.hardware.sop@1.0-service (uid 1000)
  • /system/bin/tee_test_ut (uid 0)
  • com.huawei.systemserver (APK)
  • com.huawei.hwonlineprovisionservice (APK)

The authentication mechanism is implemented in 3 parts:

  • the teecd daemon, that implements the TEE Client API, checks which native binary/APK is talking to it and sends that information to the kernel driver;
  • the kernel driver ensures that it is talking to teecd, and forwards the information it received to the TEE OS;
  • the TEE OS verifies that the client application is in the TA's whitelist.

Since we did not want to bother with injecting code in one of these binaries, we chose to circumvent the authentication by patching the kernel driver to add the ability to impersonate any native binary/APK.

Arbitrary Read

To trigger the write of arbitrary data to the SFS, we start by importing an all-zeroes AES key (with the alias key) using the cmd_import_aes_key command (ID 0x10003). We then prepare our file data by encrypting it using AES-256-CCM with the all-zeroes key, an all-zeroes IV, and the SHA256 hash of 86310d18-5659-47c9-b212-841a3ca4f814\x00...16 times...\x00 as the associated data. Finally, we call the cmd_unwrap command (ID 0x1001) with the key alias, all-zeroes IV, \x00...16 times...\x00 associated data, and the encrypted file data and tag that we calculated previously.

int cmd_get(unsigned int paramTypes, TEE_Param params[4]) {
    // ...
    ret = get_keyblob_from_alias(
        &keyblob,
        &keyblob_size,
        params[0].memref.buffer,
        params[0].memref.size,
        params[2].value.a);
    // ...
    if (keyblob->magic == 0x534554
            && keyblob->version <= 0x12C
            && keyblob->keyblob_total_size == keyblob_size) {
        memcpy_s(
            params[1].memref.buffer,
            params[1].memref.size,
            keyblob + keyblob->key_off,
            keyblob->key_len);
        // ...
        params[1].memref.size = keyblob->key_len;
    }
    // ...
}

To trigger a relative read, we write a "fake" keyblob to the sec_storage_data/PKI/blob file, and call the cmd_get command (ID 8) with the same file name. This command will read the blob file and put its content into keyblob. Since we can control the entire content of the keyblob file, we can pass the checks and have the command call memcpy_s. memcpy_s will copy into params[1] from a user-controlled offset relative to the keyblob object, and with a user-controlled size. Since the keyblob is heap-allocated, we can leak the content of the heap.

Here is an excerpt of the content of the heap:

...
X + 0x80 : 58 f0 c0 03 d0 89 fe 00 67 70 64 2e 74 61 2e 61 X.......gpd.ta.a
X + 0x90 : 70 70 49 44 00 18 0d 31 86 59 56 c9 47 b2 12 84 ppID...1.YV.G...
X + 0xa0 : 1a 3c a4 f8 14 00 00 00 41 00 00 00 41 00 00 00 .<......A...A...
X + 0xb0 : d8 89 fe 00 02 00 00 00 01 00 00 00 04 00 00 00 ................
X + 0xc0 : 90 89 fe 00 10 8a fe 00 67 70 64 2e 74 61 2e 73 ........gpd.ta.s
X + 0xd0 : 69 6e 67 6c 65 49 6e 73 74 61 6e 63 65 00 25 73 ingleInstance.%s
X + 0xe0 : 20 25 00 00 00 00 00 00 41 00 00 00 41 00 00 00  %......A...A...
X + 0xf0 : 18 8a fe 00 02 00 00 00 01 00 00 00 04 00 00 00 ................
X + 0x100: d0 89 fe 00 50 8a fe 00 67 70 64 2e 74 61 2e 6d ....P...gpd.ta.m
X + 0x110: 75 6c 74 69 53 65 73 73 69 6f 6e 00 25 73 20 25 ultiSession.%s %
X + 0x120: 00 00 00 00 00 00 00 00 41 00 00 00 41 00 00 00 ........A...A...
X + 0x130: 58 8a fe 00 02 00 00 00 01 00 00 00 04 00 00 00 X...............
...

To be able to read from arbitrary addresses, we need to leak the address of the keyblob. When dumping the heap, we noticed that around the gpd.ta.appID string were two pointers (one next pointer at X + 0x84, one prev pointer at X + 0x100) with the same value (d0 89 fe 00 in the dump above). Thanks to these pointers, we can deduce the address of the object located between them (X + 0xc0 in the dump above), and consequently of our keyblob object.

We then used our arbitrary read, and the TALoader information leak, to find the base address of the trustlet. Finally, we use this address to dump the trustlet's memory as the final step of this demonstration.

Arbitrary Write

To trigger the arbitrary write to memory, we start by calling the cmd_on_user_event command (ID 0x30001) with event_type == 2. This will initialize the user keys that are required for the next step.

We then call the cmd_begin command (ID 3) with the following param set values in params[1]:

  • KM_TAG_VENDOR_TYPE: 1
  • KM_TAG_ASSETSTORE_ACCESSLIMITATION: 2
  • KM_TAG_PURPOSE: 0x41414141
  • KM_TAG_DERIVE_FACTOR: \x00...16 times...\x00
  • 0x90015FB9: \x00\x00\x00\x00

These values allow reaching the code path presented in the vulnerability details. We specify the address we want to write to in params[3].value_a and the size in params[3].value_b. When these values are copied, the size we provide is checked and we need to make sure it's big enough to contain the values in the param set buffer. Finally, to show that we control some of the data being written, we chose to set a custom KM_TAG_PURPOSE param value.

Note that if controlling more than 1/2/4/8 contiguous bytes of the data being written is needed, we could have also used a copyable tag of type KM_BYTES, like 0x900002C3, 0x900002C4, or 0x900003EA that contains out-of-line data.

You can find below the output of the exploit running on our device:

adb wait-for-device shell su root sh -c "\"LD_LIBRARY_PATH=/data/local/tmp /data/local/tmp/poc\""
object is at 10c0910
ta_base_addr = 39ae000
TA memory dump:
0x039ae000: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 .ELF............
0x039ae010: 03 00 28 00 01 00 00 00 0c 1d 00 00 34 00 00 00 ..(.........4...
0x039ae020: dc 81 0b 00 00 02 00 05 34 00 20 00 05 00 28 00 ........4. ...(.
0x039ae030: 11 00 10 00 01 00 00 00 00 00 00 00 00 00 00 00 ................
...
Writing a params set to an arbitrary address...
Reading the params set that was just written...
0x70000800: 05 00 00 00 01 00 00 20 00 00 00 00 41 41 41 41 ....... ....AAAA
0x70000810: 00 00 00 00 be 02 00 10 2c f8 a9 03 00 00 00 00 ........,.......
0x70000820: 04 fa a9 03 c1 02 00 30 00 00 00 00 00 00 00 00 .......0........
0x70000830: 00 00 00 00 c2 02 00 30 00 00 00 00 00 00 00 00 .......0........
...

Getting Code Execution

While it is not included in the exploit, it is possible to get code execution by replacing a function pointer/overwriting a return address, and chaining ROP gadgets. There are plenty of gadgets to choose from as the shared libraries (libc_shared_a32.so, libtee_shared_a32.so, libvendor_shared_a32.so, etc.) are mapped in the trustlet's address space.

For example, this could be used to make arbitrary syscalls and potentially further escalate privileges.

Affected Devices

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

  • Kirin 990: P40 Pro (ELS)

Please note that other models might have been affected.

Patch

Name Severity CVE Patch
Write of Arbitrary Data to sec_storage_data/PKI/ Critical CVE-2021-40017 September 2022
Write of Controlled Params Set in generate_keyblob Critical CVE-2021-40040 September 2022
Integer Overflow in ber_pop_front High CVE-2021-46887 May 2023
Stack Address Leak in cmd_verify_key Low N/A Fixed
Integer Overflow in ber_init Low N/A Fixed
Logic Issue in verify_root_cert Low N/A Fixed
Stack Buffer Overflow in get_soter_cpuid Low N/A Fixed
OOB Access in get_soter_cpuid Low N/A Fixed

Timeline

  • Nov. 24, 2021 - A vulnerability report is sent to Huawei PSIRT.
  • Jan. 12, 2022 - Huawei PSIRT acknowledges the vulnerability report.
  • May 01, 2023 - Huawei PSIRT states that these issues are fixed in the September 2022 and May 2023 updates.
  • From Nov. 30, 2022 to Jul, 19 2023 - We exchange regularly about the release of our advisories.
  • Jun. 21, 2023 - We inform Huawei PSIRT that some of the vulnerabilities are not patched.
  • Jul. 03, 2023 - Huawei PSIRT replies that they will be fixed in the August 2023 update.