Vulnerability Description

We came across a use-after-free (UAF) vulnerability in the camera driver of Qualcomm MSM 7x30 SoCs. The vulnerable camera driver provides a standard ioctl interface for user space programs to communicate. However, when processing the ioctl communication, the handler function might be triggered to reference a previously freed memory block, leading to a typical use-after-free scenario and making it possible for arbitrary code execution at the kernel level.

Specifically, the ioctl handler function, msm_set_crop(), located at drivers/media/video/msm/msm_camera.c uses copy_from_user() to collect the crop data structure provided by the user-level program (see Figure 1: line 1532).


Figure 1: msm_set_crop()

When the global variable sync->croplen is 0, crop.len sized memory block is kmalloc()ed. The global pointer sync->cropinfo is used for bookkeeping the newly allocated buffer (see Figure 1: line 1540). After preparing the memory buffer, another copy_from_user() is invoked to copy crop.len bytes of user-provided data into kernel space (see Figure 1: line 1546). If this operation fails, the memory buffer is kfree() (see Figure 1: line 1550). Otherwise, sync->croplen is set to get rid of the second kmalloc() call in line 1540. Also, the sync->croplen can be used to prevent buffer overflow when crop.len is larger than the size of the memory buffer (see Figure 1: line 1543).

Everything looks fine so far. The memory block is properly allocated, filled in with user-provided data, and released when something goes wrong in the data transfer operation. However, one thing is missed here. The bookkeeping variable sync->croplen is not reset when sync->cropinfo is kfree()ed (see Figure 1:line 1550) such that the next entry of msm_set_crop() could copy arbitrary content into the already released memory buffer. If a victim user allocates a buffer between the kfree() call and the second entry of msm_set_crop(), she could use an unreliable memory buffer which is manipulated by the caller of msm_set_crop().

Exploiting the Vulnerability

It is possible to execute the shellcode at the kernel level by exploiting this vulnerability. Here’s how it works. First, you need to choose a good victim slab which contains a function pointer. Besides, the kmalloc() path of that slab should be reachable from user space and the function pointer should be easily invoked from user space. For example, the open() handler of certain sysfs entry allocates a slab containing a pointer that points to a function table while the function pointers stored in the table can be invoked by reading the sysfs entry. This makes it a good target to attack.

Second, you need to put the victim slab into the kfree()ed area pointed by sync->cropinfo such that the msm_set_crop() would modify the content of the victim slab. The best way to achieve that is to allocate a bunch of victim slabs (e.g., open() the sysfs entry 100 times) right after the crafted msm_set_crop() ioctl call which kfree() the sync->cropinfo. This way, we can fill in one of the victim slab into the memory slot that will be USE after FREE. In Figure 2, we use an invalid address to let the GPU driver kfee() the slab which is kmalloc()ed in our first trip to msm_set_crop() (line 56-57). Right after that, we open() lots of sysfs entries and keep the file descriptors in the sys_fd[] array for later usage. This allows us to setup one of the victim slabs which is reachable from one of the sysfs file descriptors.


Figure 2: Setup the Victim Slab

Third, you need to prepare the shellcode and drop it into victim slab by another msm_set_crop() ioctl. As illustrated in Figure 3, we prepare the crop.info and trick the driver to write crop.len bytes into the victim slab (Figure 1: line 1546-1548).


Figure 3: Drop the Shellcode

Finally, you can trigger the shellcode by reading the sysfs entry (Figure 4).


Figure 4: Trigger the Shellcode

Acknowledgement

The finding was mainly credited to Chiachih Wu and Yanfeng Wang from Qihoo 360.

Disclosure Timeline

  • June 5, 2015: We reported the vulnerability to the corresponding vendor – Qualcomm Inc.
  • June 26, 2015: Qualcomm confirmed the presence of the reported vulnerability
  • August 13, 2015: Qualcomm notified us with the assigned CVE-2015-0568
  • August 21, 2015: Qualcomm disclosed the details of CVE-2015-0568 on Code Aurora Forum


Published

18 November 2015

Tags