How To Patch and Protect Linux Kernel Zero Day Local Privilege Escalation Vulnerability CVE-2016-5195 [ 21/Oct/2016 ]

last updated in Categories , , , , ,

A very serious security problem has been found in the Linux kernel. A 0-day local privilege escalation vulnerability has existed for eleven years since 2005. This bug affects all sort of of Android or Linux kernel to escalate privileges. Any user can become root in less than 5 seconds. The bug has existed since Linux kernel version 2.6.22+. How do I fix this problem?

dirty-cow-logo
This bug is named as Dirty COW (CVE-2016-5195) is a privilege escalation vulnerability in the Linux Kernel. Exploitation of this bug does not leave any trace of anything abnormal happening to the logs. So you can not detect if someone has exploited this against your server.

What is CVE-2016-5195 bug?

From the project:

A race condition was found in the way the Linux kernel’s memory subsystem handled the copy-on-write (COW) breakage of private read-only memory mappings. An unprivileged local user could use this flaw to gain write access to otherwise read-only memory mappings and thus increase their privileges on the system.

A nasty bug for sure. Any local users can write to any file they can read, and present since at least Linux kernel version 2.6.22. Linus Torvalds explained:

This is an ancient bug that was actually attempted to be fixed once (badly) by me eleven years ago in commit 4ceb5db9757a (“Fix get_user_pages() race for write access”) but that was then undone due to problems on s390 by commit f33ea7f404e5 (“fix get_user_pages bug”).

In the meantime, the s390 situation has long been fixed, and we can now fix it by checking the pte_dirty() bit properly (and do it better). The s390 dirty bit was implemented in abf09bed3cce (“s390/mm: implement software dirty bits”) which made it into v3.9. Earlier kernels will have to look at the page state itself.

Also, the VM has become more scalable, and what used a purely theoretical race back then has become easier to trigger.

To fix it, we introduce a new internal FOLL_COW flag to mark the “yes, we already did a COW” rather than play racy games with FOLL_WRITE that is very fundamental, and then use the pte dirty flag to validate that the FOLL_COW flag is still valid.

A list of affected Linux distros (including VMs and containers that share the same kernel)

  1. Red Hat Enterprise Linux 7.x
  2. Red Hat Enterprise Linux 6.x
  3. Red Hat Enterprise Linux 5.x
  4. CentOS Linux 7.x
  5. CentOS Linux 6.x
  6. CentOS Linux 5.x
  7. Debian Linux wheezy
  8. Debian Linux jessie
  9. Debian Linux stretch
  10. Debian Linux sid
  11. Ubuntu Linux precise (LTS 12.04)
  12. Ubuntu Linux trusty
  13. Ubuntu Linux xenial (LTS 16.04)
  14. Ubuntu Linux yakkety
  15. Ubuntu Linux vivid/ubuntu-core
  16. SUSE Linux Enterprise 11 and 12.
  17. Openwrt

How do I fix CVE-2016-5195 on Linux?

Type the commands as per your Linux distro. You need to reboot the box. Before you apply patch, note down your current kernel version:
$ uname -a
$ uname -mrs

Sample outputs:

Linux 3.13.0-95-generic x86_64

Debian or Ubuntu Linux

$ sudo apt-get update && sudo apt-get upgrade && sudo apt-get dist-upgrade
Reboot the server:
$ sudo reboot

Related: Ubuntu Linux users can hotfix this Linux kernel bug without rebooting the server.

RHEL / CentOS Linux 5.x/6.x/7.x

$ sudo yum update
$ sudo reboot

RHEL / CentOS Linux 4.x

$ sudo up2date -u
$ sudo reboot

Suse Enterprise Linux or Opensuse Linux

To apply all needed patches to the system type:
# zypper patch
# reboot

Verification

You need to make sure your version number has changed:
$ uname -a
$ uname -r
$ uname -mrs

Determine if your system is vulnerable

For RHEL/CentOS Linux, use the following script:
$ wget https://access.redhat.com/sites/default/files/rh-cve-2016-5195_2.sh
$ bash rh-cve-2016-5195_2.sh

For all other distro try PoC (proof of concept exploit code)

Grab the PoC:
$ wget https://raw.githubusercontent.com/dirtycow/dirtycow.github.io/master/dirtyc0w.c
Run it as follows. First be root:
$ sudo -s
# echo this is not a test > foo

Run it as normal user:
$ gcc -lpthread dirtyc0w.c -o dirtyc0w
### ***[ If you get an error while compiling code, try ***] ###
$ gcc -pthread dirtyc0w.c -o dirtyc0w
$ ./dirtyc0w foo m00000000000000000
mmap 56123000
madvise 0
procselfmem 1800000000

$ cat foo
m00000000000000000

References:

Get 15% off on Linux Foundation certified SysAdmin, Progamming, Kubernetes/Containers and Open Stack certification & course. Use "SPLASH15" coupon code. Offer expires on August 27, 2018
training.linuxfoundation.org

Posted by: Vivek Gite

The author is the creator of nixCraft and a seasoned sysadmin, DevOps engineer, and a trainer for the Linux operating system/Unix shell scripting. Get the latest tutorials on SysAdmin, Linux/Unix and open source topics via RSS/XML feed or weekly email newsletter.

