
Prova di concetto per CVE-2019-5736
PoC per CVE-2019-5736
Creato con l'aiuto di @singe, @_cablethief e @feexd
Testato su Ubuntu 18.04, Debian 9 e Arch Linux. Versioni Docker 18.09.1-ce e 18.03.1-ce. Questa PoC attualmente non funziona con Ubuntu 16.04 e CentOS.
Vai a dare un'occhiata al codice dell'exploit di Dragon Sector (le persone che hanno scoperto la vulnerabilità) qui.
Questa è un'implementazione in Go di CVE-2019-5736, un container escape per Docker. L'exploit funziona sovrascrivendo ed eseguendo il binario runc del sistema host dall'interno del container.
Ci sono 2 casi d'uso per l'exploit. Il primo (che è ciò che questo repo rappresenta) è essenzialmente una trappola. Un attaccante dovrebbe ottenere l'esecuzione di comandi all'interno di un container e avviare un binario malevolo che ascolterebbe. Quando qualcuno (attaccante o vittima) usa docker exec per entrare nel container, questo attiverà l'exploit che permetterà l'esecuzione di codice come root.

Il secondo (che non è ciò che questo repo rappresenta) crea un'immagine Docker malevola. Quando quell'immagine viene eseguita, l'exploit si attiverà. Non c'è bisogno di eseguire exec nel container. Vedi il fondo di questo readme per un esempio in gif.
Per sfruttare questa vulnerabilità è necessario avere root (uid 0) all'interno del container.
Sì, sovrascriverai la tua implementazione di runc, il che farà sì che il tuo sistema non sarà più in grado di eseguire container Docker. Effettua il backup di /usr/bin/docker-runc o /usr/bin/runc (a seconda di quale hai; controlla anche /usr/sbin).
Modifica il codice come ritieni opportuno e compilalo con go build main.go. Sposta quel binario nel container da cui vuoi scappare. Esegui il binario, e poi la prossima volta che qualcuno si collega ad esso e chiama /bin/sh il tuo payload si attiverà.
Questa PoC è stata creata usando un'eccellente spiegazione da questo commit al progetto lxc (insieme a qualche utile consiglio da altri).
Ad esempio, se il binario di destinazione era /bin/bash, questo potrebbe essere sostituito con uno script eseguibile che specifica il percorso dell'interprete #!/proc/self/exe (/proc/self/exec è un collegamento simbolico creato dal kernel per ogni processo che punta al binario eseguito per quel processo). Di conseguenza, quando /bin/bash viene eseguito all'interno del container, verrà invece eseguito il target di /proc/self/exe - che punterà al binario runc sull'host.
Lo implementiamo sovrascrivendo /bin/sh nel container con #!/proc/self/exe che punterà al binario che ha avviato questo processo (il Docker exec).

L'attaccante può quindi procedere a scrivere sul target di /proc/self/exe per tentare di sovrascrivere il binario runc sull'host. Tuttavia, in generale, ciò non riuscirà perché il kernel non permetterà di sovrascriverlo mentre runC è in esecuzione. Per superare questo ostacolo, l'attaccante può invece aprire un descrittore di file verso /proc/self/exe usando il flag O_PATH e poi riaprire il binario come O_WRONLY tramite /proc/self/fd/ e provare a scriverci in un ciclo continuo da un processo separato.
Nota: Alcune parti della sezione precedente non sono del tutto accurate. Non è necessario usare il flag O_PATH quando si ottiene il descrittore di file per runcinit. Inoltre non è necessario creare il ciclo di scrittura in un altro processo. Otteniamo un descrittore di file per runcinit ottenendo un handle di file per /proc/PID/exe. Da lì, usiamo poi quell'handle per ottenere un handle di file per /proc/self/fd/FILEDESCRIPTOR. Questo è l'handle di file che useremo per scrivere.

Alla fine avrà successo quando il binario runC termina. Dopo di ciò, il binario runC è compromesso e può essere usato per attaccare altri container o l'host stesso.
Se siamo in grado di scrivere su quell'handle di file, abbiamo sovrascritto il binario runc sull'host. Siamo in grado di eseguire comandi arbitrari come root.
Questo repo non contiene questo esempio ma puoi trovarlo qui. A mio avviso, questo è lo scenario molto più pericoloso. Basta eseguire l'immagine del container malevolo e si ottiene l'esecuzione di codice come root. Per una spiegazione dettagliata, per favore vedi questo.
