This article is more than 1 year old
Dirty COW explained: Get a moooo-ve on and patch Linux root hole
Widespread flaw can be easily exploited to hijack PCs, servers, gizmos, phones
Code dive Patch your Linux-powered systems, phones and gadgets as soon as possible, if you can, to kill off a kernel-level flaw affecting nearly every distro of the open-source operating system.
Dubbed Dirty COW, the privilege-escalation vulnerability potentially allows any installed application, or malicious code smuggled onto a box, to gain root-level access and completely hijack the device.
The programming bug gets its name from the copy-on-write mechanism in the Linux kernel; the implementation is so broken, programs can set up a race condition to tamper with what should be a read-only root-owned executable mapped into memory. The changes are then committed to storage, allowing a non-privileged user to alter root-owned files and setuid executables – and at this point, it's game over.
While the flaw is not by itself a gravely serious or uncommon condition – Microsoft fixes priv-esc bugs in Windows practically every month – this vulnerability could prove particularly troublesome: it has been present in the Linux kernel since version 2.6.22 in 2007, and it is very easy to reliably exploit. We're told it is also present in Android, which is powered by the Linux kernel.
Crucially, exploit code to gain administrative control of devices is being used in the wild against internet-facing systems. And a version is now available to infosec professionals. A non-complete proof-of-concept version can be found here that tampers with a file that only root should be able to edit.
Earlier this week, Linux kernel boss Linus Torvalds admitted he had tried to fix the issue, unsuccessfully, 11 years ago, and then left it alone because at the time it was hard to trigger. Since then, the bug has become far more exploitable due to changes in the kernel's design.
According to a website dedicated to Dirty COW, a patch for the Linux kernel has been developed, and major vendors including Red Hat, Debian and Ubuntu have already released fixes for their respective Linux flavors.
Running the usual software update mechanisms, such as Debian's
apt-get, will fetch and install the patches. Don't forget to reboot after to pick up the new kernel.
Unfortunately, builds of the vulnerable kernel at the heart of countless millions of routers, Internet-of-Things gadgets and other embedded devices remain vulnerable – and many will be difficult to patch. Most people won't even know they've got a security risk sitting next to them at home.
"Even though the actual code fix may appear trivial, the Linux team is the expert in fixing it properly, so the fixed version or newer should be used," the site says, meaning you should only apply the patch yourself if you know what you're doing – otherwise, leave it to the experts.
"If this is not possible, software developers can recompile Linux with the fix applied."
The vulnerability, designated CVE-2016-5195, was discovered by security researcher Phil Oester. At least one exploit targeting the flaw has been found in the wild.
Dirty COW's disclosure continues the tradition of branding high-profile security flaws, something that was not lost on the group. "It would have been fantastic to eschew this ridiculousness, because we all make fun of branded vulnerabilities too, but this was not the right time to make that stand," they muse.
"So we created a website, an online shop, a Twitter account, and used a logo that a professional designer created."
How did it all go wrong?
Copy-on-write is used to streamline the memory management in an operating system. Among other things, it allows running programs to share common data in memory until one of them wants to privately alter that data. At that point the kernel copies the data to another page in memory so just that one process can affect it – hence the name, copy-on-write (CoW).
The exploit works by racing Linux's CoW mechanism. First, you have to open a root-owned executable as read-only and mmap() it to memory as a private mapping. The executable is now mapped into your process space. The executable has to be readable by the process's user to do this.
Meanwhile, you repeatedly call madvise() on that mapping with
MADV_DONTNEED set, which tells the kernel you don't actually intend to use the memory.
Then in another thread within the same process, you open
/proc/self/mem with read-write access. This is a special file that allows a process to access its own virtual memory as if it was a file. Using normal seek and write operations, you then repeatedly overwrite part of your own memory that's mapped to the root-owned executable. The overwrite shouldn't affect the executable on disk.
So now, your process has the read-only binary mapped in as a private read-only object, one thread is spamming
madvise() on that read-only object, and another thread is writing to that read-only object. Writing to that memory object should trigger a CoW: the touched page of the executable will be altered only in the process's memory – not the actual underlying root-owned file that's mapped in.
However, due to the aforementioned bug, the kernel performs the CoW operation but then allows the process to write to the read-only mapped executable anyway. These changes are committed to disk by the kernel, which is bad news.
This happens because, due to a race with
madvise(), the kernel does not fully break the executable from the process's private memory. The process writes to the read-only object, triggers a page access fault, and the fault is handled by allocating a new page containing a copy of the underlying file and privately mapping it into the process. So far, so good.
madvise() tells the kernel to discard the pages holding the mapped area. Calling this while writing to
/proc/self/mem will eventually lead to an inconsistent state where the pages holding the mapped executable are removed and a write is attempted to the memory area. A write that should go to the private pages will instead alter the mapped object. These changes are committed by the kernel to storage.
This can be exploited to alter a root-owned setuid binary so that it, for example, spawns a root-owned shell. It is possible to combine this exploit with
ptrace to make it more reliable, although it is not essential.
The fix – which changes just two lines and introduces a single-line inlined function – sets a flag that signals a CoW operation has occurred, preventing the underlying page holding the executable from being unlocked and written to. ®
Additional technical reporting by Chris Williams.