Hey CyberSec community,
welcome back to my blog-post. Today I am going to be covering about “Dirty-Cow” attack which is an interesting case of the race condition vulnerability.
It was found in the Linux kernel since September 2007 however, it was discovered and exploited in October 2016. When we hear this attack which was being one of the perilous exploit to affect all Linux-based operating systems, including Android-based as well. This attack has a flow to gain fully access the root privilege by exploiting the vulnerability. The most precarious method of this exploit was that it resides in the code of copy-on-write(COW) inside Linux kernel sounds creepy, does it not? The threat actor is able to modify any protected file, even though these files are only readable to them (/etc/password/passwd).
Lets dive deeper.
In order to be able to understand this attack, we shall know what mmap() function does in this scenario. If you want to be informed what Linux kernel is, i will share some links where you can read it.
Memory Mapping Using mmap()
So as to be able to understand Dirty-Cow vulnerability, we need to first understand how memory mamping works. Linux users are able to use manual page to get more information about this POSIX-compliant system.
mmap() creates a new mapping in the virtual address space of the
calling process. The starting address for the new mapping is
specified in addr. The length argument specifies the length of
the mapping (which must be greater than 0).
The default mapping type for mmap() is file-backed mapping, which maps an area of a process’s virtual memory to files; reading from the mapped are causes the file to be read.
This code was written by Wenliang Du, how mmap() was being coded.
Let me explain what this code does, this code can also be found in the Linux manual page.
- The first argument specifies that starting address for the mapped memory; if the argument is NULL
- The second argument specifies the size of the mapped memory.
- The third argument specifies whether the memory is readable. PROT_READ and PROT_WRITE flags. If the file is opened with the O_RDONLY flag(read-only)
4.The fourth argument determines whether an update to the mapping is visible.
5.The fifth argument specifies the file that needs to be mapped
MAP_SHARED , MAP_PRIVATE and Copy on Write
The mmap() system call creates a new mapping in the virtual address space of the calling process. mmap is a very common system call in user space, whether it is allocating memory, reading and writing large files, linking dynamic library files, or sharing memory between multiple processes.
MAP_SHARED: create a shared mapping area. Multiple processes can map the same file in this way, and the modified content will be synchronized to the disk file.
MAP_PRIVATE: Creates a private mapping for copy-on-write. Multiple processes can map the same file privately, and the modifications are not synchronized to disk.
Lets imagine that the process wants to have a private copy of a file, and it does not want any update to the private copy to affect the orginal file. When the process creates a private copy, the contents in the orginal memory need to be copied to the private memory. Since we know that this process takes time to copy memory, the copy action is often delayed until it is needed. MAP_PRIVATE still points to the shared physical memory which is called “the master copy” initially, if the process does not need to write to the mapped memory, there is no need to have a private copy.
MAP_PRIVATE to MMAP also create “copy on write” but what does it term mean?
Copy On Write
Any write to the region is reflected only in this process’s memory; other processes that map the same file won’t see the changes. Instead of writing directly to a page shared by all processes, the process writes to a private copy of this page. Which is also an optimization technique that allows virtual pages of memory in different processes to map. I forget to that when a parent process create a child process using FORK() system call, which is meant to have its own private memory.
Mapping Read-Only Files
I have not exactly explained about Dirty-Cow attack yet, let me explain. It involves mapping read-only(if you want to read more about this mapping file, you can check the manual page) we should be able to understand its behavior. I was willing to demonstrate this process but my Ubuntu 12.04 is not working properly 🙁 by this attack we are able to change our file to owner/group to root. Any numbers are acceptable which is written inside our file such as 1,2,3….
- In this illustration I am going to be still using Wenliang Du’s code*
Let me demonstrate what this code is, we map /aaa obviously into read-only memory. Are we able to do that no.. Because it has a memory protection, we cannot directly write to this memory however, we are able to write to it via proc file system, which is a special file system in UNIX-like operating system. For more detail check the manual page.
Through /proc/self/mem, a process is able to use file operations, such as read(),write, and lseek() system call. We ought to move the file pointer to the fifth byte from beginning of the mapped memory. What do we expect? The write operation shall trigger copy on write because of MAP_PRIVATE option when /aaa is mapped to memory.
The Dirty Cow Vulnerability
You came here to read about this attack right…? Well you are right 🙂 but before the explanation of this attack I must explain the process that is being done while this attack is in used. I have illustrated write() system call which can be used to write to the mapped memory.
The system call works as follows and has to perfom three essential steps:
- It makes a copy of the mapped memory.
- It updates the page table, means that the virtual memory will point to the newly created physical memory.
- It writes to the memory.
These three essential steps must be done. Are you thinking what the problem is? Now let me explain. The problem occurs between step 2 and step 3 because; step 2 changes the page table of the process. When step 2, thus if virtual memory points to the physical memory and if nothing else happens than step 3 will be perfomed. Write() system will succesfully write to the private copy of the mapped memory.
For sake of simplicity I am not going to deeper.
Lets dive into the exploitation part of Dirty Cow vulnerabilty.
Exploiting The Dirty Cow Vulnerability
As I mentioned I was willing to show you how this attack appears in our terminal due to the error of ubuntu, ı was able to show that nonetheless, will fix that later..
I will briefly explain How This race condition vulnerability gains the root privilege. This CVE-2016–5195 allows us to modify any file as long as we have the read permission on the file.
I will explain step by step how it works…
Selecting /etc/passwd as Target File
To be perfomed our task we need to select /etc/passwd to modify it, this file is world-readable, but non root will not be able to modify it. This address of Linux contains users’s account information, what it records for each user.
Each of the above record contains seven colon-separated fields, the interest of this field is on the third field, which specifies the user ID value assigned to a user, its also critical to security. The root user as we expected has a value of 0 which is threated by the system as root.
Have you seen UID 1000 above this image at the third column (rootkit) this user does not have the root privilege, but this version of kali has alread been patched thus the vulnerability will not be happen. The main idea behind the scenario is to create a user obviously.
We created a user called cow, and the record of this user will be added to /etc/passwd.
Set Up The Memory Mapping And Threats
We first need to map /etc/passwd into memory, we only have read permission on the file so we are only able to map it to read-only memory. Our goal is to write to this mapped memory, no to its copy. We need to code two additional threads.
I am going to be using Wenliang’s Du code.
To use our script we need to find the position where the record of cow account is. You might not be familiar with strstr() that means finding the string of “cow” and then we start two threads.
The job is replacing 0000 instead of 1001 and the job of the write thread listed in the following is to replace the string cow:x:1001 in the memory with cow:x:0000. We know that the mapped memory is of COW type.
The Madvise Thread
The madvise is useful because it does not do only one thing: it repudiates the private copy of the mapped memory, thus the page table is able to point back to the orginal mapped memory.
If the write() and the madvise() system calls are invoked thus when is invoked only after the other is finished, we know that write() operation will always be performed on the private copy, and will never be able to modify the target file.
The DirtyCow race condition attack works only inside the Linux kernel, it does not work on Windows enviroment. When I will summary this attack I would only say that this attack exists in the implementation of the copy-on-write logic involves memory mapping. When this process works Linux will want to ensure that if the process writes to the memory when yes.. It will write to a private copy of the memory. System will have been compromised :))
This attack is dangerous because when the Linux has not been patched, you are able to modify users’s records and you can change the users’s ID to be 0 in order to be associated in the root group. 0 means — -> root
Thanks for reading this blog. Please support,like,share, and follow me for more awesome exploits, hacking, and other stuffs…
Ahmet Göker | CryptAnalyst | Exploit researcher | Malware Researcher | CTF player.
You can follow me on:
Linkedin : https://www.linkedin.com/in/ahmetg%C3%B6ker/