Skip to content
KitploitKITPLOIT
OutilsExploitsBlog
Log in
Soumettre
OutilsExploitsBlog
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
StackRot — CVE-2023-3269: Vulnérabilité d'élévation de privilèges du noyau Linux | Kitploit
Outils/GitHubGitHub/lrh2000/stackrot
Escalade de PrivilègesAnalyse des VulnérabilitésExploitationCTFExploitation de Binaires
GitHublrh2000/stackrot

StackRot

CVE-2023-3269: Vulnérabilité d'élévation de privilèges du noyau Linux

Voir le dépôt
499377il y a 7 moisVérifié par Kitploit

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

StackRot (CVE-2023-3269) : Vulnérabilité d'élévation de privilèges dans le noyau Linux

GitHub CI (exploit vérifié par GitHub CI)

Démonstration

Une faille a été découverte dans la gestion de l'expansion de la pile dans le noyau Linux 6.1 à 6.4, également appelée « Stack Rot ». L'arbre maple, responsable de la gestion des zones de mémoire virtuelle, peut subir un remplacement de nœud sans avoir correctement acquis le verrou d'écriture MM, ce qui conduit à des problèmes d'utilisation après libération (use-after-free). Un utilisateur local non privilégié pourrait utiliser cette faille pour compromettre le noyau et élever ses privilèges.

Comme StackRot est une vulnérabilité du noyau Linux trouvée dans le sous-système de gestion de la mémoire, elle affecte presque toutes les configurations du noyau et nécessite des capacités minimales pour être déclenchée. Cependant, il convient de noter que les nœuds maple sont libérés à l'aide de callbacks RCU, retardant la désallocation réelle de la mémoire jusqu'à après la période de grâce RCU. Par conséquent, l'exploitation de cette vulnérabilité est considérée comme difficile.

À ma connaissance, il n'existe actuellement aucun exploit disponible publiquement ciblant les bugs de type use-after-free-by-RCU (UAFBR). Cela marque la première fois que les bugs UAFBR se sont avérés exploitables, même sans la présence des paramètres CONFIG_PREEMPT ou CONFIG_SLAB_MERGE_DEFAULT. Notamment, cet exploit a été démontré avec succès dans l'environnement fourni par Google kCTF VRP (bzImage_upstream_6.1.25, config).

La vulnérabilité StackRot est présente dans le noyau Linux depuis la version 6.1, lorsque la structure de l'arbre VMA a été changée des arbres rouge-noir (red-black trees) aux arbres maple.

Contexte

