Skip to content
KitploitKITPLOIT
FerramentasExploitsBlog
Log in
Enviar
FerramentasExploitsBlog
Enviar

Ferramentas de Hacking, PenTest e Cibersegurança para o seu Arsenal de Segurança!

Kitploit é um diretório de ferramentas de hacking, cibersegurança e pentesting. Descubra as últimas atualizações de projetos para encontrar vulnerabilidades, analisar sistemas, automatizar testes e fortalecer sua segurança.

··Feeds·Contato·Privacidade·© 2026 Kitploit

Diretório de Ferramentas

Categorias

Ver todas as categorias
Loading categories
ZigRaceExploit — CVE-2021-25741 POC em Zig | Kitploit
Ferramentas/GitHubGitHub/glutenfree69/zigraceexploit
Segurança de ContêineresAnálise de VulnerabilidadesExploraçãoSegurança na NuvemAprendizado e EducaçãoExploração de Binários
GitHubglutenfree69/zigraceexploit

ZigRaceExploit

CVE-2021-25741 POC em Zig

Ver Repositório
11há 6 mesesAinda não revisado

Mais Populares

Ver todos →

Descubra as ferramentas mais usadas pela nossa comunidade.

Explore todas as ferramentas

Navegue pela nossa coleção de ferramentas

Ver todas as ferramentas →
Compartilhar

CVE-2021-25741 — Exploração de Corrida TOCTOU via Symlink em Zig

Exploit educacional da condição de corrida TOCTOU (Time Of Check, Time Of Use) que está na origem de CVE-2021-25741, uma vulnerabilidade no kubelet do Kubernetes.

O projeto demonstra localmente (sem cluster K8s) como uma troca concorrente de symlink pode contornar uma verificação de caminho entre lstat() e open(), e prova que openat2() com os flags RESOLVE_* é a solução.

Índice

  • O problema
  • Como funciona o exploit
  • Arquitetura do código
  • Pré-requisitos
  • Build
  • Utilização
    • Teste local com Docker
    • Comparação openat vs openat2
    • Brincar com o atraso TOCTOU
    • Teste em um node Kubernetes real
  • Resultados esperados
  • Explicação do código
  • Syscalls Linux utilizados
  • Debug com strace
  • Referências

O problema

Resolução de caminhos no Linux

Quando o kernel resolve um caminho como /a/b/c/file, ele o faz componente por componente. A cada etapa, se o componente for um symlink, o kernel o segue automaticamente. Essa resolução não é atômica — o filesystem pode mudar entre duas etapas.

TOCTOU no kubelet

O kubelet do Kubernetes fazia exatamente esse padrão vulnerável:

1. CHECK : lstat(subPath) → "é um diretório, é seguro"
     ↕ JANELA DE CORRIDA — um processo no container troca o diretório por um symlink
2. USE   : mount(subPath) → segue o symlink, monta o filesystem do host

Entre a verificação e o mount, um processo malicioso no container podia substituir o subPath por um symlink para / do host, obtendo assim acesso completo ao filesystem do host.

O fix : openat2(2)

O syscall openat2 (kernel 5.6+) resolve o caminho e abre o arquivo de forma atômica, com restrições:

FlagEfeito
RESOLVE_NO_SYMLINKSRecusa seguir qualquer symlink → retorna ELOOP
RESOLVE_BENEATHRecusa sair do diretório base → retorna EXDEV
RESOLVE_IN_ROOTTrata o dirfd como a raiz do filesystem
RESOLVE_NO_XDEVRecusa atravessar pontos de montagem

Com openat2, não existe janela TOCTOU: se um symlink aparecer durante a resolução, o syscall falha imediatamente.


Como funciona o exploit

Duas threads cooperam para explorar a janela TOCTOU:

workdir/
├── legit_dir/
│   └── secret.txt  → contém "LEGIT"
├── symlink_target/
│   └── secret.txt  → contém "PWNED"
└── target/          → alternado entre dir real e symlink

Thread Racer

Fica em loop ultrarrápido e alterna atomicamente target/ entre dois estados via renameat2(RENAME_EXCHANGE):

  • Estado A : target/ é um diretório real (contém secret.txt = "LEGIT")
  • Estado B : target/ é um symlink para symlink_target/ (contém secret.txt = "PWNED")

Um único syscall por troca = janela de corrida máxima.

Thread Victim (simula o kubelet)

Reproduz o padrão vulnerável do kubelet:

  1. fstatat("target", AT_SYMLINK_NOFOLLOW) — verifica que é um diretório
  2. Pausa opcional (simula a latência do kubelet entre check e use)
  3. openat(dirfd, "target/secret.txt", O_RDONLY) — abre o arquivo
  4. read() — lê o conteúdo
  5. Se conteúdo == "PWNED" → race win (o symlink foi seguido)
  6. Se conteúdo == "LEGIT" → race loss (era mesmo o diretório real)

