Skip to content
KitploitKITPLOIT
ToolsExploitsBlog
Log in
Einreichen
ToolsExploitsBlog
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.

FeedsKontaktDatenschutz© 2026 Kitploit

Tool-Verzeichnis

Kategorien

Alle Kategorien anzeigen
Loading categories
CVE-2022-0847 — Proof-of-concept-Exploit und detaillierte Analyse der Linux-Kernel-Privilege-Escalation-Schwachstelle CVE-2022-0847 (Dirty Pipe), einschließlich einer Docker-Umgebung zum Testen und Debuggen. | Kitploit
Tools/GitHubGitHub/chenaotian/cve-2022-0847
Privilege EscalationSchwachstellenanalyseExploitationLernen & BildungBinary-ExploitationLabs & Praxis
GitHubchenaotian/cve-2022-0847

CVE-2022-0847

Proof-of-concept-Exploit und detaillierte Analyse der Linux-Kernel-Privilege-Escalation-Schwachstelle CVE-2022-0847 (Dirty Pipe), einschließlich einer Docker-Umgebung zum Testen und Debuggen.

Repository anzeigen
25767vor 4 JahrenVon Kitploit geprüft

Beliebteste

Alle anzeigen →

Entdecken Sie die meistgenutzten Tools unserer Community.

Alle Tools erkunden

Durchsuchen Sie unsere Tool-Sammlung

Alle Tools anzeigen →
Teilen

CVE-2022-0847 Dirty Pipe Linux-Kernel-Privilege-Escalation-Analyse

[toc]

Dieser Artikel wurde zuerst im Huawei Security WeChat-Konto veröffentlicht; dies ist die Blog-Version (vollständiger).

Erstveröffentlichungslink: https://mp.weixin.qq.com/s/6VhWBOzJ7uu80nzFxe5jpg

Einführung in die Schwachstelle

  • Schwachstellenkennung: CVE-2022-0847 (Alias: Dirty Pipe)
  • Betroffenes Produkt: Linux-Kernel – splice-Syscall
  • Betroffene Versionen: Eingeführt in Linux 5.8 mit Patch f6dd975583bd, behoben in 5.16.11, 5.15.25, 5.10.102
  • Auswirkung: Es kann in jede lesbare Datei ein Inhalt von höchstens einer Seite geschrieben werden (ausreichend), was eine lokale Rechteausweitung ermöglicht.

Umgebung einrichten

Docker für die Schwachstellenanalyse: chenaotian/cve-2022-0847 (falls du nicht darauf zugreifen kannst, habe ich ihn noch nicht hochgeladen)

Enthält:

  • einen kompilierten, verwundbaren und debug-fähigen Kernel 5.13
  • qemu, gdb, Linux-Kernel-5.13-Quellcode
  • Exploit

Starten:

cd ~/cve-2022-0847
gcc exp.c -o exp --static && cp exp ./rootfs && cd rootfs
find . | cpio -o --format=newc > ../rootfs.img
cd ../ 
./boot.sh

Debuggen:

gdb ./vmlinux
target remote :10086
directory /root/linux-5.13
b do_splice
b copy_page_to_iter_pipe 
b pipe_write
ignore 3 15
...
p *(struct pipe_inode_info *) pipe
p (struct pipe_buffer)pipe->bufs[0]

Schwachstellenprinzip

Der kurze Kern der Schwachstelle: Durch den Aufruf der splice-Funktion kann eine Datei per „Zero-Copy“ in die pipe übertragen werden. Die Zero-Copy auf Code-Ebene bedeutet, dass direkt der Datei-Cache-Page (Page Cache) als buf-Seite der pipe verwendet wird. Dabei wird jedoch eine Variable nicht initialisiert, wodurch der Datei-Cache-Page im späteren pipe-Kanal wie ein normaler pipe-Cache-Page behandelt und „weitergeschrieben“ und damit manipuliert werden kann. In diesem Fall markiert der Kernel diesen Cache-Page jedoch nicht als „dirty“, sodass er kurzfristig (bis zum nächsten Neustart o. Ä.) nicht auf die Festplatte geschrieben wird. In dieser Zeit verwenden alle Zugriffe auf die Datei den manipulierten Datei-Cache-Page, wodurch eine „kurzzeitige Möglichkeit, in beliebige lesbare Dateien zu schreiben“ erreicht wird. Damit ist eine lokale Rechteausweitung möglich.

Ort der Schwachstelle

Laut Patch liegt die Schwachstelle in der Funktion copy_page_to_iter_pipe; dort wurde eine Initialisierung von buf->flags hinzugefügt. Es handelt sich also um eine Schwachstelle durch fehlende Variableninitialisierung.

image-20220308170149137

Der Aufruf von copy_page_to_iter_pipe erfolgt im splice-Systemaufruf. Die splice-Funktion (Systemaufruf) überträgt den Dateiinhalt über eine „Zero-Copy“-Methode in die Pipe. Das ist leistungsfähiger als die herkömmliche Methode, den Dateiinhalt direkt in die Pipe zu schreiben. Details folgen weiter unten.

Pipe-Prinzip und pipe_write

Zunächst: Die Schwachstelle trägt den Alias Dirty Pipe. Daher werfen wir einen Blick auf die Pipe (pipe). Die pipe ist ein vom Kernel bereitgestellter Kommunikationskanal, der über die Funktionen pipe/pipe2 erstellt wird und zwei Dateideskriptoren zurückgibt – einen zum Senden von Daten und einen zum Empfangen, ähnlich wie die beiden Enden einer Röhre. Auf die genaue Verwendung gehe ich hier nicht weiter ein.

image-20220309124007780