Chaque fois que l'appel système mmap() est utilisé pour établir un mappage mémoire, le noyau génère une structure appelée vm_area_struct pour représenter la zone de mémoire virtuelle correspondante (VMA). Cette structure stocke diverses informations, notamment des drapeaux (flags), des propriétés et d'autres détails pertinents liés au mappage.```c struct vm_area_struct { long unsigned int vm_start; /* 0 8 / long unsigned int vm_end; / 8 8 / struct mm_struct * vm_mm; / 16 8 / pgprot_t vm_page_prot; / 24 8 / long unsigned int vm_flags; / 32 8 / union { struct { struct rb_node rb attribute((aligned(8))); / 40 24 / / --- cacheline 1 boundary (64 bytes) --- / long unsigned int rb_subtree_last; / 64 8 / } attribute((aligned(8))) shared attribute((aligned(8))); / 40 32 / struct anon_vma_name * anon_name; / 40 8 / } attribute((aligned(8))); / 40 32 / / --- cacheline 1 boundary (64 bytes) was 8 bytes ago --- / struct list_head anon_vma_chain; / 72 16 / struct anon_vma * anon_vma; / 88 8 / const struct vm_operations_struct * vm_ops; / 96 8 / long unsigned int vm_pgoff; / 104 8 / struct file * vm_file; / 112 8 / void * vm_private_data; / 120 8 / / --- cacheline 2 boundary (128 bytes) --- / atomic_long_t swap_readahead_info; / 128 8 / struct vm_userfaultfd_ctx vm_userfaultfd_ctx; / 136 0 */

root@kitploit:~
    /* size: 136, cachelines: 3, members: 14 */
    /* forced alignments: 1 */
    /* last cacheline: 8 bytes */

} attribute((aligned(8)));

root@kitploit:~
Par la suite, lorsque le noyau rencontre des défauts de page ou d'autres appels système liés à la mémoire, il nécessite une recherche rapide de la VMA uniquement basée sur l'adresse. Auparavant, les VMA étaient gérées à l'aide d'arbres rouge-noir. Cependant, à partir de la version 6.1 du noyau Linux, la migration vers les arbres érables a eu lieu. Les [arbres érables][mt] sont des structures de données d'arbre B protégées par RCU, optimisées pour stocker des intervalles non chevauchants. Néanmoins, leur nature complexe ajoute de la complexité à la base de code et introduit la vulnérabilité StackRot.

 [mt]: https://docs.kernel.org/6.4/core-api/maple_tree.html

Au cœur, un arbre érable est composé de nœuds érables. Bien que la structure de l'arbre puisse être complexe, il est important de noter que cette complexité n'a rien à voir avec le bogue StackRot. Par conséquent, dans cet article, il est supposé que l'arbre érable ne comprend qu'un seul nœud, c'est-à-dire le nœud racine.

Ce nœud racine peut contenir jusqu'à 16 intervalles. Ces intervalles peuvent soit représenter un espace vide, soit pointer vers une VMA. Comme les espaces vides comptent également comme des intervalles, tous les intervalles sont connectés séquentiellement, ce qui nécessite seulement 15 points d'extrémité, également appelés pivots, dans la structure du nœud. Notez que le point d'extrémité le plus à gauche et le point d'extrémité le plus à droite sont omis, car ils peuvent être récupérés à partir du nœud parent.```c
struct maple_range_64 {
        struct maple_pnode *       parent;               /*     0     8 */
        long unsigned int          pivot[15];            /*     8   120 */
        /* --- cacheline 2 boundary (128 bytes) --- */
        union {
                void *             slot[16];             /*   128   128 */
                struct {
                        void *     pad[15];              /*   128   120 */
                        /* --- cacheline 3 boundary (192 bytes) was 56 bytes ago --- */
                        struct maple_metadata meta;      /*   248     2 */
                };                                       /*   128   128 */
        };                                               /*   128   128 */

        /* size: 256, cachelines: 4, members: 3 */
};

La structure maple_range_64, comme montré ci-dessus, représente un nœud maple. En plus des pivots, les emplacements sont utilisés pour faire référence à la structure VMA lorsque le nœud fonctionne comme un nœud feuille, ou à d'autres nœuds maple lorsque le nœud fonctionne comme un nœud intérieur. Si un intervalle correspond à un écart, l'emplacement contiendra simplement une valeur NULL. La disposition des points pivots et des emplacements peut être visualisée comme illustré ci-dessous :``` Slots -> | 0 | 1 | 2 | ... | 12 | 13 | 14 | 15 | ┬ ┬ ┬ ┬ ┬ ┬ ┬ ┬ ┬ │ │ │ │ │ │ │ │ └─ Implied maximum │ │ │ │ │ │ │ └─ Pivot 14 │ │ │ │ │ │ └─ Pivot 13 │ │ │ │ │ └─ Pivot 12 │ │ │ │ └─ Pivot 11 │ │ │ └─ Pivot 2 │ │ └─ Pivot 1 │ └─ Pivot 0 └─ Implied minimum

root@kitploit:~
Concernant la modification concurrente, l'arbre en érable impose une restriction spécifique : un verrou exclusif doit être détenu par les rédacteurs (*Règle W*). Dans le cas de l'arbre VMA, le verrou exclusif correspond au verrou d'écriture MM. Quant aux lecteurs, deux options sont disponibles. La première option consiste à détenir le verrou de lecture MM (*Règle A1*), ce qui bloque le rédacteur par le verrou de lecture-écriture MM. La seconde option est d'entrer dans la section critique RCU (*Règle A2*). Ainsi, le rédacteur n'est pas bloqué et les lecteurs peuvent poursuivre leurs opérations puisque l'arbre en érable est compatible RCU. Bien que la plupart des accès VMA existants optent pour la première option (c'est-à-dire la Règle A1), la Règle A2 est employée dans quelques scénarios critiques en termes de performances, comme les défauts de page sans verrou.

Cependant, il existe un aspect supplémentaire qui nécessite une attention particulière, et qui concerne l'expansion de la pile. La pile représente une zone mémoire mappée avec le drapeau MAP_GROWSDOWN, indiquant une expansion automatique lorsqu'une adresse située en dessous de la région est accédée. Dans de tels cas, l'adresse de début de la VMA correspondante est ajustée, ainsi que l'intervalle associé dans l'arbre en érable. Notamment, ces ajustements sont effectués sans détenir le verrou d'écriture MM.```c
static inline
void do_user_addr_fault(struct pt_regs *regs,
                        unsigned long error_code,
                        unsigned long address)
{
	// ...

	if (unlikely(!mmap_read_trylock(mm))) {
		// ...
	}
	// ...
	if (unlikely(expand_stack(vma, address))) {
		// ...
	}

	// ...
}

