Resources

People often ask me "How did you learn how to hack?" The answer: by reading. This page is a collection of the blog posts and other articles that I have accumulated over the years of my journey. Enjoy!

Dirty Pipe Vulnerability - 800

Max Kellermann Posted 4 Years Ago
  • The author of this article is not a security researcher; they are a software developer. They found this vulnerability in the Linux kernel while trying to track down a software bug. In particular, a CRC at the end of a file kept getting corrupted and they couldn't figure out why. The corruption just kept on happening and they HAD to get to the bottom of it.
  • The corruption was happening within the CRC of a log file. The functionality works like this:
    1. Web service writes a ZIP header.
    2. Splice is used to send all compressed files. This syscall is used for copying data between one file to another in kernel space, which helps with I/O performance.
    3. write is called to write the directory file header. This is 50 4b 01 02 1e 03 14 00. This is EXACTLY the corruption that is happening.
  • After debugging this for a while, they came to the conclusion that this MUST be a kernel bug. They wrote up a quick proof of concept to demonstrate the bug; this was composed of two progams running simultanously. The first program was writing data to a file, acting as the the log splitter. The second program was running splice followed by a call to write, simulating the zip generator. Even though the write was NOT occuring on the call to splice, the string from the second write appeared to be in the file being read. Specifically, this problem existed in pipes.
  • The Linux kernels smallest unit of memory managed by the CPU is a page (4kB). If an application requests memory from the kernel, it will get a number of pages; all I/O happens with pages. For instance, if you read in a file, the kernel wil first copy a number of 4kB chunks form the hard disk into kernel memory called a page cache. A pipe is a unidirectional file descriptor used for inter-process communication. One end is for pushing (writing) data, while the other end of it pulls the data.
  • When using a pipe, a reference to a page cache entry is made via the data structure pipe_buffer. These objects have a reference to the backing memory page and have several flags for what permissions and things are allowed. The main bug is that the flags for this are NOT initialized when using pipes. In Linux 5.8, the flag PIPE_BUF_FLAG_CAN_MERGE was added, which indicates whether a page table is mergable or not. Practically, a poisoned set of flags, namely PIPE_BUF_FLAG_CAN_MERGE, allows data to be written to a page cache that the pipe does not own!
  • This can facilitate the ability to write and change the file data in the page cache even if it’s opened as read-only, giving a privilege escalation primitive. The data is never written to disk unless the page cache entry is considered dirty. Since the accidental write does not mark the page cache as dirty, the write to disk will never occur on disk; it will only happen in the cache. As a result, the file can be changed in memory, but never be written to disk, making this a real sly attack.
  • To exploit this vulnerability, the following steps need to happen:
    1. Create a pipe.
    2. Fill the pipe with arbitrary data. We want to set the PIPE_BUF_FLAG_CAN_MERGE flag in ALL of the entries.
    3. Drain the pipe (remove).
    4. Splice the data on the target file that is opened read only, just before the target offset that we want to overwrite.
    5. Write data into the pipe. This data will overwrite the cache file page instead of the data in the proepr buffer.
  • The author has a final note on this bug: "To make this vulnerability more interesting, it not only works without write permissions, it also works with immutable files, on read-only btrfs snapshots and on read-only mounts (including CD-ROM mounts). That is because the page cache is always writable (by the kernel), and writing to a pipe never checks any permissions."
  • To me, it's pretty crazy how the lack of initialization of a value caused such major problems. Even in the Linux kernel security hardened memory management system, there are security vulnerabilities. DayZeroSec has a good synopsis on this vulnerability as well.