30 comment

  1. Wild bug, eh?

    I guess, there is no way to mitigate it without rebooting the box. How about SELinux or grsecurity? It can protect against this kind of bug? Right guys? If not why I should bother with grsecurity or SELinux or apparmor thingy?

    1. Because SELinux applies to the filesystem, not memory. COW(copy on write) is a bug in how memory is handled by the kernel. You are basically asking why you need a fire extinguisher when someone can kick down your door and clock you upside the head…

  2. After running

    After running this command gcc -lpthread dirtyc0w.c -o dirtyc0w, i got error :

    dirtyc0w.c:96:3: warning: format ‘%x’ expects argument of type ‘unsigned int’, but argument 2 has type ‘void *’ [-Wformat=]
    printf(“mmap %x\n\n”,map);
    ^
    /tmp/cc5X5rGq.o: In function `main’:

    1. Since it’s only a warning, it should still generate a binary file. I had some compilation warnings too but it worked nevertheless.

    1. I think you shouldn’t rely on the minimum version only. For example, under ubuntu and it’s derivatives (linux mint), kernel 3.13 will be patched, because it’s LTS (Long-Term Support) but kernels 3.14-3.19 will be not, because they reached End of Life in August 2016.
      So I would advise you check the support lifetime for your kernel.

  3. This is wrong – just a change of kernel version is not sufficient!

    Patched kernels have not migrated to all mirror sites.

  4. Hmmm, I ran

    sudo apt-get update && sudo apt-get upgrade && sudo apt-get dist-upgrade

    but my version (uname -mrs) remains unchanged

    The only thing that’s change is trusty from 14.04.1 to 14.04.5

    p.s. I get the same error trying to compile your dirtyc0w.c

  5. Even if you update kernel , it is still detecting as vulnerable. Partial mitigation may be a temporary fix but it appears to break syscall.ptrace function!
    My question is the same, do any one have any idea which is the minimum patched kernel version where we can say it is not vulnerable?

  6. Various websites (including this one) and news articles simply state that you can protect yourself on RHEL and CentOS by installing the latest updates (yum update; reboot). Which is currently not true, there is no update for RHEL 5 and 6.

  7. I think it would be helpful to indicate what kind of output from the test indicates a vulnerability, and what kind of output would indicate not being vulnerable.

    For example, here’s my output:


    # ./dirtyc0w foo m00000000000000000
    mmap b76f3000
    madvise 0
    procselfmem -100000000
    root@myhostname:/home/myusername# cat foo
    this is not a test

    I assume this means my system is not vulnerable as the file was not overwritten, but it might be helpful to readers to state explicitly what “I am vulnerable” looks like versus “I am not vulnerable.” My system was not patched, and yet the test looks good, so while I surmise that the above means I am “safe,” it seems strange that an unpatched version would come through as safe.

      1. Thank you Vivek. I get the same results when running as non-root.


        [user@host ~ ] $ ./dirtyc0w foo m00000000000000000
        mmap b76e5000
        madvise 0
        procselfmem -100000000

        [user@host ~ ] $ cat foo
        this is not a test

        My question to the author still stands. It would be helpful to see what the expected output would be for a vulnerable system, *and* the expected output for a non-vulnerable system. I don’t know how to interpret the above. As I said, I can _surmise_ that since the file wasn’t changed, it means I am not vulnerable, but I have not patched my system, and it is not a recently updated debian system.

        1. Thanks Vivek for the nice explanation, but I agree with LC as I am also facing the same thing.
          Which is, either I run the command ” ./dirtyc0w foo m00000000000000000 ” as a root or non root user in my non patched system, i cannot see that foo is modified.

        2. I think the author simply doesn’t know. He put a general article with no in-depth explanation, plus instructions for patching RHEL/CentOS didn’t work as there was no patch released. I wouldn’t expect any answer.

        3. File will not modify if your system is patched. If system is not patched the text ” this is not a test” from foo will be replaced by a dirtyc0w.c running as a non root user. The foo file will look as follows when your run cat foo on affected kernels:

           m00000000000000000
          1. Vivek, thank you for confirming what I suspected. If the file is not modified, it means the system is not vulnerable. If the file *is* modified, it means the system is vulnerable.

            I was just confused because I know I didn’t patch my system so I thought I must be misreading the output, and since the author didn’t post a positive and negative outcome I wasn’t sure if my assumptions were sound.

            Somehow either my system was patched by someone who is administering the cloud VMs, or my system simply happened to not be vulnerable to this bug (though that seems strange as it is ordinary debian distribution).

            Thanks again.

  8. Hi,

    I have followed the instructions above and updated my Ubuntu 12.04 system using:

    sudo apt-get update && sudo apt-get upgrade && sudo apt-get dist-upgrade

    I then run the test program, and I am still vulnerable.

    When I run uname -rv I get:
    3.5.0-54-generic #81~precise1-Ubuntu

    So the guys at Ubuntu say I have patched my system however running the test program shows I am still vulnerable.

    Anybody else hit the same issue?

    Have a question? Post it on our forum!