Typiquement, un espace existe entre la VMA de la pile et sa VMA voisine, car le noyau applique un garde-fou de pile. Dans ce scénario, lors de l'expansion de la pile, seule la valeur pivot dans le nœud maple doit être mise à jour, un processus qui peut être effectué de manière atomique. Cependant, si la VMA voisine possède également le drapeau MAP_GROWSDOWN, aucun garde-fou de pile n'est appliqué.```c int expand_downwards(struct vm_area_struct *vma, unsigned long address) { // ...

root@kitploit:~
if (prev) {
	if (!(prev->vm_flags & VM_GROWSDOWN) &&
	    vma_is_accessible(prev) &&
	    (address - prev->vm_end < stack_guard_gap))
		return -ENOMEM;
}

// ...

}

root@kitploit:~
Par conséquent, l'expansion de la pile peut éliminer l'espace. Dans de telles situations, l'intervalle de l'espace dans le nœud maple doit être supprimé. Comme l'arbre maple est compatible RCU, il n'est pas possible de réécrire le nœud sur place. Au lieu de cela, un nouveau nœud est créé, déclenchant le remplacement du nœud, et l'ancien nœud est ensuite détruit à l'aide d'un callback RCU.```c
static inline void mas_wr_modify(struct ma_wr_state *wr_mas)
{
	// ...

	if ((wr_mas->offset_end - mas->offset <= 1) &&
	    mas_wr_slot_store(wr_mas))           // <-- in-place update
		return;
	else if (mas_wr_node_store(wr_mas))      // <-- node replacement
		return;

	// ...
}

Le callback RCU n'est invoqué qu'après la conclusion de toutes les sections critiques RCU préexistantes. Cependant, le problème survient lors de l'accès aux VMA, car seul le verrou de lecture MM est détenu, et celui-ci n'entre pas dans la section critique RCU (selon la règle A1). Par conséquent, en théorie, le callback pourrait être invoqué à tout moment, entraînant la libération de l'ancien nœud maple. Cependant, des pointeurs vers l'ancien nœud peuvent déjà avoir été récupérés, ce qui conduit à un bug de use-after-free lors d'une tentative d'accès ultérieur à celui-ci.

