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.

FeedsContatoPrivacidade© 2026 Kitploit

Diretório de Ferramentas

Categorias

Ver todas as categorias
Loading categories
page_inject — CVE-2026-31431-killed page-cache exploit — execução de código em containers que compartilham a mesma camada de imagem | Kitploit
Ferramentas/GitHubGitHub/sgkdev/page_inject
Análise de VulnerabilidadesExploraçãoPós-ExploraçãoTestes de PenetraçãoRed TeamingEscape de ContêinerExploração de Binários
GitHubsgkdev/page_inject

page_inject

CVE-2026-31431-killed page-cache exploit — execução de código em containers que compartilham a mesma camada de imagem

Ver Repositório
741478há 5 mesesRevisado pelo Kitploit

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

page_inject - escape entre contêineres via AF_ALG aead

Exploit AF_ALG aead de escape entre contêineres -- pivot de um contêiner comprometido para todos os contêineres irmãos que compartilham a mesma camada de imagem libc.so.6.

Esta é uma primitiva de escape: executa de dentro de um contêiner sem privilégios que o atacante já comprometeu, e usa o bug de escrita arbitrária de 4 bytes por rotação ESN authencesn do AF_ALG (CVE-2026-31431) para plantar um hook persistente de read() nas páginas do page-cache de libc.so.6. Como o Docker / containerd fazem o backing das camadas inferiores do overlayfs com inodes compartilhados, essas páginas são visíveis para todos os contêineres irmãos instanciados a partir da mesma imagem -- o hook dispara também nos processos deles, e o atacante obtém execução de comandos dentro de cada um.

Modelo de ameaça

  • O atacante tem acesso a shell a um único contêiner (chamado de victim) em um host que executa outros contêineres (siblings) a partir da mesma imagem que victim.
  • victim executa com a postura padrão do Docker/k8s: uid sem privilégios dentro do user namespace do contêiner, perfil seccomp padrão, perfil AppArmor padrão, sem capabilities especiais, sem bind mounts do host.
  • victim tem apenas:
    • acesso de leitura à sua própria libc (/usr/lib/x86_64-linux-gnu/libc.so.6 ou onde a distro a instala)
    • a família de syscalls padrão socket(AF_ALG, ...)
    • os syscalls padrão splice / vmsplice
    • acesso de escrita a um diretório onde possa fazer chmod +x (ex.: /tmp)
  • O kernel deve estar vulnerável a CVE-2026-31431 (qualquer build algif_aead + authencesn anterior ao fix de revert upstream).

Isto é tudo. Sem CAP_* especial, sem acesso ao filesystem do host. O atacante deposita um binário estático autocontido dentro do contêiner, o executa, e a corrupção do page-cache -- e, portanto, o hook -- torna-se visível para todos os irmãos.

