Kernel Exploit for CVE-2016-6187 (Local Privilege Escalation) (affected versions: < 4.6.5)
Exploit was developed against a custom compiled 4.6 kernel + buildroot (kconfig coming soon)
and defeats KASLR
, SMEP
, SMAP
(KPTI
not available for 4.6)
The exploited vulnerability is a heap based single nullbyte overflow in one of AppArmor's LSM hooks (setprocattr). See https://nvd.nist.gov/vuln/detail/CVE-2016-6187
Initial measurements indicate a 94.44% chance of spawning a root shell. In 1 out of 18 cases, the exploit will oops the kernel. Further work is needed to fully stabilize the kernel after gaining root access.
We abuse the nullbyte overflow to corrupt the LSB of a freelist pointer in kmalloc-128
, effectively letting
it point to an already allocated object. After that, we free that allocated object, causing a double free scenario.
Now we can allocate two overlapping structures from the kmalloc-128
cache. The first structure is an object
with content that can be read back by userspace. For that, I have chosen ip6_sf_socklist
. The second structure,
which will be allocated on top of ip6_sf_socklist
should contain kernel pointers - rfkill_data
is a good fit.
It contains a heap pointer (empty rfkill_data.events
list) and a global data pointer to rfkill_fds
(rfkill_data.list.prev
).
Once rfkill_data
is allocated on top of ip6_sf_socklist
, we can retrieve the kernel pointers by calling
getsockopt(sock, IPPROTO_IPV6, MCAST_MSFILTER, gsf, &optlen)
. The kernel text base can then be derived from rfkill_fds
.
We then cause a second double free scenario, but this time we abuse the message queue's linking process to overwrite the freelist pointer
and trick the allocator to return us a chunk from kmalloc-96
when we actually request a chunk from kmalloc-128
.
We can then leverage the size mismatch to overflow into the freelist pointer of a kmalloc-96
chunk, giving us the primitive to
allocate arbitrary addresses. In our case, we go for the ptmx_fops
structure and overwrite the ptmx_fops.unlocked_ioctl
callback with a pointer to our stack pivot gadget.
Calling ioctl
on /dev/ptmx
triggers the pivot gadget and we pivot the stack to a fake stack in kmalloc-128
where we
already prepared our first stage rop chain.
Since we have limited space on the kmalloc-128
fake stack (we don't know if the next chunk will be on the same slab
), the task
of the first stage rop chain is to copy the second stage rop chain from userspace into the kernel dmesg log buffer (__log_buf
)
and to pivot the stack to __log_buf
.
The second stage rop chain then tries to repair the kernel (restoring ptmx_fops
, ...) before it calls commit_creds(prepare_kernel_cred(NULL))
to escalate privileges.
And finally it exits the kernelspace via iretq
and we return, with elevated privileges, to our execve("/bin/sh", 0, 0)
subroutine.
Unprivileged users must be able to at least open /dev/rfkill
. Some distros might
not permit that. I have checked Ubuntu and Fedora, both allow unprivileged users to open /dev/rfkill
(although Fedora is using SELinux instead of AppArmor)
The following section explains how to reproduce the local privilege escalation in qemu-x86_64
.
First, you will have to obtain an initrd
and bzImage
kernel. There are two ways:
The easiest and quickest way. You can find all the required components in prebuilt/
.
Or, alternatively, you could also build the components yourself. Simply fetch the
4.6 kernel and use
the provided x86_64_vuln_kern_defconfig
in config/
to compile the bzImage
.
For the initrd
you could, for example, use buildroot. Just make
sure to set the correct permissions for /dev/rfkill
in your init
chmod 664 /dev/rfkill
This is the same as in Ubuntu-16.04
and many other distros. Also do not forget to compile
the exploit and put it into your initrd
make
Once you have obtained the components, boot the vulnerable machine
./boot.sh <path/to/bzImage> <path/to/rootfs.cpio>
and execute the exploit
./exploit
You may need to run the exploit several times, depending on whether we have nulled out a LSB that was already zero. Also note, that there is still a slight chance (maybe 10%-20%?) of crashing after gaining root access - I am working on it.
Have fun.
- repair
ptmx_fops
to increase stability - repair
kmalloc-128
freelist to increase stability - repair message queue to increase stability
- automatically restart exploit when detecting failure
- add proper writeup
- publish kconfig, build instructions, ...
- code refactoring/cleanup