Skip to content
KitploitKITPLOIT
ИнструментыБлог
Отправить
ИнструментыБлог
Отправить

Инструменты для хакинга, пентеста и кибербезопасности — ваш арсенал защиты!

Kitploit — это каталог инструментов для хакинга, кибербезопасности и пентестинга. Находите последние обновления проектов для поиска уязвимостей, анализа систем, автоматизации тестирования и усиления вашей безопасности.

··Ленты·Контакты·Конфиденциальность·© 2026 Kitploit

Каталог инструментов

Категории

Все категории
Loading categories
wasm2c-tableflip — wasm2c sandbox escape. Недоверенный WebAssembly-модуль совершает побег из сгенерированной C-песочницы и выполняет произвольную shell-команду на хосте. | Kitploit
Инструменты/GitHubGitHub/trustsig-eu/wasm2c-tableflip
Анализ уязвимостейЭксплуатацияВиртуализация для безопасностиЭксплуатация Бинарных Файлов
GitHubtrustsig-eu/wasm2c-tableflip

wasm2c-tableflip

wasm2c sandbox escape. Недоверенный WebAssembly-модуль совершает побег из сгенерированной C-песочницы и выполняет произвольную shell-команду на хосте.

Репозиторий
4141 день назадЕщё не проверено

Популярное

Смотреть все →

Откройте для себя самые используемые инструменты нашего сообщества.

Изучить все инструменты

Просмотрите нашу коллекцию инструментов

Смотреть все инструменты →
Поделиться

Побег из песочницы wasm2c: гостевой модуль выполняет shell-команду на хосте

Читайте блог: trustsig.eu/blog

Функция wasm_rt_allocate_funcref_table() в wasm2c/wasm-rt-impl-tableops.inc устанавливает table->size из объявленного количества элементов модуля, а затем игнорирует результат calloc(). Когда выделение памяти не удаётся, таблица остаётся с data == NULL и полным объявленным size, поэтому все проверки границ по-прежнему проходят, и table->data[i] становится абсолютным адресом i * sizeof(wasm_rt_funcref_t).

Количество элементов задаётся гостем, поэтому гость выбирает размер выделения и может вызвать сбой.

Воспроизведение

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

Образ клонирует wabt по тегу 1.0.41 из апстрима, собирает wat2wasm и wasm2c, компилирует гостевой модуль и простой эмбеддер и запускает его.

Ожидаемый вывод:

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

Последняя строка — содержимое /tmp/pwned.txt, файла, которого не существовало до запуска модуля в песочнице.

Проверено на linux/arm64 и linux/amd64. В модуле нет ничего архитектурно-зависимого: адрес экземпляра, слот GOT и смещение libc определяются во время сборки из только что собранного бинарника и libc из образа.

Без Docker

В Linux с установленными clang, cmake, ninja и binutils:

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 не является поддерживаемой целью для этого PoC. Darwin не обеспечивает соблюдение RLIMIT_AS, поэтому увеличенный calloc завершается успешно, и баг никогда не срабатывает; сборки macOS для arm64 всегда позиционно-независимы, а в Mach-O нет ELF GOT для шага утечки. Сам дефект не зависит от платформы; только эта цепочка эксплойта специфична для Linux.

Другие версии и команды:

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"

Что делает гость

  1. Объявляет (table $t 2147483648 funcref). calloc на 68 ГБ завершается неудачей, data равен NULL, size остаётся 2147483648, и индексы таблицы становятся абсолютными адресами.
  2. table.get по адресу got_slot/32 читает GOT эмбеддера через сломанную таблицу, а table.set сохраняет результат в собственные глобальные переменные модуля, где wasm-код может прочитать его как целое число. Так утекает адрес malloc из libc, а system находится на фиксированном расстоянии от него в libc целевой системы.
  3. Собирает wasm_rt_funcref_t в четырёх последовательных глобальных переменных: func_type, указывающий на копию хеша типа из места вызова (func_types_eq_slowpath сравнивает его с помощью , поэтому контролируемые гостем байты проходят проверку), = и = строка команды, также хранящаяся в глобальных переменных.

Единственный факт раскладки, зашитый в модуль, — это адрес экземпляра модуля; он является глобальным и поэтому фиксирован в эмбеддере без PIE. ASLR остаётся включённым; адрес libc утекает во время выполнения.

Условие срабатывания

Выделение памяти для таблицы должно завершиться неудачей. PoC использует ulimit -v 1000000 — ограничение адресного пространства, которое хост, выполняющий недоверенный код, как правило, устанавливает. Выделение также не удаётся на 32-битных хостах, где оно вообще не может быть удовлетворено, при vm.overcommit_memory=2 или при достаточном давлении на память.

На стандартном 64-битном Linux с эвристикой overcommit по умолчанию выделение успешно и память никогда не затрагивается, поэтому при обычном тестировании баг не проявляется.

Затронутые версии

Все выпуски, в которых есть таблицы wasm2c. Непроверяемый calloc восходит к коммиту ab9e0b55 (#813). Проверено на выпуске 1.0.41 и текущем main.

Выделитель памяти в этом же рантайме обрабатывает этот случай так:

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

Выделитель таблиц нуждается в такой же проверке.

Файлы

  • Dockerfile собирает wabt и запускает PoC.
  • tableflip_poc.py генерирует гостевой модуль, собирает эмбеддер, определяет три константы из собранного бинарника и libc целевой системы с помощью nm и readelf, и запускает его. Создаваемый им эмбеддер не содержит вспомогательного кода для эксплойта.
Скачать инструмент
memcmp
func
system
module_instance
  • call_indirect по адресу globals_addr/32. wasm2c генерирует ((t)entry.func)(entry.module_instance, ...), поэтому вызывается system(command).