Como o exploit se encadeia

  1. Identidade da página do page-cache. Dentro de um contêiner overlayfs, /usr/lib/.../libc.so.6 é servido pelo inode ext4 da camada inferior da imagem. Todo contêiner iniciado a partir da mesma imagem compartilha esse inode de backing, e o page-cache do kernel é indexado pelo inode subjacente -- não pelo overlay nem pelo namespace. Portanto, uma única escrita de 4 bytes em uma página do page-cache é visível para todos os processos dos contêineres irmãos que têm essa página mmap'd.

  2. A vuln AF_ALG aead transforma uma dessas escritas em muitas. algif_aead encadeia o iovec RX do usuário com os bytes authsize finais do SGL TX com splice, e a rotação ESN de authencesn estaciona 4 bytes do campo seq_high do AAD em dst[assoclen + cryptlen] -- que é o primeiro byte dessa cauda externa encadeada. A página com splice é uma página do page-cache de um arquivo ao qual o atacante tem apenas acesso de leitura, mas o cipher copia bytes para ela mesmo assim, sem bookkeeping de dirty. (Veja crypto/algif_aead.c e crypto/authencesn.c para a mecânica subjacente.)

  3. Bootstrapping de uma primitiva chamável. A primeira coisa que page_inject faz é fazer o bootstrap da Zona A -- uma re-implementação codificada em asm da mesma dança AF_ALG (write_cache.asm), colocada na cave .text da libc. Isso torna a escrita de 4 bytes um call regular a partir de qualquer payload de hook futuro, sem necessidade de setup de socket a cada chamada.

  4. Instalação do hook. O injetor então escreve a Zona C (zone_c.asm) na cave .text da libc e patcha os primeiros 7-12 bytes de read() com um salto E9 disp32 para ela. Os bytes deslocados do prólogo são emulados fielmente no fast-path da Zona C (três prólogos glibc distintos são reconhecidos -- veja "Tratamento do prólogo de read()" abaixo). O hook agora está vivo no page-cache da libc.

  5. Propagação do hook. Todo contêiner irmão executa processos que chamam read() constantemente (daemons de log, healthchecks, cat /etc/hostname, qualquer coisa). Na primeira dessas chamadas dentro de um contêiner irmão, o prólogo sequestrado salta para a Zona C, que:

    • faz stat("/") do inode raiz do contêiner (um ID por namespace estável), usando-o como chave de slot do contêiner,
    • varre a tabela de slots em busca de uma entrada existente com essa chave,
    • se ausente, registra a chave e faz fork() de um filho de longa duração command-loop que faz polling da área CMD por ordens,
    • retorna para read()+N para que o chamador não perceba nada. O processo irmão original continua executando. A partir de agora o atacante tem um daemon dentro desse contêiner.
  6. Canal de comando. O atacante usa o mesmo binário page_inject no modo --shell para escrever comandos na área CMD da região de slots. Cada filho hook registrado de cada contêiner irmão faz polling, faz fork de /bin/sh -c <cmd>, captura stdout/stderr na área OUTPUT, sinaliza a conclusão e volta ao polling. O shell mostra a saída. Como toda escrita CMD/OUTPUT também passa pela primitiva da vuln, nenhum privilégio especial é necessário.

  7. Remoção do hook. Quando terminar, unhook restaura os bytes originais do prólogo de read() e zera a tabela de slots; os filhos hook veem um slot vazio na próxima iteração e se autoterminam. As modificações do page-cache em si são limpas (o kernel nunca marcou as páginas modificadas como dirty), então, uma vez que todo contêiner que tem a libc mmap'd seja parado, um drop_caches reverte o cache completamente -- nenhum artefato em disco permanece.

Compilação

O injetor é compilado fora do contêiner vítima -- tipicamente na própria máquina de desenvolvimento do atacante -- porque a maioria das imagens de contêiner de produção não inclui um compilador. Um ambiente de desenvolvimento Linux x86_64 padrão com gcc (com suporte a link -static) e nasm é suficiente.

make            # assembles .asm sources via gen_arrays.sh, links static page_inject
make shellcode  # also produces inspectable .bin flat binaries
make clean      # removes generated files and the binary

O resultado é um único ELF estaticamente linkado (./page_inject) que executa em qualquer kernel Linux x86_64 moderno.

Entrega e uso (a partir do contêiner vítima)

Uma vez que o atacante tem shell em victim, ele envia o binário para um diretório gravável (tipicamente /tmp):

# inside the compromised container, attacker session
victim$ ./page_inject

Sem argumentos, page_inject usa como padrão /usr/lib/x86_64-linux-gnu/libc.so.6 (o local pós-merge do Debian/Ubuntu). Para outras distros a libc está em um caminho diferente; ou passe-o explicitamente ou use --root / para escanear a tabela de lookup embutida a partir da raiz do contêiner:

# Fedora / Rocky / CentOS
victim$ ./page_inject /usr/lib64/libc.so.6

# Arch
victim$ ./page_inject /usr/lib/libc.so.6

# Auto-detect, regardless of distro:
victim$ ./page_inject --root /

Qualquer uma das invocações faz a mesma coisa: faz parse de ELF da libc dentro do contêiner, instala o hook no page-cache dela, monitora a tabela de slots por ~30 s enquanto os irmãos se registram, e executa um id one-shot contra o primeiro irmão que se registrou como verificação de sanidade.

Após o bootstrap, entre no shell de comando para dirigir qualquer irmão registrado:

victim$ ./page_inject --shell --no-bootstrap
=== page-cache shell ===
Containers (3):
  [0] 0x0018598d  <- target
  [1] 0x001859ab
  [2] 0x001859cd

inject:0018598d> exec id
uid=0(root) gid=0(root) groups=0(root)
inject:0018598d> target 0x001859ab
inject:001859ab> exec hostname
1ccd66abee9d
inject:001859ab> exec cat /etc/shadow
root:$6$.....
inject:001859ab> unhook
... read() prologue restored, slot table zeroed ...
Baixar ferramenta