
Exploit d'escalade de privilèges local pour CVE-2023-0386 ciblant le noyau Linux overlayfs. Comprend une analyse détaillée de la vulnérabilité, le code PoC, et un guide d'exploitation pas à pas utilisant FUSE et les espaces de noms utilisateur.
gcc -Wall exp.c `pkg-config fuse --cflags --libs` -o exp
./exp /tmp

Les connaissances théoriques de cet article (espaces de noms, système de fichiers overlay, système de fichiers FUSE, etc.) proviennent toutes de ChatGPT.
Identifiant CVE : CVE-2023-0386
Produit vulnérable : noyau Linux – système de fichiers overlay
Plage de versions affectées : 5.11 ~ 5.19
Condition d’exploitation : pouvoir utiliser unshare ou créer un système de fichiers overlay
Effet de l’exploitation : élévation de privilèges locale
Compiler le noyau soi-même :
Préparer une version dans la plage vulnérable, en dehors de la 5.15 (la 5.15 semble problématique), activer les deux FS overlay et FUSE :
CONFIG_SLUB_DEBUGOVERLAY_FS
CONFIG_FUSE_FS
Ubuntu 21.10, version du noyau 5.13.0-16-generic, testé avec succès :

Avant d’analyser la vulnérabilité, laissons ChatGPT jouer le rôle d’un expert du noyau Linux :
(Question à ChatGPT : À partir de maintenant, joue le rôle d’un expert du noyau Linux pour m’aider à répondre à quelques questions.)
Les informations publiques sur la vulnérabilité sont rares ; le correctif est le plus direct. Le lien du correctif est le suivant :
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=4f11ada10d0a

On voit qu’une vérification a été ajoutée dans la fonction ovl_copy_up_one. Demandons d’abord à ChatGPT ce que fait cette fonction :

Cette fonction intervient donc lors de la copie d’un fichier de la couche inférieure vers la couche supérieure du système de fichiers overlay. Examinons maintenant la nouvelle vérification ajoutée par le correctif dans son contexte :
static int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry,
int flags)
{
int err;
DEFINE_DELAYED_CALL(done);
struct path parentpath;
struct ovl_copy_up_ctx ctx = {
.parent = parent,
.dentry = dentry,
.workdir = ovl_workdir(dentry),
};
if (WARN_ON(!ctx.workdir))
return -EROFS;
ovl_path_lower(dentry, &ctx.lowerpath);
err = vfs_getattr(&ctx.lowerpath, &ctx.stat,//[1] Récupère le stat du système de fichiers inférieur
STATX_BASIC_STATS, AT_STATX_SYNC_AS_STAT);
if (err)
return err;
//[2] Nouvelle vérification du correctif : l'UID et le GID du fichier ont-ils une correspondance dans l'espace de noms actuel ?
if (!kuid_has_mapping(current_user_ns(), ctx.stat.uid) ||
!kgid_has_mapping(current_user_ns(), ctx.stat.gid))
return -EOVERFLOW;
[1] Tout d’abord, la fonction vfs_getattr récupère les attributs du fichier cible du système de fichiers inférieur. vfs_getattr reçoit une structure struct path d’un fichier et renvoie la structure struct stat correspondante.
[1.1] ctx.lowerpath est le chemin d’un fichier du système de fichiers inférieur du système de fichiers overlay. Le système de fichiers overlay sera présenté plus loin.
[1.2] La structure struct stat contient les métadonnées du fichier, notamment le propriétaire et le groupe. Les informations de propriétaire obtenues sont ensuite vérifiées par la nouvelle condition du correctif.
[2] Ensuite, la fonction kuid_has_mapping est appelée pour vérifier le propriétaire et le groupe du fichier récupéré. Elle détermine si le propriétaire et le groupe du fichier cible ont une correspondance dans l’espace de noms utilisateur actuel.
[2.1] kuid_has_mapping prend deux paramètres : une structure struct user_namespace (espace de noms utilisateur) et une structure struct kuid (UID noyau). Cette fonction détermine si l’information utilisateur donnée a une correspondance dans l’espace de noms utilisateur donné. La correspondance des utilisateurs dans les espaces de noms sera détaillée plus loin.
Nous savons donc que lorsqu’on effectue l’opération de la fonction vulnérable (ovl_copy_up_one), si le propriétaire ou le groupe du fichier de la couche inférieure n’a pas de correspondance dans l’espace de noms actuel, l’opération échoue.
Le principe du correctif est donc clair. Cependant, il reste à résoudre les questions suivantes pour reproduire la vulnérabilité :
ovl_copy_up_one, c’est-à-dire la copie d’un fichier de la couche inférieure vers la couche supérieure dans le système de fichiers overlay ?lowerpath (dont le propriétaire est vérifié) dans la chaîne logique ?Avant de répondre à ces deux questions, nous devons clarifier quelques notions de base :
(Question à ChatGPT : Présente les espaces de noms du noyau Linux.)
Dans Linux, les espaces de noms (namespaces) sont une fonctionnalité du noyau permettant l’isolation des ressources. Grâce aux espaces de noms, un ensemble de processus peut sembler fonctionner dans un environnement système indépendant, améliorant ainsi la sécurité et la gestion. Les espaces de noms jouent un rôle clé dans les technologies de conteneurs (comme Docker), permettant aux conteneurs de s’exécuter dans un environnement isolé sans affecter les autres conteneurs ou le système hôte.
Le noyau Linux prend en charge 7 types d’espaces de noms (mount, pid, net, ipc, user, time, cgroup), chacun isolant une catégorie spécifique de ressources système. Les espaces de noms sont créés, modifiés et gérés via des appels système comme clone, unshare et setns. Les exécutants de conteneurs (comme Docker) et autres outils de virtualisation utilisent ces espaces de noms pour fournir un environnement d’exécution indépendant et isolé.
La fonction de vérification ajoutée par le correctif (kuid_has_mapping) concerne l’un des 7 espaces de noms : l’espace de noms utilisateur.
(Question à ChatGPT : Présente l’espace de noms utilisateur.)
L’espace de noms utilisateur (User Namespace) permet d’isoler les identifiants utilisateur (UID) et groupe (GID). Grâce à l’espace de noms utilisateur, différents ensembles d’UID et GID peuvent être utilisés indépendamment. Ainsi, un utilisateur ou un groupe dans un espace de noms peut avoir un ID ou des permissions différents dans un autre espace de noms. L’espace de noms utilisateur améliore la sécurité et la gestion, en particulier dans les environnements conteneurisés.
La caractéristique clé de l’espace de noms utilisateur est le mappage d’ID : il permet de mapper des UID et GID d’un espace de noms vers un autre. Par exemple, l’utilisateur root (UID 0) dans un conteneur peut être mappé à un utilisateur non privilégié dans le système hôte.
Rappelons les points importants :
/proc/[pid]/uid_map et /proc/[pid]/gid_map), cette opération nécessite généralement les privilèges root de l’espace de noms initial.