A mini-article about CVE-2017-18344 — an arbitrary-read vulnerability I found in the Linux kernel timer subsystem.
Contains a brief description of the
/etc/shadow leak exploit I wrote for this bug.
Originally posted as an announcement on the OSS-Security mailing list.
In November 2017, syzbot reported a global-out-of-bounds bug in the timer subsystem of the Linux kernel. This bug is exploitable and allows reading arbitrary kernel memory to leak keys, credentials, or other sensitive information. The impact of this bug is thus similar to Meltdown.
The bug only affects kernels that have
Many modern distributions enable these configs.
This bug has been fixed in Ubuntu 16.04 but still affects at least CentOS 7 at the time of this writing (at least 3.10.0-862.9.1.el7.x86_64, which I’ve checked). I haven’t checked any other distros.
|CVE-2017-18344 — MITRE|
I wrote a proof-of-concent exploit that allows:
- Reading arbitrary virtual or physical (within the physmap) memory,
- Dumping virtual memory that belongs to a particular process by its pid, and
- Searching physical memory for a pattern (only the start of each page, which is enough to locate
See the comment in the exploit source code for a usage example that shows how to leak
/etc/shadow on Ubuntu Xenial 4.13.0-38-generic.
The exploit bypasses KASLR and SMEP but doesn’t bypass SMAP.
The bug is that the
timer_create syscall doesn’t validate the
sigevent->sigev_notify value but uses it to address a global array of strings in
/proc/PID/timers is read.
By providing a large
sigev_notify value, I caused a global-out-of-bounds access that results in
nstr[notify & ~SIGEV_THREAD_ID] overflowing 8 bytes and ending up in the userspace.
I then mmaped the accessed userspace page and put a contolled address there, which allowed reading arbitrary kernel memory.
Since the kernel accessed the userspace there, the exploit attempt would be caught by SMAP.
Since kernel image location is randomized due to KASLR, I couldn’t know in advance which page exactly I should have mmapped.
However, since the kernel usually lies within a known address range
[0xffffffff81000000, ...), I could mmap a huge chunk of userspace memory that would catch the access wherever the kernel image is placed.
By filling half of the mapped memory with one pointer and the other half with another and reading
/proc/PID/timers to see which one got accessed, I could then bisect the exact location in the userspace where I could place a pointer to read kernel data.
To limit the RAM usage of the exploit, I chose to use the
memfd_create syscall to mmap two distinct chunks of memory over and over.
I could now calculate the kernel location based on this userspace address, but instead I decided to leak the first IDT entry (which is
divide_error) and calculate the kernel image address based on that.
The location of physmap is also randomized on the kernels built with
CONFIG_RANDOMIZE_MEMORY=y, so I read the
page_offset_base global variable value to find out the physmap location.
It should be possible to find the location of all required kernel symbols heuristically instead of hard coding offsets, but I haven’t explored this.
Now, I could read arbitrary physical memory through physmap.
/etc/shadow content always seems to be page-aligned, so I could search the beginning of each page for something like
root:!: and locate it in the physical memory.
I could also walk the list of running tasks starting with
init_task and dump memory that belongs to a particular task by walking its page tables.
Dumping and inspecting memory for
gnome-keyring-daemon, for example, allows finding out the user’s password.
See CVE-2017-18344 analysis & exploitation notes for a detailed write-up of an alternative approach to exploiting this bug.
I thought it would be interesting to see when certain Linux distros fixed this bug, as there was no CVE requested and assigned for a while after the bug was fixed.
Initially, I was only looking at Ubuntu 16.04. Here’s the related timeline:
- Nov 30, 2017 — Bug reported by syzbot
- Dec 15, 2017 — Mainline fix committed
- Feb 17, 2018 — Fix backported to the 4.4 stable kernel branch
- Mar 15, 2018 — Fix added to the Ubuntu Xenial 4.4 kernel branch
- Jul 25, 2018 — CVE requested
- Aug 2, 2018 — Notified linux-distros@
- Aug 2, 2018 — Posted on oss-security@
In this particular case of a somewhat “scary” bug, there was a window of 3.5 months between the bug being reported and the fixing commit reaching the Ubuntu Xenial 4.4 kernel branch. This gives some insight into how much time it usually takes for a fix to travel from upstream through stable into a distro kernel when there’s no CVE. Compared to the 14 days that distros are usually given to fix a security bug reported through linux-distros@, the 3.5 months seem rather long.
Then, I decided to take a look at the CentOS kernel. I was quite surprised to find out that this bug hasn’t been fixed there at all. I was under the impression that most Linux distros either follow stable kernel branches or monitor upstream commits for security-related fixes themselves. It seems that this is not the case. Perhaps this fix was missed because CentOS 7 kernel is based on the 3.10 kernel version, and the 3.10 stable kernel release stopped being supported just before the bug was found in November 2017.
And this is just this single bug. Right now, there are 700+ fixed bugs reported by syzbot and 200+ more that are still not fixed. Almost none of them have CVEs (so if anybody wants to practice requesting CVEs, go for it). There are also ~9000 fixes backported to the 4.4 stable kernel. Some of them are security-relevant and don’t have CVEs. On top of that, apparently, there are ~700 fixes that are missing in the 4.4 stable kernel.
It seems that a CVE is required for a particular security-related fix to end up in distro kernels, but there are no CVEs requested for most of the bugs that are being fixed. So there’s this inconsistency between the Linux kernel community that fixes the bugs without bothering about CVEs and the distros, which require CVEs to apply fixes to their kernels.
Just sharing some thoughts 🙂
💜 Thank you for reading!
🐱 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.