В ядре Linux выявлена уязвимость (CVE-2022-0847), позволяющая перезаписать содержимое страничного кэша для любых файлов, в том числе находящихся в режиме только для чтения, открытых с флагом O_RDONLY или размещённых в файловых системах, примонтированных в режиме только для чтения. С практической стороны, уязвимость может применяться для подстановки кода в произвольные процессы или искажения данных в открываемых файлах. Например, можно изменить содержимое файла authorized_keys для процесса sshd. Для тестирования доступен прототип эксплоита.
Проблеме присвоено кодовое имя Dirty Pipe, по аналогии с выявленной в 2016 году критической уязвимостью Dirty COW. Отмечается, что по уровню опасности Dirty Pipe находится на одном уровне с Dirty COW, но значительно проще в эксплутации. Уязвимость была выявлена в ходе разбора жалоб о периодическом повреждении загружаемых по сети файлов в системе, загружающей сжатые архивы с log-сервера (37 повреждений за 3 месяца на нагруженной системе), при подготовке которых использовалась операция splice() и неименнованные каналы.
Уязвимость проявляется начиная с ядра Linux 5.8, выпущенного в августе 2020 года, т.е. присутствует в Debian 11, но не затрагивает базовое ядро в Ubuntu 20.04 LTS. Ядра RHEL 8.x и openSUSE/SUSE 15 изначально основаны на старых ветках, но не исключено, что вызывавшее проблему изменение было бэкпоритировано в них (точных данных пока нет). Проследить за публикацией обновлений пакетов в дистрибутивах можно на данных страницах: Debian, SUSE, Ubuntu, RHEL, Fedora, Gentoo, Arch Linux. Исправление уязвимости предложено в выпусках 5.16.11, 5.15.25 и 5.10.102. Исправление также включено в ядро, используемое в платформе Android.
Уязвимость вызвана отсутствием инициализации значения “buf->flags” в коде функций copy_page_to_iter_pipe() и push_pipe(), при том, что память при выделении структуры не очищается и при определённых манипуляциях с неименованными каналами в “buf->flags” может оказаться значение от другой операции. Используя данную особенность непривилегированный локальный пользователь может добиться появления во флаге значения PIPE_BUF_FLAG_CAN_MERGE, которое позволяет организовать перезапись данных в страничном кэше через простую запись новых данных в специально подготовленный неименованный канал (pipe).
Для атаки целевой файл должен быть доступен на чтение, при этом так как при записи в pipe не проверяются права доступа замена в страничном кэше может быть произведена в том числе и для файлов, находящихся в разделах, примонтированных только для чтения (например, для файлов c CD-ROM). После замены информации в страничном кэше процесс при чтении данных из файла получит не фактические, а подменённые данные.
Эксплуатация сводится к созданию неименованного канала и наполнению его произвольными данными для того, чтобы добиться выставления флага PIPE_BUF_FLAG_CAN_MERGE во всех связанных с ним кольцевых структурах. Далее данные читаются из канала, но флаг остаётся выставленных во всех экземплярах структуры pipe_buffer в кольцевых структурах pipe_inode_info. Затем выполняется вызов splice() для чтения данных из целевого файла в неименованный канал, начиная с требуемого смещения. При записи данных в этот неименованный канал из-за выставленного флага PIPE_BUF_FLAG_CAN_MERGE данные в страничном кэше будут перезаписаны, вместо создания нового экземпляра структуры pipe_buffer.