Kurz zur Implementierung im Kernel: Der Pipe-Cache hat normalerweise eine Gesamtlänge von 65536 Bytes und wird in Seiten verwaltet, insgesamt 16 Seiten (eine Seite 4096 Bytes). Die Seiten sind nicht zusammenhängend, sondern werden über ein Array verwaltet und bilden eine ringförmige Liste. Es werden zwei Listenzeiger gepflegt: einer zum Schreiben (pipe->head) und einer zum Lesen (pipe->tail). Hier wird hauptsächlich die pipe_write-Funktion analysiert:

linux-5.13\fs\pipe.c : 400 : pipe_write

static ssize_t
pipe_write(struct kiocb *iocb, struct iov_iter *from)
{
	struct file *filp = iocb->ki_filp;
	struct pipe_inode_info *pipe = filp->private_data;
	unsigned int head;
	ssize_t ret = 0;
	size_t total_len = iov_iter_count(from);
	ssize_t chars;
	bool was_empty = false;
	bool wake_next_writer = false;

	··· ···
    ··· ···
	head = pipe->head;
	was_empty = pipe_empty(head, pipe->tail);
	chars = total_len & (PAGE_SIZE-1);
	if (chars && !was_empty) { 
        //[1]pipe 缓存不为空,则尝试是否能从当前最后一页"接着"写
		unsigned int mask = pipe->ring_size - 1;
		struct pipe_buffer *buf = &pipe->bufs[(head - 1) & mask];
		int offset = buf->offset + buf->len; 

		if ((buf->flags & PIPE_BUF_FLAG_CAN_MERGE) &&
		    offset + chars <= PAGE_SIZE) { 
            /*[2]关键,如果PIPE_BUF_FLAG_CAN_MERGE 标志位存在,代表该页允许接着写
             *如果写入长度不会跨页,则接着写,否则直接另起一页 */
			ret = pipe_buf_confirm(pipe, buf);
			···
			ret = copy_page_from_iter(buf->page, offset, chars, from);
			···
			}
			buf->len += ret;
			···
		}
	}

	for (;;) {//[3]如果上一页没法接着写,则重新起一页
		··· ···
		head = pipe->head;
		if (!pipe_full(head, pipe->tail, pipe->max_usage)) {
			unsigned int mask = pipe->ring_size - 1;
			struct pipe_buffer *buf = &pipe->bufs[head & mask];
			struct page *page = pipe->tmp_page;
			int copied;

			if (!page) {//[4]重新申请一个新页
				page = alloc_page(GFP_HIGHUSER | __GFP_ACCOUNT);
				if (unlikely(!page)) {
					ret = ret ? : -ENOMEM;
					break;
				}
				pipe->tmp_page = page;
			}

			spin_lock_irq(&pipe->rd_wait.lock);

			head = pipe->head;
			··· ···
			pipe->head = head + 1;
			spin_unlock_irq(&pipe->rd_wait.lock);

			/* Insert it into the buffer array */
			buf = &pipe->bufs[head & mask];
			buf->page = page;//[5]将新申请的页放到页数组中
			buf->ops = &anon_pipe_buf_ops;
			buf->offset = 0;
			buf->len = 0;
			if (is_packetized(filp))
				buf->flags = PIPE_BUF_FLAG_PACKET;
			else
				buf->flags = PIPE_BUF_FLAG_CAN_MERGE;
            	//[6]设置flag,默认PIPE_BUF_FLAG_CAN_MERGE
			pipe->tmp_page = NULL;

			copied = copy_page_from_iter(page, 0, PAGE_SIZE, from); 
            //[7]拷贝操作
			··· ···
			ret += copied;
			buf->offset = 0;
			buf->len = copied;

			··· ···
		}
        ··· ···
    }
	··· ···
	return ret;
}
  1. Wenn die aktuelle Pipe nicht leer ist (head==tail bedeutet leere Pipe), dann sind darin noch ungelesene Daten vorhanden. Der head-Zeiger wird geholt, also die zuletzt zum Schreiben verwendete Seite, und deren len und offset werden geprüft (um das Datenende zu finden). Anschließend wird versucht, auf dieser Seite fortzuschreiben.
  2. Es wird geprüft, ob die aktuelle Seite das Flag PIPE_BUF_FLAG_CAN_MERGE besitzt; ist es nicht vorhanden, ist das Fortschreiben auf der aktuellen Seite nicht erlaubt. Oder wenn die aktuell geschriebenen Daten zusammen mit den vorherigen Daten eine Seite überschreiten (also der Schreibvorgang über eine Seitengrenze geht), ist ein Fortschreiben ebenfalls nicht möglich.
  3. Wenn auf der vorherigen Seite nicht fortgeschrieben werden kann, wird eine neue Seite begonnen.
  4. alloc_page fordert eine neue Seite an.
  5. Die neue Seite wird an den Anfang des Arrays gesetzt (möglicherweise wird eine vorhandene Seite ersetzt) und die Werte werden initialisiert.
  6. buf->flag wird standardmäßig auf PIPE_BUF_FLAG_CAN_MERGE gesetzt, da standardmäßig erlaubt ist, auf der Seite fortzuschreiben.
  7. Die zu schreibenden Daten werden kopiert; wenn nicht alles kopiert wurde, wird der Vorgang wiederholt.

Der Schlüssel zur Ausnutzung ist das in splice nicht initialisierte Flag PIPE_BUF_FLAG_CAN_MERGE, denn es bestimmt, ob wir auf einer „nicht vollständig geschriebenen“ pipe-Seite weiterschreiben können.

Von splice zu copy_page_to_iter_pipe

Tool herunterladen