A mini-article about CVE-2017-6074 — a double-free vulnerability I found in the Linux kernel DCCP sockets. Contains a brief description of the LPE exploit I wrote for this bug. Originally posted as an announcement on the OSS-Security mailing list.
While fuzzing the Linux kernel with syzkaller, I found a double-free bug in the DCCP sockets. This bug can be exploited to gain kernel code execution from an unprivileged process.
The bug was likely introduced in the first kernel release with DCCP support: 2.6.14 from October 2005. However, the oldest kernel version that I checked to be vulnerable is 2.6.18 from September 2006.
Together with the network subsystem maintainers, I fixed the bug with 5edabca9d4cf (“dccp: fix freeing skb too early for IPV6_RECVPKTINFO”) committed on February 17th, 2017.
The kernel needs to be built with
CONFIG_IP_DCCP enabled to be vulnerable.
Many modern distributions enable this option by default.
In the current DCCP implementation, an skb for a
DCCP_PKT_REQUEST packet is forcibly freed via
dccp_v6_conn_request() successfully returns.
IPV6_RECVPKTINFO is set on a socket, the address of the skb is saved to
ireq->pktopts and the ref count for skb is incremented in
dccp_v6_conn_request() to mark that the skb is still in use.
Nevertheless, the skb still gets freed in
This bug is technically a use-after-free followed by a double-free.
The kernel frees the skb in
dccp_rcv_state_process(), and then keeps using it until freeing again when destroying the socket due to
There’s more going on under the hood, but this is the essential part.
The fix is to call
consume_skb(), which accounts for
skb->users, instead of doing
goto discard and therefore calling
I wrote a proof-of-concept exploit that gets root on Ubuntu 16.04 with the 4.4.0-62-generic kernel. The exploit includes SMEP and SMAP bypasses.
As I mentioned, the bug causes both a use-after-free and a double-free.
The use-after-free happens on
skb->data, which are allocated and freed one after another.
Exploiting this use-after-free allows overwriting
skb->data with arbitrary data.
Targeting the double-free, however, is more powerful. It allows controlling what object gets overwritten by turning the double-free into another use-after-free:
// The first free: kfree(dccp_skb) // Another object allocated on the same place as dccp_skb: some_object = kmalloc() // The second free; effectively frees some_object: kfree(dccp_skb)
This way, I could get a use-after-free on
As I could control what object that would be, I could overwrite its contents with arbitrary data by using some of the kernel slab spraying techniques.
If the overwritten object had any triggerable function pointers, I could get to execute arbitrary code within the kernel.
To control the execution flow, I applied the same technique that I used for exploiting a double-free in the Linux kernel USB MIDI driver.
skb->data, since it has
skb_shared_info struct at the end.
skb_shared_info->destructor_arg->callback is a function pointer, which is triggered by
My exploit puts the
ubuf_info struct for
destructor_arg and the shellcode for getting root into userspace, points
callback to the shellcode, and triggers it.
However, as the data is placed in userspace, the exploit gets detected by SMAP and SMEP.
I decided to disable these protections using the idea from the CVE-2016-8655 exploit by Philip Pettersson.
Phillip’s technique relies on overwriting a
packet_sock struct, which has a
timer_list field deep inside of it.
timer_list contains both a callback and its argument.
packet_sock, overwrote the
timer_list field, and scheduled the timer.
native_write_cr4 as the callback and a fake CR4 value with both SMEP and SMAP bits disabled as its argument.
Note that CVE-2016-8655 used a use-after-free on the
packet_sock struct, but in my case, I made a use-after-free happen by leveraging a double-free.
The exploit is not very reliable, but I don’t want to spend any more time on it. The kernel can crash due to a memory corruption if it fails to reallocate objects in time or in the correct order. I, however, managed to make it work in three different environments (including two VMs and one physical machine).
💜 Thank you for reading!
- 15 Feb, 2017 — Bug reported to firstname.lastname@example.org
- 16 Feb, 2017 — Patch submitted to netdev@
- 17 Feb, 2017 — Mainline fix is committed
- 18 Feb, 2017 — Notification sent to linux-distros@
- 22 Feb, 2017 — Public announcement sent to oss-security@
- 26 Feb, 2017 — Write-up and exploit published
🐱 About me
I’m a security researcher and a software engineer focusing on the Linux kernel.
I contributed to several security-related Linux kernel subsystems and tools: KASAN — a fast dynamic bug detector, syzkaller — a production-grade kernel fuzzer, and Arm Memory Tagging Extension — an exploit mitigation.
I also wrote a few Linux kernel exploits for the bugs I found.