La backtrace où le use-after-free (UAF) se produit est présentée ci-dessous :```

  • CPU 0 - - CPU 1 -

mm_read_lock() mm_read_lock() expand_stack() find_vma_prev() expand_downwards() mas_walk() mas_store_prealloc() mas_state_walk() mas_wr_story_entry() mas_start() mas_wr_modify() mas_root() mas_wr_node_store() node = rcu_dereference_check() mas_replace() [ The node pointer is recorded ] mas_free() ma_free_rcu() call_rcu(&mt_free_rcu) [ The node is dead ] mm_read_unlock()

[ Wait for the next RCU grace period.. ] rcu_do_batch() mas_prev() mt_free_rcu() mas_prev_entry() kmem_cache_free() mas_prev_nentry() [ The node is freed ] mas_slot() mt_slot() rcu_dereference_check(node->..) [ UAF occurs here ] mm_read_unlock()

root@kitploit:~
## Correctif

J'ai signalé cette vulnérabilité à l'équipe de sécurité du noyau Linux le 15 juin.
Par la suite, le processus de correction de ce bug a été mené par Linus Torvalds.
Compte tenu de sa complexité, il a fallu près de deux semaines pour développer un ensemble de correctifs
qui ont fait l'objet d'un consensus.

Le 28 juin, lors de la fenêtre de fusion pour le noyau Linux 6.5, le correctif a été fusionné
dans l'arbre de Linus. Linus a fourni un [message de fusion complet][fix] pour
expliquer la série de correctifs d'un point de vue technique.

 [fix]: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=9471f1f2f50282b9e8f59198ec6bb738b4ccc009

Ces correctifs ont ensuite été rétroportés vers les noyaux stables ([6.1.37][6.1],
[6.3.11][6.3] et [6.4.1][6.4]), résolvant ainsi le bug « Stack Rot » le
1er juillet.

 [6.1]: https://lore.kernel.org/stable/2023070133-create-stainless-9a8c@gregkh/T/
 [6.3]: https://lore.kernel.org/stable/2023070146-endearing-bounding-d21a@gregkh/T/
 [6.4]: https://lore.kernel.org/stable/2023070140-eldercare-landlord-133c@gregkh/T/

## Exploit

L'exploit se concentre principalement sur le défi Google kCTF, en particulier lorsque
ni CONFIG_PREEMPT ni CONFIG_SLAB_MERGE_DEFAULT ne sont activés. Pour exploiter
StackRot, la tâche la plus importante est de localiser une itération VMA qui remplit
les critères suivants :
 1. Le timing de l'itération peut être contrôlé. Ce contrôle nous permet de garantir
    que la période de grâce RCU se termine pendant l'itération VMA.
 2. L'itération récupère des informations spécifiques de la structure VMA, et
    les renvoie à l'espace utilisateur. Cette fonctionnalité nous permet
    d'exploiter la vulnérabilité UAF du nœud d'érable pour divulguer certaines
    adresses du noyau.
 3. L'itération invoque certains pointeurs de fonction dans la structure VMA. Cette
    capacité particulière nous permet d'exploiter l'UAF du nœud d'érable pour
    contrôler le compteur de programme (PC) en mode noyau.

L'itération VMA choisie est l'itération chargée de générer le
contenu de `/proc/[pid]/maps`. Les sections suivantes montreront comment cette
itération satisfait aux critères ci-dessus.

### Étape 0 : De l'UAFBR à l'UAF

Lors d'une itération VMA, la référence au nœud racine de l'arbre VMA est
obtenue, et l'itération progresse à travers ses emplacements. Ainsi, en déclenchant
l'expansion de la pile dans un autre thread sur un CPU séparé pendant l'itération
VMA, le remplacement du nœud peut être initié simultanément. À ce stade, l'accès
à l'ancien nœud est considéré comme une situation use-after-free-par-RCU (UAFBR). Cependant,
les problèmes réels ne surviennent que lorsque l'ancien nœud est véritablement libéré, ce qui se produit
dans le rappel RCU.

Cela présente deux défis : (i) déterminer quand l'ancien nœud est libéré et
(ii) s'assurer que l'itération VMA ne se termine pas avant que l'ancien nœud ne soit
libéré.

La première question est relativement simple. Dans le noyau, la
fonction `synchronize_rcu()` peut être utilisée pour attendre la fin de la période de grâce
RCU, garantissant que tous les rappels RCU préexistants ont été invoqués. Dans
l'espace utilisateur, les appels système qui finissent par appeler `synchronize_rcu()` peuvent être
utilisés dans le même but. Ainsi, lorsque ces appels système se terminent, on sait
que l'ancien nœud a été libéré. Notamment, il existe un appel système,
`membarrier(MEMBARRIER_CMD_GLOBAL, 0, -1)`, qui invoque uniquement
`synchronize_rcu()`.```c
SYSCALL_DEFINE3(membarrier, int, cmd, unsigned int, flags, int, cpu_id)
{
	// ...

	switch (cmd) {
	// ...
	case MEMBARRIER_CMD_GLOBAL:
		/* MEMBARRIER_CMD_GLOBAL is not compatible with nohz_full. */
		if (tick_nohz_full_enabled())
			return -EINVAL;
		if (num_online_cpus() > 1)
			synchronize_rcu();
		return 0;
	// ...
	}
}

