CVE: |
|
Vendor: |
|
Device: |
Nexus 6 |
Affected Component: |
Widevine |
Publication Date: |
March 2023 |
Credits: |
CyberIntel Team |
1. Description
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 PRDiagProvisionDataHandler()
command.
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-2015-6639
...
}
else {
// data_len is not zero
// filePathBuffer @ 0x2e0fc
// fullFilePathBuffer @ 0x2e5fc
memzero(&fullFilePathBuffer, 0x80);
pcVar2 = strncpy(&fullFilePathBuffer, &persist_prefix, 0x80);
sVar3 = strlen(&filePathBuffer);
sVar4 = strlen(&persist_prefix);
memcpy(&fullFilePathBuffer + sVar4, &filePathBuffer, sVar3);
sVar3 = strlen(&filePathBuffer);
pcVar2[&fullFilePathBuffer + sVar3] = '/';
sVar4 = strlen(&filePathBuffer);
memcpy(&filePathBuffer + 1 + pcVar2 + sVar3, &filePathBuffer, sVar4);
iVar5 = PRDiagPruneTrailingSlashes(&fullFilePathBuffer);
if (msg_buf_size + iVar5 < 0x80) {
if (iVar5 >= 1) {
(&fullFilePathBuffer)[iVar5] = '/';
memcpy(&fullFilePathBuffer + 1 + iVar5, 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 not zero, it constructs a file path
by concatenating two strings: persist_prefix
and filePathBuffer
, which are global buffers
stored in the data segment. The resulting SFS file path is stored in the fullFilePathBuffer
variable, which is used later in the function to write the provisioning data.
The persist_prefix
string is initialized with the value "/persist/data/"
by default,
and the filePathBuffer
is initially empty, so that the total length of the final string afer
calling PRDiagPruneTrailingSlashes()
is 13. After that, the contents of the user-controlled
msg_buf
are appended to fullFilePathBuffer
after checking if the length does not exeed 128
bytes.
The bug is present in this check. Since the addition is done using a signed integer,
this calculation causes an integer overflow if the sum of msg_buf_size
and the string length
of fullFilePathBuffer
is large enough, resulting in a negative value and bypassing the if
statement. Consequently, the subsequent memory copy can cause a buffer overflow:
The range of values for msg_buf_size
that can cause a buffer overflow extends from
-13 (0xfffffff3
) to -1 (0xffffffff
).
The user has full control over the value of msg_buf_size
and the contents of the
msg_buf
buffer in the Request Buffer.
Since there are 13 specific and large msg_buf_size
values that can cause the buffer to overflow,
a memory copy operation with any of these values will exceed the bounds of the data segment,
resulting in an invalid memory access and potentially causing the application to crash.
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 = 0xffffffff;
req->payload.data_len = 1;
The Commmand ID 0x50004
is specified to invoke PRDiagProvisionDataHandler()
, and
the PRDiagParseAndStoreData
operation is being specified. In addition, msg_buf_size
is set
to 0xffffffff
so that the sum of msg_buf_size + iVar5
overflows
producing a small value, bypassing the check and eventually calling memcpy()
producing the
buffer overflow. When this request is sent to Widevine, it ends up crashing.
<5>widevine: "PRDiagProvisionDataHandler: pTzMsg 0xfff00008, reqlen 10240"
<5>widevine: "PRDiagParseAndStoreData: begins!"
<5>widevine: "PRDiagPruneTrailingSlashes: pPath 0x2e5fc"
<5>widevine: "PRDiagPruneTrailingSlashes: ends!"
*** crash ***
The Android log in normal world reports that qseecom_send_cmd
has failed with error -22,
which means APP_FAULTED:
<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
The reason it crashes is because the large memory copy ends up writing to unmapped memory due to exceeding the data segment.