“Dirty COW” is a serious Linux kernel vulnerability that was recently discovered to have been lurking in the code for more than nine years. It is pretty much guaranteed that if you’re using any version of Linux or Android released in the past decade, you’re vulnerable. But what is this vulnerability, exactly, and how does it work? To understand this, it’s helpful to illustrate it using a popular tourist scam.
Have you ever played the game of shells? It’s traditionally played with a pea and three walnut shells — hence the name — and it is found on touristy street corners all over the world. Besides the shells themselves, it also involves the gullible “mark” (that’s you), the con artist (that’s the person moving the shells), and, invariably, one or several scammer’s assistants in the crowd pretending to be fellow tourists. At first, the accomplices “bait” the crowd by winning many bets in a row, so you assume the game is pretty easy to win — after all, you can clearly see the ball move from shell to shell, and it’s always revealed right where you thought it would be.
So, you step forward, win a few rounds, and then decide to go for a big bet, usually goaded by the scammers. At just the right time you’re momentarily distracted by the scammer’s assistants, causing you to look away for a mere fraction of a second — but that’s enough for the scammer to palm the pea or quickly move it to another shell. When you call your bet, the empty shell is triumphantly revealed and you walk away relieved of your cash.
In computing terms, you just experienced a “race condition.” You saw the ball go under a specific shell (checked for required condition), and therefore that’s the one you pointed at (performed the action). However, unknown to you, between the check and the action the situation has changed, causing the initial condition to no longer be true. In real life, you were probably only out of a couple of hundred bucks, but in computing world race conditions can lead to truly bad outcomes.
Race conditions are usually solved by requiring that the check and the action are performed as part of an atomic transaction, locking the state of the system so that the initial condition cannot be modified until the action is completed. Think of it as putting your foot on the shell right after you see the pea go under it — to prevent the scammer from palming or moving it while you are distracted (though I don’t suggest you try this unless you’re ready to get into a fistfight with the scammer and their accomplices).
Unfortunately, one such race condition was recently discovered in the part of the Linux Kernel that is responsible for memory mapping. Linux uses the “Change on Write” (COW) approach to reduce unnecessary duplication of memory objects. If you are a programmer, imagine you have the following code:
a = ‘COW’ b = a
Even though there are two variables here, they both point at the same memory object — since there is no need to take up twice the amount of RAM for two identical values. Next, the OS will wait until the value of the duplicate object is actually modified:
b += ‘ Dirty’
At this point, Linux will do the following (I’m simplifying for clarity):
allocate memory for the new, modified version of the object
read the original contents of the object being duplicated (‘COW’)
perform any required changes to it (append ‘ Dirty’)
write modified contents into the newly allocated area of memory
Unfortunately, there is a race condition between step 2 and step 4 which tricks the memory mapper to write the modified contents into the original memory range instead of the newly allocated area, such that instead of modifying memory belonging to “b” we end up modifying “a”.
Just like any other POSIX system, Linux implements “Discretionary Access Controls” (DAC), which relies on a framework of users and groups to grant or deny access to various parts of the OS. The grant permission can be read-only, or read-write. For example, as a non-privileged user you should be able to read “/bin/bash” in order to start a shell session when you log in, but not write to it. Only a privileged account (e.g. “root”) should be able to modify this file — otherwise any malicious user could replace the bash binary with a modified version that, for example, logs all passwords or starts up a backdoor.
The race condition described above allows the attacker to bypass this permissions framework by tricking the COW mechanism to modify the original read-only objects instead of their copies. In other words, a carefully crafted attack can indeed replace “/bin/bash” with a malicious version by an unprivileged user. This vulnerability has been assigned both the boring name (“CVE-2016-5195”), and the now-customary branded name of “Dirty COW.”
The really bad news is that this race condition has been present in the kernel for over 9 years, which is a very long time when it comes to computing. It is pretty much guaranteed that if you’re using any version of Linux or Android released in the past decade, you’re vulnerable.
Triggering this exploit is not as trivial as running a simple “cp” operation and putting any kind of modified binary in place. That said, given enough time and perseverance, we should assume that attackers will come up with cookie-cutter exploits that will allow them to elevate privileges (i.e. “become root”) on any unpatched system where they are able to freely execute arbitrary code. It is imperative that all Linux systems are patched as soon as possible — and a full reboot will be required, unless you have some kind of live patching solution available to you (if you don’t already know whether you can live-patch, then you probably cannot, as it’s not a widely used technology yet).
There is a fix available in the upstream kernel, and, at the time of writing this article, the distributions are starting to release updated packages. You should be closely monitoring your distribution’s release alerts and apply any outstanding kernel errata as soon as it becomes available. The same applies to any Android devices you may have.
If you cannot update and reboot your system right away, there are some mitigation mechanisms available to you while you wait (see this Red Hat Bugzilla entry for more details). It is important to note that the STAP method will only mitigate against known proof of concept exploits and is not generic enough to be considered a good long-term fix. Unfortunately, “Dirty COW” is not the kind of bug that can be prevented (much) by SELinux, AppArmor or any other RBAC solution, nor is it mitigated by PaX/GrSecurity hardening patches.
As I said earlier, in order to exploit the “Dirty COW” bug, the attacker must first be able to execute arbitrary code on the system. This, in itself, is bad enough — even if an attacker is not able to gain immediate root-level privilege, being able to execute arbitrary code gives them a massive foothold on your infrastructure and allows them a pivot point to reach your internal networks.
In fact, you should always assume that there are bad bugs lurking in the kernel that we do not yet know about (but the attackers do). Kees Cook in his blog about security bug lifetimes points out that vulnerabilities are usually fixed long after they are first introduced — many of them lurking in the code for years. Really bad bugs the caliber of the “Dirty COW” are worth hundreds of thousands of dollars on the black market, and you should always assume that an attacker who is able to execute arbitrary code on your systems will eventually be able to escalate their privileges and gain root access. Efforts like the “Kernel Self Protection Project” can help reduce the impact of some of these lurking bugs, but not all of them — for example, race conditions are particularly tricky to guard against and can be devastating in their scope of impact.
Therefore, any mitigation for the “Dirty COW” and other privilege escalation bugs should really be considered a part of a comprehensive defense-in-depth strategy that would work to keep attackers as far away as possible from being able to execute arbitrary code on your systems. Before they even get close to the kernel stack, the attackers should have to first defeat your network firewalls, your intrusion prevention systems, your web filters, and the RBAC protections around your daemons.
Taken altogether, these technologies will provide your systems with a great deal of herd immunity to ensure that no single exploit like the “Dirty COW” can bring your whole infrastructure to its tipping point.
Learn more about how to secure Linux systems through The Linux Foundation’s online, self-paced course Linux Security Fundamentals.