
Ausnutzung von CVE-2022-0847 - geschrieben von: Antonius (w1sdom)
Dirty Pipe (CVE-2022-0847) ist eine der bedeutendsten Sicherheitslücken im Linux-Kernel 5.8 – 5.15.24, entdeckt von Max Kellermann im Jahr 2022. Diese Schwachstelle erlaubt es normalen Benutzern (ohne besondere Berechtigungen), Daten in Dateien zu überschreiben, die schreibgeschützt sein sollten. Verständnis der Kernkonzepte
Bevor wir Dirty Pipe im Detail besprechen, müssen einige interne Konzepte des Linux-Kernels verstanden werden:
1. Paging
Paging ist ein Speicherverwaltungsmechanismus im Linux-Kernel, bei dem das Speichersystem den physischen Speicher in kleine Blöcke fester Größe, sogenannte Seitenrahmen (Page Frames), und den virtuellen Speicher in Blöcke gleicher Größe, sogenannte Seiten (Pages), unterteilt.
Dieser Mechanismus erlaubt dem Kernel, den virtuellen Adressraum von Prozessen auf nicht-sequenzielle Weise auf den physischen Speicher abzubilden, was für Effizienz und Sicherheit in modernen Systemen entscheidend ist.
2. Seite (Virtueller Speicher)
In Linux ist eine Seite die kleinste Einheit der physischen Speicherverwaltung, die vom Kernel gehandhabt wird.
Analogie: RAM ist wie ein riesiges Buch. Eine Seite ist ein Blatt Papier in diesem Buch. Der Kernel bewegt Daten nicht Bit für Bit, sondern Blatt für Blatt (Seite für Seite).
Im Allgemeinen beträgt die Standardgröße einer Seite auf modernen Systemarchitekturen (wie x86_64) 4 KB (4096 Bytes).
3. Page Cache
Dies ist ein entscheidender Teil. Linux liest Dateien nicht jedes Mal direkt von der Festplatte, da dies langsam ist. Der Kernel kopiert Dateiinhalte in den RAM, der als Page Cache bezeichnet wird.
4. Pipe Buffer
Pipe ist ein Mechanismus zur Interprozesskommunikation (IPC). Intern verwaltet der Kernel Pipes mithilfe der pipe_inode_info-Datenstruktur. Daten innerhalb einer Pipe werden in einem "Puffer" namens Pipe Buffer gespeichert.
5. Pipe Buffer Flag (PIPE_BUF_FLAG_CAN_MERGE)
Das Flag PIPE_BUF_FLAG_CAN_MERGE wurde mit Linux-Kernel Version 5.8 eingeführt.
Hier liegt die Hauptschwachstelle. Das Flag heißt PIPE_BUF_FLAG_CAN_MERGE.
6. Splice
splice() ist ein Syscall zum Verschieben von Daten zwischen zwei Dateideskriptoren, ohne die Daten zwischen Kernelspace und Userspace zu kopieren. Dies wird oft als Zero-Copy-Mechanismus bezeichnet.
Der splice()-Syscall ist der "Hauptakteur" bei Dirty Pipe:
7. Copy on Write (CoW)
Der Copy-on-Write (CoW)-Mechanismus ist eine Optimierungsstrategie der Speicherverwaltung, die vom Linux-Kernel verwendet wird, um das Kopieren von Daten so lange wie möglich hinauszuzögern.
Die Beziehung zwischen Copy-on-Write (CoW) und dem Dirty-Pipe-Exploit (CVE-2022-0847) besteht darin, wie ein kleiner Fehler im Linux-Kernel den CoW-Mechanismus erfolgreich "austrickst", sodass Daten in Dateien geschrieben werden können, die schreibgeschützt sein sollten.
8. Dirty Page
Eine Dirty Page ist eine Speicherseite im RAM, die von einer Anwendung geändert wurde, deren Änderungen jedoch noch nicht zurück auf den Sekundärspeicher (wie SSD oder Festplatte) geschrieben wurden.
Analyse der Dirty Pipe Sicherheitslücke
Dirty Pipe ist eine Art Logikfehler in der Pipe-Puffer-Verarbeitung im Linux-Kernel 5.8 bis Linux-Kernel 5.15.24. Das Hauptproblem liegt im Pipe-Mechanismus (Interprozesskommunikationskanal) und darin, wie der Kernel den Page Cache (Speicher, der Kopien von Dateidaten von der Festplatte speichert) verwaltet. Der Kernfehler ist ein Fehler im Flag PIPE_BUF_FLAG_CAN_MERGE.
Das Hauptproblem liegt darin, dass der Kernel dieses Flag nicht ordnungsgemäß neu initialisiert (Logikfehler). Hier ist die Codeanalyse: In den Funktionen copy_page_to_iter_pipe und push_to_pipe im Linux-Kernel vor Version 5.16.11 bereitet der Kernel bei splice-Operationen die pipe_buffer-Struktur vor, vergisst jedoch, das .flags-Mitglied zu bereinigen.
Anfällige Codestruktur:
// Ort des Problems: fs/pipe.c oder include/linux/pipe_fs_i.h
struct pipe_buffer {
struct page *page;
unsigned int offset, len;
const struct pipe_buf_operations *ops;
unsigned int flags; // <--- DIESES FLAG WIRD NICHT ZURÜCKGESETZT
unsigned long private;
};
Code vor dem Patch (anfällig):
// lib/iov_iter.c - Vor CVE-2022-0847 Patch
static size_t copy_page_to_iter_pipe(struct page *page,
size_t offset, size_t bytes, struct iov_iter *i) {
// ---------snip-----------
struct pipe_buffer *buf = &pipe->bufs[head & mask];