When you think about security in your operating system, the common notion is that windows is trash, Linux is extremely safe but very few users actually use Linux, and mac is quite safe but their price points aren’t always budget friendly.
But it was a cold day in hell when a huge vulnerability was discovered in an operating system that is pretty much one of the safest available.
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.
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.
However, 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 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.
Operating system security is crucial because an operating system is the legs upon which other programs stand on. If the operating system has a security vulnerability, everything contained within becomes potentially vulnerable. For example if an ATM machine’s operating system has a vulnerability such as Dirty Cow which is quite serious, that represents a great risk from a user standpoint as well since that vulnerability could be exploited by hackers to take money from tons of accounts.
Thankfully ethical hacking is gaining popularity, and everyday we see incredibly talented ethical hackers finding these vulnerabilities and helping companies develop systems with good security.