<--

Google Android Synaptics Touchscreen Heap Overflow #2

Aleph Research Advisory

Identifier

Severity

High

Products

  1. Nexus 5X
  2. Nexus 6P
  3. Nexus 9
  4. Android One
  5. Pixel
  6. Pixel XL

Vulnerable Version

Verified on Nexus 9 6.0.1/MOB30W, 7.0/NRD90M, 7.0/NRD90R

Mitigation

Apply the December 2016 Android Security Patches.

Technical Details

Due to lenient SELinux and DAC policy, vulnerable Synaptics DSX (touchscreen driver) sysfs file entires are exposed to an attacker that executes code within the mediaserver context on Android M 6.0.1 and system_server, bluetooth, nfc contexts on Android N 7.0 (or any other SELinux domain that has target type sysfs with the open and write permissions on file class).

The vulnerability has been found in the synaptics_dsx_fw_update.c.

On module initialization, a fixed-size heap buffer is created:

static int synaptics_rmi4_fwu_init(struct synaptics_rmi4_data *rmi4_data)
{
    [...]
    fwu->image_name = kzalloc(MAX_IMAGE_NAME_LEN, GFP_KERNEL);
    if (!fwu->image_name) {
        dev_err(rmi4_data->pdev->dev.parent,
               "%s: Failed to alloc mem for image name\n",
             __func__);
        retval = -ENOMEM;
        goto exit_free_fwu;
    }
    [...]
}

where MAX_IMAGE_NAME_LEN equals 256. fwu->imagename can be controlled from userspace by the write syscall – see CVE-2016-8394. But, in fwu_go_nogo() (a function that is called within the flashing firmware flow), on certain conditions (header->contains_firmware_id=0), the attacker copy most of the contents of fwu->image_name to a heap allocated buffer of size 10 (MAX_FIRMWARE_ID_LEN=10), causing a heap overrun:

static enum flash_area fwu_go_nogo(struct image_header_data *header)
 {
    [...]
    char *strptr;
    char *firmware_id;
    [...]
    /* Get image firmware ID */
    if (header->contains_firmware_id) {
        image_fw_id = header->firmware_id;
    } else {
        strptr = strstr(fwu->image_name, "PR");
    if (!strptr) {
        [...]
        goto exit;
    }

    strptr += 2;
    firmware_id = kzalloc(MAX_FIRMWARE_ID_LEN, GFP_KERNEL);
    while (strptr[index] >= '0' && strptr[index] <= '9') {
        firmware_id[index] = strptr[index];
        index++;
    }
    [...]
}

Timeline

Credit

External References