La deuxième question nécessite une réflexion plus approfondie. Plusieurs solutions potentielles sont les suivantes :

  1. La tâche d'itération est préemptée, la période de grâce RCU se termine, et l'itération reprend son exécution. Cependant, cette approche est inefficace si CONFIG_PREEMPT n'est pas défini.
  2. La tâche d'itération entre en état de sommeil (par exemple, en attente d'E/S), la période de grâce RCU se termine, et l'itération continue. Actuellement, je ne connais aucune itération VMA qui satisfait cette condition et qui peut être exploitée pour fuiter des adresses noyau et contrôler le compteur de programme (PC). Cela peut exister, mais une enquête approfondie est nécessaire.
  3. La tâche d'itération subit une interruption (par exemple, une interruption d'horloge), pendant laquelle la période de grâce RCU se conclut. Il est possible d'utiliser timerfd pour créer plusieurs temporisateurs matériels qui, lors d'un dépassement de temps pendant l'itération VMA, peuvent déclencher une interruption longue. Cependant, cette approche n'est pas viable car le gestionnaire d'interruption fonctionne avec les interruptions désactivées, et si un CPU ne peut pas gérer les interruptions inter-processeurs (IPI), la période de grâce RCU ne se terminera pas.
  4. La tâche d'itération est délibérément prolongée, permettant à la période de grâce RCU d'expirer. C'est la solution choisie. Si la période de grâce RCU actuelle dépasse jiffies_till_first_fqs (par défaut de plusieurs jiffies), une interruption inter-processeur (IPI) sera envoyée au CPU victime et déclenchera une préemption volontaire. Dans le cas d'une itération VMA, la préemption volontaire peut faire en sorte que la période de grâce RCU se termine et libère le nœud d'érable, convertissant effectivement UAFBR en un véritable scénario d'utilisation après libération (UAF).

