A newly disclosed vulnerability in the Linux kernel could allow an attacker to write any data into an arbitrary file and gain elevated privileges. The bug affects the major Linux distributions going back to version 5.8 and Android, but a fix was included in the latest Linux kernel and Android releases in late February.
Many vulnerabilities are discovered by researchers who are digging into a particular app or code base, looking for potential issues. But this flaw (CVE-2022-0847) has an unusual origin story. It began in February 2021 when Max Kellermann received a support ticket from a customer of IONOS, the hosting provider where he works. The customer was having an issue decompressing nightly log files, and Kellermann discovered a corrupt file on the log server. He found a cyclic redundancy check (CRC) error in the file, which he fixed and then moved on. The same issue happened several more times in the next couple of months, and Kellermann found each time that the contents of the file looked correct, save for the CRC error.
After quite a bit of digging and investigation, Kellermann eventually found some commonalities among all of the corrupted files on the server.
“I compared all known-corrupt files and discovered, to my surprise, that all of them had the same CRC32 and the same “file length” value. Always the same CRC - this implies that this cannot be the result of a CRC calculation. With corrupt data, we would see different (but wrong) CRC values. For hours, I stared holes into the code but could not find an explanation. Then I stared at these 8 bytes. Eventually, I realized that 50 4b is ASCII for “P” and “K”. “PK”, that’s how all ZIP headers start,” Kellermann said in a post explaining the bug.
“My first flash of inspiration why it’s always the last day of the month which gets corrupted. When a website owner downloads the access log, the server starts with the first day of the month, then the second day, and so on. Of course, the last day of the month is sent at the end; the last day of the month is always followed by the “PK” header. That’s why it’s more likely to corrupt the last day.”
Kellermann spent several more hours trying to work out the root of the issue and finally settled on the fact that it had to be a bug in the Linux kernel. He later narrowed it down to an issue with the way that the kernel handled pipe buffer flags.
“First, some data gets written into the pipe, then lots of files get spliced, creating page cache references. Randomly, those may or may not have PIPE_BUF_FLAG_CAN_MERGE set. If yes, then the write() call that writes the central directory file header will be written to the page cache of the last compressed file,” Kellermann said.
“But why only the first 8 bytes of that header? Actually, all of the header gets copied to the page cache, but this operation does not increase the file size. The original file had only 8 bytes of “unspliced” space at the end, and only those bytes can be overwritten. The rest of the page is unused from the page cache’s perspective.”
The end result of exploiting the vulnerability, which Kellermann named Dirty Pipe, is the ability to write arbitrary data into a target file. The attacker must have read permissions, and there are some other constraints, as well. But Kellermann said exploiting the vulnerability is not difficult. He published a proof-of-concept exploit and other researchers have also confirmed the ease of exploitation.
“This affects everybody who runs untrusted code. The exploit I published is very reliable and easy to use,” Kellermann said in an email.
The vulnerability is fixed in Linux 5.16.11, 5.15.25, and 5.10.102. Google merged a fix for the bug into the Android code base on Feb. 24.