
PoC for CVE-2019-5736
PoC pour CVE-2019-5736
Créé avec l'aide de @singe, @_cablethief, et @feexd
Testé sur Ubuntu 18.04, Debian 9, et Arch Linux. Versions Docker 18.09.1-ce et 18.03.1-ce. Ce PoC ne fonctionne pas actuellement avec Ubuntu 16.04 et CentOS.
Allez voir le code de l'exploit de Dragon Sector (les personnes qui ont découvert la vulnérabilité) ici.
Ceci est une implémentation Go de CVE-2019-5736, une évasion de conteneur pour Docker. L'exploit fonctionne en écrasant et en exécutant le binaire runc du système hôte depuis le conteneur.
Il y a 2 cas d'utilisation pour l'exploit. Le premier (celui de ce dépôt) est essentiellement un piège. Un attaquant aurait besoin d'obtenir une exécution de commande à l'intérieur d'un conteneur et de lancer un binaire malveillant qui écouterait. Lorsque quelqu'un (attaquant ou victime) utilise docker exec pour entrer dans le conteneur, cela déclenchera l'exploit qui permettra une exécution de code en tant que root.

Le second (qui n'est pas ce que ce dépôt contient) crée une image Docker malveillante. Lorsque cette image est exécutée, l'exploit se déclenche. Pas besoin de exec dans le conteneur. Voir le bas de ce readme pour un exemple en gif.
Pour exploiter cette vulnérabilité, vous devez avoir root (uid 0) à l'intérieur du conteneur.
Oui, vous écraserez votre implémentation de runc, ce qui fera que votre système ne pourra plus exécuter de conteneurs Docker. Veuillez sauvegarder soit /usr/bin/docker-runc soit /usr/bin/runc (selon celui que vous avez ; vérifiez aussi /usr/sbin).
Modifiez le code comme vous le souhaitez et compilez-le avec go build main.go. Déplacez ce binaire dans le conteneur dont vous voulez vous échapper. Exécutez le binaire, et la prochaine fois que quelqu'un s'y attache et appelle /bin/sh, votre payload se déclenchera.
Ce PoC a été créé en utilisant une excellente explication de ce commit du projet lxc (ainsi que quelques conseils utiles d'autres personnes).
Par exemple, si le binaire cible était /bin/bash, il pourrait être remplacé par un script exécutable spécifiant le chemin de l'interpréteur #!/proc/self/exe (/proc/self/exec est un lien symbolique créé par le noyau pour chaque processus qui pointe vers le binaire qui a été exécuté pour ce processus). Ainsi, lorsque /bin/bash est exécuté à l'intérieur du conteneur, la cible de /proc/self/exe sera exécutée à la place - qui pointera vers le binaire runc sur l'hôte.
Nous implémentons cela en écrasant /bin/sh dans le conteneur avec #!/proc/self/exe qui pointera vers le binaire qui a démarré ce processus (le Docker exec).

L'attaquant peut alors procéder à l'écriture sur la cible de /proc/self/exe pour tenter d'écraser le binaire runc sur l'hôte. Cependant, en général, cela ne réussira pas car le noyau ne permettra pas de l'écraser pendant que runC est en cours d'exécution. Pour contourner cela, l'attaquant peut plutôt ouvrir un descripteur de fichier vers /proc/self/exe en utilisant le drapeau O_PATH, puis rouvrir le binaire en O_WRONLY via /proc/self/fd/ et essayer de l'écrire dans une boucle active à partir d'un processus séparé.
Note : Certaines parties de la section précédente ne sont pas entièrement exactes. Vous n'avez pas besoin d'utiliser le drapeau O_PATH pour obtenir le descripteur de fichier pour runcinit. De plus, vous n'avez pas besoin de créer la boucle d'écriture dans un autre processus. Nous obtenons un descripteur de fichier pour le runcinit en obtenant un handle de fichier vers /proc/PID/exe. De là, nous utilisons ce handle pour obtenir un handle de fichier vers /proc/self/fd/FILEDESCRIPTOR. C'est le handle de fichier que nous utiliserons pour l'écriture.

Finalement, cela réussira lorsque le binaire runC se termine. Après cela, le binaire runC est compromis et peut être utilisé pour attaquer d'autres conteneurs ou l'hôte lui-même.
Si nous sommes capables d'écrire sur ce handle de fichier, nous avons écrasé le binaire runc sur l'hôte. Nous pouvons exécuter des commandes arbitraires en tant que root.
Ce dépôt ne contient pas cet exemple mais vous pouvez le trouver ici. À mon avis, c'est le scénario beaucoup plus dangereux. Exécutez simplement l'image du conteneur malveillant et vous obtenez une exécution de code en tant que root. Pour une explication détaillée, veuillez consulter ceci.