Em modo protegido (--use-openat2), o passo 3 usa openat2 com RESOLVE_NO_SYMLINKS | RESOLVE_BENEATH. Se um symlink estiver presente, o kernel retorna ELOOP em vez de segui-lo.


Arquitetura do código

src/
├── main.zig       # Ponto de entrada: faz parse da CLI, cria o shared state, spawna
│                  # as threads, mede o tempo, exibe os resultados
│
├── racer.zig      # Thread racer: prepara o estado inicial (target → symlink),
│                  # depois fica em loop com renameat2(RENAME_EXCHANGE) para alternar
│                  # target/ e legit_dir/ continuamente
│
├── victim.zig     # Thread victim: faz loop de N iterações de fstatat → delay →
│                  # openat/openat2 → read → compara "LEGIT" vs "PWNED"
│
├── setup.zig      # Cria a árvore de teste: workdir/, legit_dir/,
│                  # symlink_target/, target/ com os arquivos sentinela
│
├── syscalls.zig   # Constantes e wrappers para os syscalls raw:
│                  # - struct open_how (UAPI do kernel, 3×u64)
│                  # - flags RESOLVE_*
│                  # - RENAME_EXCHANGE (= 2)
│                  # - openat2() via linux.syscall4(.openat2, ...)
│                  # - rename_exchange() via linux.renameat2()
│                  # - Helpers : is_err(), to_errno(), to_fd()
│
└── stats.zig      # Contadores atômicos lock-free (std.atomic.Value(u64))
                   # para wins, losses, errors, eloop + exibição formatada

Todos os syscalls são chamados via std.os.linux.* diretamente (sem wrappers std.fs ou std.posix). O único syscall sem wrapper na stdlib do Zig 0.15 é openat2, que é chamado via linux.syscall4(.openat2, ...) com uma struct open_how definida manualmente a partir dos headers UAPI do kernel.


Pré-requisitos

FerramentaVersãoPorquê
Zig0.15.xCompilador + cross-compilação
DockerqualquerPara executar o binário Linux no Mac
colimaqualquerRuntime Docker no macOS (ou Docker Desktop)

O binário é compilado como estático (musl libc) e roda em qualquer Linux sem dependências.

Kernel mínimo:

  • 3.15+ para renameat2(RENAME_EXCHANGE)
  • 5.6+ para openat2 com RESOLVE_* (necessário apenas para --use-openat2)

Docker Desktop e colima no Mac usam kernel 6.x — tudo é suportado.


Build

Cross-compilação Mac → Linux

# ARM64 (Mac M1/M2/M3 → Docker colima / EC2 ARM)
zig build -Dtarget=aarch64-linux-musl -Doptimize=ReleaseSafe

# x86_64 (para nodes x86)
zig build -Dtarget=x86_64-linux-musl -Doptimize=ReleaseSafe

O binário está em zig-out/bin/race-exploit.

$ file zig-out/bin/race-exploit
ELF 64-bit LSB executable, ARM aarch64, statically linked

Utilização

Teste local com Docker

# Iniciar o runtime Docker (se macOS)
colima start

# Executar o exploit (modo vulnerável por padrão)
docker run --rm -v $(pwd)/zig-out/bin:/app alpine /app/race-exploit --iterations 10000

Comparação openat vs openat2

# Modo vulnerável (openat) — a corrida funciona
docker run --rm -v $(pwd)/zig-out/bin:/app alpine \
  /app/race-exploit --iterations 10000

# Modo protegido (openat2) — a corrida é bloqueada
docker run --rm -v $(pwd)/zig-out/bin:/app alpine \
  /app/race-exploit --iterations 10000 --use-openat2

Ou com o script que executa os dois:

./scripts/run_in_docker.sh --iterations 10000

Brincar com o atraso TOCTOU

O parâmetro --delay-us adiciona um atraso entre o lstat (check) e o openat (use). Quanto maior o atraso, maior a janela TOCTOU e maior a taxa de vitória:

# Sem atraso — win rate ~25%
docker run --rm -v $(pwd)/zig-out/bin:/app alpine \
  /app/race-exploit --iterations 50000

# 10µs — win rate ~35%
docker run --rm -v $(pwd)/zig-out/bin:/app alpine \
  /app/race-exploit --iterations 50000 --delay-us 10

# 100µs — win rate ~50%
docker run --rm -v $(pwd)/zig-out/bin:/app alpine \
  /app/race-exploit --iterations 50000 --delay-us 100

# 1000µs (1ms) — win rate ~50% (limitado, o racer alterna muito mais rápido)
docker run --rm -v $(pwd)/zig-out/bin:/app alpine \
  /app/race-exploit --iterations 10000 --delay-us 1000
Baixar ferramenta