
Analyse approfondie et exploit de preuve de concept pour CVE-2022-0847 (DirtyPipe), une vulnérabilité du noyau Linux permettant l'écrasement arbitraire de fichiers et l'escalade de privilèges locale via des drapeaux de tampon de tube non initialisés.
title: Analyse de la vulnérabilité CVE-2022-0847 (DirtyPipe – élévation de privilèges locale) date: 2022-03-08 14:41:20 tags: - Élévation de privilèges Linux categories: - Recherche en sécurité
CVE-2022-0847est une vulnérabilité dans le noyau Linux depuis la version 5.8. Un attaquant peut l’exploiter pour écraser arbitrairement les données de tout fichier en lecture seule. Cela permet d’élever des privilèges ordinaires jusqu’àroot, car un processus non privilégié peut injecter du code dans un processus root.
CVE-2022-0847est similaire àCVE-2016-5195 “Dirty Cow”et est facile à exploiter. L’auteur de la vulnérabilité l’a nomméeDirty Pipe.
Ce blog est principalement utilisé pour enregistrer des événements de sécurité et des articles sur les vulnérabilités, à des fins d’apprentissage et de test. Toute conséquence ou dommage direct ou indirect résultant de la diffusion ou de l’utilisation des informations ou outils fournis dans cet article est de la seule responsabilité de l’utilisateur. L’auteur de l’article n’assume aucune responsabilité à cet égard.
Niveau de danger : Élevé
POC/EXP : Publié
Versions affectées : Noyau Linux 5.8 et versions ultérieures
Remarque : Versions sécurisées : noyau Linux >= 5.16.11, noyau Linux >= 5.15.25, noyau Linux >= 5.10.102
Présentation rapide des détails de la vulnérabilité.
Quelques concepts :
Linux pipe : Semi-duplex, le flux de données ne peut aller que d’un côté à l’autre.
pipe_buffer : Cache du tube, utilisé pour stocker temporairement les données écrites dans le tube. Les lectures et écritures se font via ce cache.
page : Cadre de page, 4 Ko, en relation un-à-un avec le cache du tube.
pipe_buf_operations : Stocke l’ensemble des opérations du cache du tube.
can_merge : Drapeau de fusion. S’il est défini à 1, une lecture/écriture générique du tube peut fusionner les données dans un tampon existant. S’il est à 0, un nouveau segment de page de tube est toujours utilisé pour les nouvelles données.
splice() : Déplace des données entre deux descripteurs de fichier, comme sendfile(). Il prend en charge les tubes et est également une copie zéro. Il lie le cache de page du fichier et le cache du tube, de sorte que l’écriture affecte les deux en même temps. Lors de la vérification des autorisations, il ne vérifie que si le fichier source a une autorisation de lecture ; il n’y a pas de vérification d’autorisation en écriture. La chaîne d’appel approximative est :
// 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

Historique du contrôle de « fusion » dans les tubes Linux :
Analyse rapide du code d’après l’auteur.
Les premiers systèmes Linux correspondaient au concept présenté : un indicateur can_merge existait pour marquer si de nouvelles données pouvaient être écrites dans le cache de tube existant.
Commit 5274f052e7b3 a introduit la fonction splice(), mais la vérification n’a pas changé : elle utilisait toujours l’indicateur can_merge pour déterminer si le cache de tube actuel était disponible.


Commit 01e7187b4119 a cessé d’utiliser l’indicateur can_merge pour le remplacer par une comparaison du pointeur struct pipe_buf_operations. Seul le type anon_pipe_buf_ops permet l’écriture de nouvelles données ; il suffit donc de vérifier si le type correspond.


Commit 241699cd72a8 a ajouté deux nouvelles fonctions qui allouent de nouvelles struct pipe_buf_operations mais n’initialisent pas leur indicateur flags.

Commit f6dd975583bd a transformé cette comparaison de pointeurs en une comparaison de l’indicateur PIPE_BUF_FLAG_CAN_MERGE de chaque tampon, permettant d’injecter PIPE_BUF_FLAG_CAN_MERGE, tout en supprimant la définition et l’utilisation d’autres types de buf_ops.



Ainsi, l’auteur a la méthode d’exploitation suivante :
PIPE_BUF_FLAG_CAN_MERGE de pipe_buffer.splice. À ce moment, splice lie le cache du tube et le cache de page.copy_page_from_iter() pour effectuer l’écriture. Grâce au drapeau PIPE_BUF_FLAG_CAN_MERGE, l’écriture est effectuée directement.Analyse de l’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();