
Análisis en profundidad y exploit de prueba de concepto para CVE-2022-0847 (DirtyPipe), una vulnerabilidad del kernel de Linux que permite la sobrescritura arbitraria de archivos y la escalada local de privilegios mediante indicadores de búfer de tubería no inicializados.
title: Análisis de la vulnerabilidad CVE-2022-0847 (escalada local de privilegios DirtyPipe) date: 2022-03-08 14:41:20 tags: - Escalada de privilegios en Linux categories: - Investigación de seguridad
CVE-2022-0847es una vulnerabilidad en el kernel deLinuxdesde5.8. Un atacante puede aprovecharla para sobrescribir los datos de cualquier archivo de solo lectura. De esta manera, los privilegios normales se elevan aroot, porque un proceso no privilegiado puede inyectar código en un proceso root.
CVE-2022-0847es similar aCVE-2016-5195 “Dirty Cow”(escalada de privilegios Dirty Cow) y es fácil de explotar; el autor de la vulnerabilidad la denominóDirty Pipe
Este blog se utiliza principalmente para documentar eventos de seguridad y artículos sobre vulnerabilidades, con el fin de que todos puedan aprender, intercambiar y realizar pruebas. Cualquier consecuencia o daño, directo o indirecto, causado por la difusión o el uso de la información o las herramientas proporcionadas en los artículos de este blog será responsabilidad exclusiva del propio usuario; el autor de los artículos no asume ninguna responsabilidad por ello.
Nivel de peligro: Alto
POC/EXP: Público
Versiones afectadas: linux kernel 5.8 y versiones posteriores
Nota: versiones seguras: kernel de Linux >= 5.16.11, kernel de Linux >= 5.15.25, kernel de Linux >= 5.10.102
Aquí se presentan brevemente los detalles de la vulnerabilidad.
Algunos conceptos:
Linux pipe: Semidúplex, el flujo de datos solo puede ir de un extremo al otro
pipe_buffer: Caché del pipe, se utiliza para almacenar temporalmente los datos escritos en el pipe; la lectura y la escritura se realizan en la caché del pipe
page: Marco de página, 4 kb, con una relación uno a uno con la caché del pipe
pipe_buf_operations: Se utiliza para almacenar el conjunto de operaciones de la caché del pipe
can_merge: Indicador de fusión; si la lectura/escritura general del pipe puede fusionarse, se establece en 1 para fusionar los datos en el búfer existente. Si se establece en 0, siempre se utiliza un nuevo segmento de página del pipe para los nuevos datos.
splice(): Mueve datos entre dos descriptores de archivo; al igual que la función sendfile( ), admite el pipe y es de copia cero. Vincula la caché de página del archivo con la caché del pipe, es decir, la escritura afecta a ambas al mismo tiempo; al comprobar los permisos, solo verifica si el archivo de origen de los datos tiene permiso de lectura, sin comprobación de permisos al escribir. La cadena de llamadas aproximada es:
// fs/splice.c
syscall --> do_splice --> do_splice_to --> splice_read(generic_file_splice_read()) --> call_read_iter(generic_file_read_iter)
// linux/mm/filemap.c
generic_file_read_iter --> filemap_read --> copy_folio_to_iter
// linux/lib/iov_iter.c
copy_folio_to_iter --> __copy_folio_to_iter --> copy_page_to_iter_pipe

Historia del desarrollo de la detección de "fusión" en el pipe de Linux:
Aquí analizamos brevemente el código según la introducción del autor.
Los primeros sistemas Linux coincidían con los conceptos presentados: existía el indicador can_merge, que se utilizaba para marcar si los nuevos datos podían escribirse en la caché del pipe ya existente.
Commit 5274f052e7b3 añadió la función splice(), pero la comprobación no cambió: seguía basándose en el indicador can_merge para determinar si la caché del pipe actual estaba disponible.


Commit 01e7187b4119 dejó de usar el indicador can_merge y pasó a comparar el puntero struct pipe_buf_operations, es decir, como solo el tipo anon_pipe_buf_ops permite escribir nuevos datos, basta con comprobar si es ese tipo.


Commit 241699cd72a8 añadió dos nuevas funciones que pueden asignar un nuevo struct pipe_buf_operations, pero no inicializan su indicador flags.

Commit f6dd975583bd transformó esta comparación de punteros en la comparación del indicador PIPE_BUF_FLAG_CAN_MERGE de cada búfer y permitió inyectar PIPE_BUF_FLAG_CAN_MERGE; al mismo tiempo, eliminó la definición y el uso de otros tipos de buf_ops.



Por lo tanto, el autor tiene la siguiente idea de explotación:
Análisis del Exp
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright 2022 CM4all GmbH / IONOS SE
*
* author: Max Kellermann <[email protected]>
*
* Proof-of-concept exploit for the Dirty Pipe
* vulnerability (CVE-2022-0847) caused by an uninitialized
* "pipe_buffer.flags" variable. It demonstrates how to overwrite any
* file contents in the page cache, even if the file is not permitted
* to be written, immutable or on a read-only mount.
*
* This exploit requires Linux 5.8 or later; the code path was made
* reachable by commit f6dd975583bd ("pipe: merge
* anon_pipe_buf*_ops"). The commit did not introduce the bug, it was
* there before, it just provided an easy way to exploit it.
*
* There are two major limitations of this exploit: the offset cannot
* be on a page boundary (it needs to write one byte before the offset
* to add a reference to this page to the pipe), and the write cannot
* cross a page boundary.
*
* Example: ./write_anything /root/.ssh/authorized_keys 1 $'\nssh-ed25519 AAA......\n'
*
* Further explanation: https://dirtypipe.cm4all.com/
*/
#define _GNU_SOURCE
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/user.h>
#ifndef PAGE_SIZE
#define PAGE_SIZE 4096
#endif