
PoC para CVE-2019-5736
PoC para CVE-2019-5736
Criado com ajuda de @singe, @_cablethief e @feexd
Testado no Ubuntu 18.04, Debian 9 e Arch Linux. Versões do Docker 18.09.1-ce e 18.03.1-ce. Este PoC atualmente não funciona com Ubuntu 16.04 e CentOS.
Vá conferir o código do exploit do Dragon Sector (as pessoas que descobriram a vulnerabilidade) aqui.
Esta é uma implementação em Go do CVE-2019-5736, uma fuga de container para Docker. O exploit funciona sobrescrevendo e executando o binário runc do sistema host a partir de dentro do container.
Existem 2 casos de uso para o exploit. O primeiro (que é o que este repositório é), é essencialmente uma armadilha. Um atacante precisaria obter execução de comandos dentro de um container e iniciar um binário malicioso que ficaria escutando. Quando alguém (atacante ou vítima) usa docker exec para entrar no container, isso acionará o exploit, que permitirá a execução de código como root.

O segundo (que não é o que este repositório é), cria uma imagem Docker maliciosa. Quando essa imagem é executada, o exploit será acionado. Não é necessário fazer exec no container. Veja o final deste readme para um exemplo em gif.
Para explorar esta vulnerabilidade, você precisa ter root (uid 0) dentro do container.
Sim, você sobrescreverá sua implementação do runc, o que garantirá que seu sistema não será mais capaz de executar containers Docker. Por favor, faça backup de /usr/bin/docker-runc ou /usr/bin/runc (dependendo de qual você tem; verifique também /usr/sbin).
Modifique o código como achar melhor e compile-o com go build main.go. Mova esse binário para o container do qual você deseja escapar. Execute o binário e, na próxima vez que alguém se conectar a ele e chamar /bin/sh, seu payload será acionado.
Este PoC foi criado usando uma excelente explicação de este commit do projeto lxc (junto com alguns conselhos úteis de outras pessoas).
Como exemplo, se o binário alvo fosse /bin/bash, ele poderia ser substituído por um script executável especificando o caminho do interpretador #!/proc/self/exe (/proc/self/exec é um link simbólico criado pelo kernel para cada processo, que aponta para o binário que foi executado para aquele processo). Assim, quando /bin/bash é executado dentro do container, em vez disso, o alvo de /proc/self/exe será executado - o que apontará para o binário runc no host.
Implementamos isso sobrescrevendo /bin/sh no container com #!/proc/self/exe, que apontará para o binário que iniciou este processo (o Docker exec).

O atacante pode então prosseguir para escrever no alvo de /proc/self/exe para tentar sobrescrever o binário runc no host. Porém, em geral, isso não terá êxito, pois o kernel não permitirá que ele seja sobrescrito enquanto o runC estiver em execução. Para contornar isso, o atacante pode, em vez disso, abrir um descritor de arquivo para /proc/self/exe usando a flag O_PATH e então reabrir o binário como O_WRONLY através de /proc/self/fd/ e tentar escrever nele em um busy loop a partir de um processo separado.
Nota: Algumas partes da seção anterior não são totalmente precisas. Você não precisa usar a flag O_PATH ao obter o descritor de arquivo para o runcinit. Além disso, você não precisa criar o loop de escrita em outro processo. Obtemos um descritor de arquivo para o runcinit obtendo um handle de arquivo para /proc/PID/exe. A partir daí, usamos esse handle para obter um handle de arquivo para /proc/self/fd/FILEDESCRIPTOR. Este é o handle de arquivo que usaremos para escrever.

Em última análise, terá êxito quando o binário runC sair. Depois disso, o binário runC está comprometido e pode ser usado para atacar outros containers ou o próprio host.
Se conseguirmos escrever nesse handle de arquivo, sobrescrevemos o binário runc no host. Somos capazes de executar comandos arbitrários como root.
Este repositório não contém este exemplo, mas você pode encontrá-lo aqui. Na minha opinião, este é o cenário muito mais perigoso. Basta executar a imagem de container maliciosa e você obtém execução de código como root. Para uma explicação detalhada, veja isto.