Une observation importante est que lors de l'itération VMA pour /proc/[pid]/maps, celui-ci génère le chemin complet du fichier pour les régions mémoire mappées en fichier. Bien que le nom du répertoire soit généralement limité à un maximum de 255 caractères, il n'y a pas de limitation sur la profondeur du répertoire. Cela signifie qu'en créant un fichier avec une profondeur de répertoire extrêmement grande et en établissant un mappage mémoire pour ce fichier, l'accès à /proc/[pid]/maps peut prendre un temps considérable pendant l'itération VMA. Par conséquent, cette durée prolongée permet la possibilité de conclure la période de grâce RCU et d'acquérir la primitive UAF.```c static void show_map_vma(struct seq_file *m, struct vm_area_struct *vma) { // ...

root@kitploit:~
/*
 * Print the dentry name for named mappings, and a
 * special [heap] marker for the heap:
 */
if (file) {
	seq_pad(m, ' ');
	/*
	 * If user named this anon shared memory via
	 * prctl(PR_SET_VMA ..., use the provided name.
	 */
	if (anon_name)
		seq_printf(m, "[anon_shmem:%s]", anon_name->name);
	else
		seq_file_path(m, file, "\n");
	goto done;
}

// ...

}

root@kitploit:~
Cette étape est illustrée dans la figure suivante :

![Étape 0 : De UAFBR à UAF](https://assets.kitploit.com/production/public/readmes/28620/6791d2c105f1fc504247b668317c7d23c3141c92456dab2fb2e54f7f70e16e15.png)

### Étape 1 : De l'UAF dans un slab à l'UAF dans une page

Maintenant que l'UAF fonctionne à l'intérieur d'un slab. Si CONFIG_SLAB_MERGE_DEFAULT est activé et que le slab des nœuds maple se fusionne avec kmalloc-256, le contenu de l'ancien nœud peut être contrôlé en allouant une nouvelle structure depuis kmalloc-256 et en la remplissant avec des données utilisateur. Cependant, si CONFIG_SLAB_MERGE_DEFAULT n'est pas défini, une approche alternative est nécessaire. Dans ce cas, il faut retourner la page du nœud libéré à l'allocateur de pages, permettant ainsi de contrôler l'ancien nœud en allouant une nouvelle page et en la remplissant en conséquence.

Rappelez-vous que l'arbre VMA ne contiendra qu'un seul nœud. Ainsi, en utilisant `fork()`/`clone()`, plusieurs arbres VMA et un nombre égal de nœuds maple sont générés. En supposant qu'un slab englobe M nœuds maple, et qu'un nœud sur M nœuds est conservé tandis que tous les autres nœuds sont libérés via `exit()`, les nœuds restants deviennent les seuls nœuds dans leurs slabs respectifs. Initialement, ces slabs résident dans la liste partielle du CPU. Lorsque la liste partielle atteint sa capacité, les slabs sont vidés vers la liste partielle du nœud NUMA correspondant.

Si le dernier nœud maple dans un slab est libéré, le slab devient vide. Si ce slab réside dans la liste partielle d'un nœud NUMA, et que la liste partielle de ce nœud NUMA particulier est déjà à pleine capacité, la page est immédiatement retournée à l'allocateur de pages. Par conséquent, l'UAF de slab se transforme en un scénario d'UAF de page. Le contenu de la page libérée peut être manipulé en envoyant des données via `msgsnd()`, qui alloue des objets élastiques et les remplit directement avec les données utilisateur fournies.```c
static void __slab_free(struct kmem_cache *s, struct slab *slab,
			void *head, void *tail, int cnt,
			unsigned long addr)

{
	// ...

	if (unlikely(!new.inuse && n->nr_partial >= s->min_partial))
		goto slab_empty;

	// ...
	return;

slab_empty:
	// ...
	discard_slab(s, slab);
}

Le nombre de nœuds maple par slab, M, dépend du nombre de processeurs. L'implémentation de l'exploit considère une situation avec deux processeurs et suppose donc 16 comme valeur de M, comme illustré dans la figure suivante :

Étape 1 : De l'UAF slab à l'UAF page

Étape 2 : De l'UAF à la fuite d'adresse

Après avoir pris le contrôle du nœud maple, il devient possible de manipuler les adresses des VMA suivants qui seront itérés plus tard. Comme l'itération ciblée vise à générer /proc/self/maps, certaines informations VMA, telles que les adresses de début et de fin, qui résident dans la structure VMA, sont renvoyées à l'espace utilisateur.

Cependant, un défi se pose : l'adresse d'une structure VMA dans le nœud maple ne peut être correctement définie que si certaines adresses sont déjà connues. Heureusement, CVE-2023-0597 sert directement cet objectif. Selon CVE-2023-0597, l'adresse de cpu_entry_area n'est pas aléatoire. Bien que cette vulnérabilité ait été corrigée dans Linux 6.2, elle n'a pas été rétroportée vers les noyaux stables antérieurs à la date de rédaction. Par conséquent, en écrasant l'adresse de la structure VMA avec celle de la dernière entrée IDT, l'entrée qui contient l'adresse de asm_sysvec_spurious_apic_interrupt est directement divulguée, révélant ainsi les adresses de base du code du noyau et des données du noyau.

Étape 2 : De l'UAF à la fuite d'adresse (1)

La méthode précédemment discutée peut être utilisée de manière récurrente pour exposer progressivement plus d'adresses de la section de données du noyau. Par exemple, le pointeur init_task.tasks.prev dans la section de données dirige vers la structure task_struct de la tâche la plus récemment créée, qui est sans aucun doute allouée sur le tas.

Étape 2 : De l'UAF à la fuite d'adresse (2)

