Introduction:
A privilege escalation vulnerability in Linux Kernel has been discovered by Phil Pester. The bug has been in existence since version 2.6.22 which was released in 2007 and has been fixed on Oct 18 2016. The bug allows an unprivileged authenticated local user to gain write access to read only memory mappings. A number of PoC’s have been disclosed that show how the bug is exploited in different ways (PoC for Android OS is also present).
When a file, which exists in the physical memory is mapped within a processes memory space, it is considered to be a byte-for-byte correlation, this allows the process to treat the mapping as if it were the physical memory. The mapped file can be either shared with other processes , where updates to the mapping are visible to other processes or set as private which creates a copy-on-write mapping.
Copy-on-write is a resource management technique and is implemented in various scenarios. If a resource is duplicated but not modified, it is not necessary to create a new resource. This we will reduce the number of unmodified copies. In lay man terms different processes refer the same mapped file as long as the contents of the file are not modified. If a change occurs a new copy of the file is created for that process, this reduces memory consumption.
The vulnerability exists as a race condition in the way the Linux Kernel memory management handles duplication due to Copy-On-Write for private read only memory mappings. The exploits encountered in the wild rely on racing write operation on /proc/self/mem and ‘madvise‘ system call on a mmapped file (Target file).
As per the Linux kernel commit logs this vulnerability was documented during a fix for ‘get_user_pages‘ but its practical implications were not guaranteed. But the recent entries show that this race condition is easier to trigger. In this article we will focus more on the race condition between write operation on /proc/self/mem and ‘madvise‘ system call. /proc is virtual file system used by the kernel to keep track of run-time system information. Each of the sub directories within the /proc represents a process ID and the files within them contain its state, configuration and memory stats.
The PoC for the vulnerability is available here. The PoC takes two arguments the name of the target file and the content to be written. Basically we have two threads, first making the ‘madvise’ system call on a loop.
The second performing a write operation on the mapped file by directly referencing its address in the /proc/self/mem.
The madvise() system call is used by application to advice the kernel on how it will use the mapped or shared memory within its memory space. This will help the kernel to use the appropriate read-ahead and caching mechanism. If we observe we use MADV_DONTNEED flag, this informs the kernel that the application is finished with the address space for now and can be freed. However, if any access operations are encountered, the kernel will repopulate the contents of the memory pages based on the latest contents of the mmaped file. This is the crux of the exploit on one hand we are telling the kernel that we are finished with the memory pages and can be flushed and on the hand we are continuously writing to the memory pages of the same mapped file. This will cause the kernel to reload a new copy of the original file through mapping.
Most of the time the kernel will be able to handle this scenario, however if the concurrent operations are repeated enough number of times a break in the copy on write cycle occurs. Which will result in the application to be able to write to the read only file.
Exploit:
We will demonstrate the exploit on an un-patched Ubuntu 16.04 LTS with kernel version 4.4.0-21.
Next we are going to create a file owned by the root user account.
Executing PoC:
We can see in the image above, the contents of the root owned file is being overwritten by unprivileged user account.
Fix:
A new internal flag ‘FOLL_COW’ was introduced in ‘include/linux/mm.h‘ to fix the issue, the FOLL_COW flag is used in conjunction with the pte dirty flag to validate the completion of copy on write operation. Previously the kernel relied on the FOLL_WRITE flag. The details about this fix can be found here.
Conclusion:
We request our customer to scan using the following QID’s
QID | Title |
169351 | OpenSuSE Security Update for kernel (openSUSE-SU-2016:2649-1) |
169343 | OpenSuSE Security Update for the Linux Kernel (openSUSE-SU-2016:2625-1) |
256077 | CentOS Security Update for kernel (CESA-2016:2124) |
256076 | CentOS Security Update for kernel (CESA-2016:2105) |
236134 | Red Hat Update for kernel (RHSA-2016:2128) (Dirty Cow) |
236133 | Red Hat Update for kernel (RHSA-2016:2124) (Dirty Cow) |
236132 | Red Hat Update for kernel (RHSA-2016:2120) (Dirty Cow) |
157280 | Oracle Enterprise Linux Security Update for kernel (ELSA-2016-2124) (Dirty Cow) |
157279 | Oracle Enterprise Linux Security Update for kernel (ELSA-2016-2105) (Dirty Cow) |
236130 | Red Hat Update for kernel (RHSA-2016:2118) (Dirty Cow) |
236129 | Red Hat Update for kernel-rt (RHSA-2016:2110) (Dirty Cow) |
236128 | Red Hat Update for kernel-rt (RHSA-2016:2107) (Dirty Cow) |
236127 | Red Hat Update for kernel (RHSA-2016:2106) (Dirty Cow) |
236126 | Red Hat Update for kernel (RHSA-2016:2105) (Dirty Cow) |
256075 | CentOS Security Update for kernel (CESA-2016:2098) (Dirty Cow) |
169314 | SUSE Enterprise Linux Security Update for the Linux Kernel (SUSE-SU-2016:2592-1) (Dirty Cow) |
169312 | SUSE Enterprise Linux Security Update for the Linux Kernel (SUSE-SU-2016:2585-1) (Dirty Cow) |
169287 | OpenSuSE Security Update for the Linux Kernel (openSUSE-SU-2016:2584-1) (Dirty Cow) |
169286 | OpenSuSE Security Update for the Linux Kernel (openSUSE-SU-2016:2583-1) (Dirty Cow) |
370172 | Linux Kernel Privilege Escalation Vulnerability (Dirty Cow) |
276206 | Fedora Security Update for kernel (FEDORA-2016-db4b75b352) (Dirty Cow) |
276205 | Fedora Security Update for kernel (FEDORA-2016-c3558808cd) (Dirty Cow) |
276204 | Fedora Security Update for kernel (FEDORA-2016-c8a0c7eece) (Dirty Cow) |
175865 | Debian Security Update for linux (DSA 3696-1) (Dirty Cow) |
157278 | Oracle Enterprise Linux Security Update for kernel (ELSA-2016-2098) (Dirty Cow) |
236124 | Red Hat Update for kernel (RHSA-2016:2098) (Dirty Cow) |
350912 | Amazon Linux Security Advisory for kernel: ALAS-2016-757 (Dirty Cow) |
196605 | Ubuntu Security Notification for Linux Vulnerability (USN-3107-1) (Dirty Cow) |
196604 | Ubuntu Security Notification for Linux Vulnerability (USN-3106-1) (Dirty Cow) |
196603 | Ubuntu Security Notification for Linux Vulnerability (USN-3105-1) (Dirty Cow) |
196602 | Ubuntu Security Notification for Linux Vulnerability (USN-3104-1) (Dirty Cow) |
155355 | Oracle Enterprise Linux Security Update for Unbreakable Enterprise kernel (ELSA-2016-3633) |
References:RedHat has provided a shell script to detect if the OS is vulnerable or not it also checks for the presence of the corresponding patch.
CVE-2016-5195
DirtyCow-VulnerabilityDetails
Linux Kernel Commit Logs
/proc