Skip to content
KitploitKITPLOIT
FerramentasBlog
Enviar
FerramentasBlog
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
wasm2c-tableflip — Escape de sandbox wasm2c. Um módulo WebAssembly não confiável escapa da sandbox C gerada e executa um comando shell arbitrário no host. | Kitploit
Ferramentas/GitHubGitHub/trustsig-eu/wasm2c-tableflip
Análise de VulnerabilidadesExploraçãoVirtualização para SegurançaExploração de Binários
GitHubtrustsig-eu/wasm2c-tableflip

wasm2c-tableflip

Escape de sandbox wasm2c. Um módulo WebAssembly não confiável escapa da sandbox C gerada e executa um comando shell arbitrário no host.

Ver Repositório
414há 1 diaAinda 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

Escape da sandbox do wasm2c: módulo convidado executa um comando shell no host

Leia o blog: trustsig.eu/blog

wasm_rt_allocate_funcref_table() em wasm2c/wasm-rt-impl-tableops.inc define table->size a partir da contagem de elementos declarada pelo módulo e depois ignora o resultado de calloc(). Quando a alocação falha, a tabela fica com data == NULL e o size declarado completo, então todas as verificações de limites continuam passando e table->data[i] torna-se o endereço absoluto i * sizeof(wasm_rt_funcref_t).

A contagem de elementos vem do convidado, portanto o convidado escolhe o tamanho da alocação e pode forçar a falha.

Reproduzir

root@kitploit:~
git clone https://github.com/trustsig-eu/wasm2c-tableflip.git
cd wasm2c-tableflip
root@kitploit:~
docker build -t wabt-w2c-poc .
docker run --rm wabt-w2c-poc

A imagem clona o wabt na tag 1.0.41 do upstream, compila wat2wasm e wasm2c, compila o módulo convidado e um embedder simples, e o executa.

Saída esperada:

root@kitploit:~
running guest
guest returned 0
--- file on host ---
goodbye sandbox

A última linha é o conteúdo de /tmp/pwned.txt, um arquivo que não existia antes de o módulo em sandbox ser executado.

Verificado em linux/arm64 e linux/amd64. Nada no módulo é específico de arquitetura: o endereço da instância, o slot da GOT e o offset da libc são todos resolvidos no momento da compilação a partir do binário recém-construído e da libc dessa imagem.

Sem Docker

No Linux, com clang, cmake, ninja e binutils instalados:

root@kitploit:~
git clone --depth 1 --branch 1.0.41 --recurse-submodules --shallow-submodules \
  https://github.com/WebAssembly/wabt ~/wabt
cmake -S ~/wabt -B ~/wabt/out -G Ninja -DCMAKE_BUILD_TYPE=Release \
  -DBUILD_TESTS=OFF -DBUILD_LIBWASM=OFF -DWITH_WASI=OFF
ninja -C ~/wabt/out wat2wasm wasm2c

python3 tableflip_poc.py --wabt-src ~/wabt --wat2wasm ~/wabt/out/wat2wasm \
  --wasm2c ~/wabt/out/wasm2c --run
cat /tmp/pwned.txt

macOS não é um alvo suportado para este PoC. Darwin não impõe RLIMIT_AS, então o calloc superdimensionado tem sucesso e o bug nunca é acionado. Builds de macOS para arm64 são sempre independentes de posição, e Mach-O não possui GOT de ELF para a etapa de vazamento. O defeito em si é independente de plataforma; apenas esta cadeia de exploração é específica do Linux.

Outras versões e comandos:

root@kitploit:~
docker build --build-arg WABT_REF=main -t wabt-w2c-poc .
docker run --rm wabt-w2c-poc bash -c \
  "python3 tableflip_poc.py --run --command 'id > /tmp/pwned.txt' && cat /tmp/pwned.txt"

O que o convidado faz

  1. Declara (table $t 2147483648 funcref). O calloc de 68 GB falha, data fica NULL, size permanece 2147483648, e os índices da tabela tornam-se endereços absolutos.
  2. table.get em got_slot/32 lê a GOT do embedder através da tabela quebrada e table.set armazena o resultado nos globais do próprio módulo, onde o código wasm pode lê-lo como um inteiro. Isso vaza o endereço de malloc da libc, e system vem de uma distância fixa na libc do alvo.
  3. Constrói um wasm_rt_funcref_t em quatro globais consecutivos: func_type apontando para uma cópia do hash de tipo do local de chamada (func_types_eq_slowpath o compara com , portanto bytes controlados pelo convidado passam na verificação), = , e = a string do comando, também mantida em globais.

O único fato de layout embutido no módulo é o endereço da instância do módulo, que é um global e, portanto, fixo em um embedder não-PIE. O ASLR permanece habilitado; o endereço da libc é vazado em tempo de execução.

Condição de acionamento

A alocação da tabela precisa falhar. O PoC usa ulimit -v 1000000, um limite de espaço de endereço do tipo que um host que executa código não confiável definiria. Também falha em hosts de 32 bits, onde a alocação não pode ser satisfeita de forma alguma, com vm.overcommit_memory=2, ou sob pressão de memória suficiente.

Em um Linux 64 bits padrão com a heurística de overcommit padrão, a alocação é bem-sucedida e nunca é tocada, e é por isso que o bug sobrevive aos testes normais.

Versões afetadas

Todas as versões que incluem tabelas do wasm2c. O calloc sem verificação remonta ao commit ab9e0b55 (#813). Verificado contra a versão 1.0.41 lançada e o main atual.

O alocador de memória no mesmo runtime trata deste caso:

root@kitploit:~
  memory->data = (MEMORY_CELL_TYPE)calloc(byte_length, 1);
  if (byte_length != 0 && !memory->data) {
    abort();
  }

O alocador de tabelas precisa da mesma verificação.

Arquivos

  • Dockerfile compila o wabt e executa o PoC.
  • tableflip_poc.py gera o módulo convidado, compila o embedder, resolve as três constantes a partir do binário compilado e da libc do alvo com nm e readelf, e o executa. O embedder que ele emite não contém código de suporte à exploração.
Baixar ferramenta
memcmp
func
system
module_instance
  • call_indirect em globals_addr/32. O wasm2c emite ((t)entry.func)(entry.module_instance, ...), então isso chama system(command).