Lorsque toutes les tâches nouvellement créées sont terminées, leurs structures task_struct seront ensuite désallouées. Si la quantité de ces tâches est suffisamment grande, les pages correspondantes peuvent être rendues à l'allocateur de pages. Cela permet la possibilité de réallouer ces pages et de les remplir avec des données utilisateur. Cependant, gardez à l'esprit que les pages libérées appartiennent généralement à la liste de pages par CPU (PCP). Pour les pages présentes dans la liste PCP, elles ne peuvent être réallouées que dans le même ordre de pages. Par conséquent, le simple fait de mapper de nouvelles pages dans l'espace utilisateur, ce qui ne nécessite que des pages d'ordre 0 de l'allocateur de pages, ne remplira pas les objectifs.

Néanmoins, l'appel système msgsnd sollicitera des blocs de mémoire via kmalloc et remplira ces blocs avec des données définies par l'utilisateur. Lorsque le cache kmalloc est épuisé, il demandera des pages à l'allocateur de pages à un ordre spécifique. Si la taille du message est ajustée avec précision, l'ordre exact sera souhaité. Ainsi, la page dont l'adresse a été précédemment divulguée sera réallouée. En conséquence, il devient possible d'obtenir une page avec une adresse connue et des données manipulées par l'utilisateur.

Étape 3 : De l'UAF aux privilèges root

Il est maintenant possible de forger la structure VMA dans la page d'adresse connue et de contrôler le pointeur de fonction vma->vm_ops->name. L'étape suivante consiste à trouver des gadgets appropriés pour s'échapper des conteneurs et acquérir les privilèges root.```c static void show_map_vma(struct seq_file *m, struct vm_area_struct *vma) { // ...

root@kitploit:~
if (vma->vm_ops && vma->vm_ops->name) {
	name = vma->vm_ops->name(vma);
	if (name)
		goto done;
}

// ...

}

root@kitploit:~
![Étape 3 : De l'UAF aux privilèges root](https://assets.kitploit.com/production/public/readmes/28620/d9c03475803bb799f2b594935d118d73f2547ea4dcf5b40c54e1454324575a0f.png)

Les constructions de gadgets sont les suivantes :
 1. Pivot de pile : `movq %rbx, %rsi; movq %rbp, %rdi; call
    __x86_indirect_thunk_r13` -> `pushq %rsi; jmp 46(%rsi)` -> `popq %rsp; ret`
    -> `popq %rsp; ret`, où %rdi, %rbx et %r13 pointent _initialement_ vers des
    données contrôlables par l'utilisateur.
 2. Obtenir les privilèges root : `popq %rdi; ret` -> `prepare_kernel_cred` -> `popq
    %rdi; ret` -> `movq %rax, (%rdi); ret`, où %rdi pointe _maintenant_ vers le
    sommet de la pile ; `popq %rdi; ret` -> `commit_creds`, exécutant effectivement
    `commit_creds(prepare_kernel_cred(&init_task))`.
 3. Échapper aux conteneurs : `popq %rdi; ret` -> `find_task_by_vpid` -> `popq %rdi;
    ret` -> `movq %rax, (%rdi); ret`, où %rdi pointe _maintenant_ vers le sommet de
    la pile ; `popq %rdi; ret` -> `popq %rsi; ret` -> `switch_task_namespaces`,
    effectuant `switch_task_namespaces(find_task_by_vpid(1), &init_nsproxy)`.
 4. Déverrouiller mm : `popq %rax; ret` -> `movq %rbp, %rdi; call
    __x86_indirect_thunk_rax`, où %rbp pointe vers le seq_file original ;
    `popq %rax; ret` -> `m_stop`, exécutant effectivement `m_stop(seq_file, ..)`.
 5. Retour dans l'espace utilisateur : utiliser `swapgs_restore_regs_and_return_to_usermode`, et
    appeler `execve()` pour obtenir le shell.

Enfin, en utilisant `nsenter --mount=/proc/1/ns/mnt` pour restaurer l'espace de noms de montage
et obtenir le flag via `cat /flag/flag`.

### Code source

Le code source complet de l'exploit est disponible [ici](https://github.com/lrh2000/stackrot/blob/master/exp). Pour plus de détails, consultez
son fichier README.
Télécharger l’outil