
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.
title: CVE-2022-0847(DirtyPipe lokale Rechteausweitung) Schwachstellenanalyse date: 2022-03-08 14:41:20 tags: - Linux-Rechteausweitung categories: - Sicherheitsforschung
CVE-2022-0847ist eine Schwachstelle imLinux-Kernel, die es seit5.8gibt. Ein Angreifer kann mit ihr Daten in beliebigen schreibgeschützten Dateien überschreiben. Auf diese Weise können normale Benutzerrechte aufrooterweitert werden, da nicht privilegierte Prozesse Code in Root-Prozesse injizieren können.
CVE-2022-0847ähneltCVE-2016-5195 "Dirty Cow"(Dirty-Cow-Rechteausweitung) und ist leicht auszunutzen. Der Autor der Schwachstelle hat sieDirty Pipegenannt.
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.
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
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:
// 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

Entwicklungsgeschichte der „Merge“-Erkennung bei Linux-Pipes:
Hier wird der Code anhand der Ausführungen des Autors kurz analysiert.
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.
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.


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.


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

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.



Daraus ergibt sich für den Autor folgender Ausnutzungsansatz:
PIPE_BUF_FLAG_CAN_MERGE-Flag des pipe_buffer setzensplice den Pipe-Puffer an den Seiten-Cache.copy_page_from_iter() auf, und dank des PIPE_BUF_FLAG_CAN_MERGE-Flags wird der Schreibvorgang direkt abgeschlossen.Analyse des Exploits
/* 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]分别指向管道两端。前者读,后者写