A few months ago while auditing the MSM Android kernel for bugs (MSM is the Linux kernel branch for the Qualcomm SoC on Android), I found an interesting issue. When I found some time, I reported it to the Android Security Team for responsible disclosure.
This issue was aknowledged by the Android Security Team and it got a
High severity rating, id
#198231 and label
In this blog post we will explore the issue, its limitations and the exploitation.
One of my old test devices is a Nexus 7 2013 which runs a Qualcomm SoC, so Android ships with a MSM kernel for this model.
You can find the kernel sources for the msm kernel of this device directly from Google Android repository.
The codename for Nexus 7 2013 device in AOSP is
master branch of the kernel is empty, you will need to switch to your branch of interest to actually get the sources that interest you, for example checking out the
A very interesting feature and attack surface of the Linux kernel, especially for Android devices, is the
debug filesystem or
debugfs for short.
In another post, we discussed another Linux kernel attack surface which is Linux devices under
Like with the kernel devices under
/dev/, you can touch several interesting kernel code paths by interacting with the filesystem that
Quote from the Linux kernel documentation under
Documentation/filesystems/debugfs.txt, which I think it is very representative:
As you can imagine from this quote,
debugfs is a nice place to hunt for infoleaks and memory corruption bugs. In addition to being able to read information produced by the kernel,
debugfs has a lot of entries which give you the ability to pass data to the kernel — potentially allowing for memory corruption.
debugfs is mounted in
/sys/kernel/debug and usually on Android devices it’s symlinked to
Hunting for bugs:
This time instead of starting with searching the filesystem entries, I took the opposite approach; inspection of the kernel code. I started looking for dangerous functions and macros such as
writel and backtracing the usage to see if they could be abused by
devices from userspace.
writel macro/function is implementation dependant but it can be considered equivalent to a function
void writel(unsigned value, address); which would write
As you can guess, if we can control
value we have an arbitrary kernel write, which is a very powerful primitive for an attacker.
After not too much looking into the code, I backtraced a
writel call to a
/sys/kernel/debug/mddi/reg -rw-r--r-- root root
As you can see this
debugfs entry is writable by root (which lowers a lot the impact of this issue), and readable by any user.
Before going into the code, let me show how simple the PoC is, which is IMO very funny, running it as root:
echo "41414141 42424242" > /sys/kernel/debug/mddi/reg
The device will kernel panic and reboot, then you can get a summary of the kernel panic from
Panic summary of arbitrary write:
As you can see the kernel paniced by trying to write
0x41414141, specified by the attacker.
Notice that you can panic the kernel also by reading from this as unprivileged user:
This may possibly lead to an
infoleak as an unpriviledged user if we can avoid causing a kernel panic and getting the kernel to return us meaningful memory. I haven’t yet found to the to time to see if there is a feasible code path that would allow this behavior, but feel free to try.
We will go backward, starting from the code creating the
/sys/kernel/debug/mddi/reg entry in the file
In this code the kernel will create the
debugfs entry, and assign the
pmdh_fops file operation handler (same file:
pmdh_reg_write you will see the function that reads our data from the write to the filesystem (same file:
mddi_reg_write will call the dangerous
writel as we mentioned without proper bound checkings (still in
For the read operation, it is a very similar codepath:
You can check it out if you have time, I haven’t explored the possibility of memory leaks, which can potentially be used by unprivileged users.
I didn’t code any PoC except the trigger. An arbitrary kernel memory write is probably the best primitive you can have for kernel exploitation, so it is very easy to use.
You can overwrite a function pointer for example and hijack the kernel to get kernel code execution.
Kernel code execution compared to userspace root only is starting to get significant on Android, if you want to try to exploit the
TrustZone, you most likely need to execute code in kernel context.
You must be
root to utilize this vulnerability. One example usecase of this may be: if you gain code execution to a root service, but its
SELinux context is restricted, you could utilize a vulnerability such as this to completely disable
SELinux to enjoy full, unrestricted access to the system.
- 2016/1/5 The issues is reported to the Android Security Team with the bug reporting form.
- 2016/1/14 The Android Security Team reviewed the issue and bumped the severity to
- 2016/1/27 Suggested a fix to the Android Security Team. If nobody uses this interface the best solution would be to disable it in release kernels by adding a new configuration option to make it independant from the global
debugfs, in this way it can still be used for debugging by recompiling the kernel with the new configuration enabled. An additional fix can be deployed by sanitizing and checking the input from userspace, making sure it’s inside the expected areas. Since this feature it’s heavily platform dependant I think the best candidate to make this kind of fix is
Qualcommitself since they know the internals.
- 2016/3/1 Further discussion with Android Security about disclosure and deadlines. I suggested it would be nice to stick with their 90 days disclosure policy that Google applies at Project Zero for other vendors.
- 2016/3/31 This issue got
- 2016/4/15 I got awarded a bug bounty from Google, more than I expected actually considering the issue.
- 2016/5/2 The issue is fixed and credited in the May 2016 Nexus Security Bulletin
debugfsis a good attack surface, especially on Android devices.
- OEMs should probably remember to disable this debug features in production, since I don’t see a real usage from them in a production build.
- If they leave them enable, they should be more careful to what they expose, and do proper input checks.