The vulnerability that we will be working on is CVE-2016-5195 also known as DirtyCOW which affects the Linux kernel of all Linux-based operating systems. The vulnerability is caused by exploiting a race condition in the implementation of a copy-on-write (COW) mechanism in the memory management subsystem. As a result of this, users without write-permissions can write to read-only files which allows them to modify files which they are not authorised to such as /etc/passwd
to obtain root permissions.
The COW mechanism is a mechanism which allows multiple processes to read from the same resource if the resource was not modified to reduce resource consumption. Only when the resource is modified will it be copied to a different location in memory. While we are not able to write to read-only files since we don’t have the approriate permissions, it is possible to create a copy of the file and write to the copy instead, such that the original is not being affected. In such cases, the COW mechanism will be triggered when we try to write to a read-only file if the conditions are met.
However, the COW mechanism is vulnerable because of the fact that it is a non-atomic operation that consists of 3 steps: (1) making a copy; (2) update the reference in the process’ virtual address space that points at the copy.; and (3) writing to where the reference is pointing to (copy). If the user is only trying to read the file, the COW mechanism is not triggered and the reference points to the original file in disk instead of creating a copy.
This mechanism operates with the objective of saving resources. If the file isn’t being modified, multiple processes can read the file without having to create a copy for every process that’s just trying to read. As such, a copy is only created when a process attempts to write to a file that it only has read-permissions to, which is the COW mechanism.
The problem lies where when the three steps interleave with other processes/threads, which can create a race condition between the thread that is performing the COW mechanism and other threads. If the other threads manage to interrupt the COW mechanism between steps (2) and (3), it is possible for the thread to influence the output of the COW mechanism. This vulnerability can be exploited using madvise()
which is used to give “advice” to the kernel on a specific part of the memory. To be specific, the MADV_DONTNEED
advice.
What happens with this advice is that it tells the kernel that the specified memory range will not be needed by the calling process in the near future, and thus the kernel can free up the physical memory used by those pages. This would then trigger an update of the reference (the same one as in (2)) to point back to the original file in disk.
The reason why the fact that the COW mechanism is non-atomic is the cause of the vulnerability is that if it wasn’t non-atomic, and all 3 steps execute together, then even if madvise(MADV_DONTNEED)
frees up the resources, it should not have any negative impact since the next write would trigger the COW mechanism again and create a new file even if the copy had been “discarded” by madvise()
.