TGViewer
Искусство. Код... ИИ? Искусство. Код... ИИ? @art_code_ai · 707 subscribers
Post #53 1.89K
🤝 Ni8mare перед Рождеством — по следам уязвимости

Самой громкой CVE за каникулы стала, пожалуй, брендово-трендовая-и-вот-это-всё CVE-2026-21858 aka Ni8mare, получившая CVSS 10.0 благодаря возможности развития атаки на неё до полноценного RCE. Саму уязвимость не разобрал разве что только ленивый <автор этого канала>. Вот хороший разбор с красивыми картинками и примерами кода, и, опять-таки — повторяться, теперь уже, смысла нет.

Причина этой уязвимости, тут же названная «Content-Type Confusion» теми, кому совершенно не жаль составителей таксономий, заключается в ошибках обработки HTTP-запросов с различными Content-Type заголовками в webhook-эндпоинтах n8n: сервер необоснованно доверяет данным из req.body.files даже когда заголовок Content-Type не указывает на multipart/form-data, что позволяет атакующему подменить содержание и структуру тела запроса. Это приводит к тому, что функции обработки считают произвольно сформированные данные «загруженными файлами» и используют их для чтения файлов на сервере. Используя это и функциональность прочих модулей, доступных в n8n, атакующий может развить атаку вплоть до RCE.

Цепляет в уязвимости то, что здесь явно теряется соблюдение в коде границы доверия, но настолько неявным способом, что заметить это, просто читая код глазами или SAST'ом — не так уж и просто. Вот близкий к оригиналу псевдокод, иллюстрирующий уязвимость этого типа:

// Middleware: разбор тела запроса в вебхуке
function parseRequestBody(req) {
if (req.headers['content-type'].startsWith('multipart/form-data')) {
// Парсим форму и файлы (через, например, Formidable)
req.body = parseFormData(req); // сформирует req.body.files для файлов
} else {
// Парсим JSON или другие типы как обычное тело
req.body = parseBody(req); // напрямую десериализует тело в req.body
}
}
// Обработчик webhook формы (уязвимая версия)
app.post('/form-webhook', (req, res) => {
parseRequestBody(req);
const result = prepareFormReturnItem(req.body);
// ...
});
// Функция обработки загруженных файлов
function prepareFormReturnItem(body) {
for (const fileId in body.files) {
// Скопировать файл из временного пути в постоянное хранилище
copyBinaryFile(body.files[fileId].filepath, uploadDir);
}
// ... вернуть результат для workflow
}

Да, copyBinaryFile как бы намекает, что это потенциально опасная операция копирования файлов. Но SAST, не знающий о деталях работы Formidable, как минимум, здесь даст фолз+, на ветке с multipart/form-data, а человек, проводящий триаж/ревью — вообще забьет на обе сработки, т.к. по логике — копирование файлов тут норм, ведь их исходные пути мы получаем от парсера (ведь только от парсера же, да? 😬), выполняющего здесь ещё и роль доверенного санитайзера.

🖥 Что делать разработчикам?

• Структурные входные данные должны валидироваться по схеме конкретного кейса бизнес-логики, следуя принципу fail-closed, прежде, чем начнется работа с их полями (даже их валидация).

• Не стоит смешивать в одном потоке выполнения несколько логических кейсов. В данном случае — следовало бы разнести по разным эндпоинтам работу с разными типами контента (облегчает задачу SAST, делает счастливыми триажеров и ревьюеров — сплошной профит). Но, если уж смешались, то п.1 должен быть корректно реализован для всех веток выполнения.

• Инварианты и гарантии, предоставляемые используемыми парсерами, валидаторами и санитизаторами стоит изучить досконально. Даже в «нормальной» ветке с multipart/form-data, то, что formidable гарантирует загрузку по безопасным путям относительно options.uploadDir — нужно знать, а не предполагать. А ещё лучше — лишний раз убеждаться в этом, прежде, чем работать с полученными результатами. Почему?

• Потому что, Defense in Depth через многоуровневую модель угроз никто не отменял. То, что, например, через обычную читалку файлов стало возможным вытащить .n8n/database.sqlite говорит о том, что уровней границ доверия у n8n просто не было.

⚠ TL;DR: читаем разбор Ni8mare, делаем выводы, сравниваем с написанным выше ✍️

#уязвимости
  • ❤ 3
  • 👍 3
  • 🔥 1
  • 💯 1
More from @art_code_ai
  1. Sep 29, 2026Объяснили с Раддой Юрьевой на Хабре, почему смешивать формальные и ИИшные подходы в задача…
  2. Sep 27, 2026🔗 Старый добрый аппсек, часть 2 Продолжаем вспоминать ключевые вехи аппсека и смежных обл…
  3. Sep 26, 2026Post #155
  4. Sep 24, 2026🔗 Старый добрый аппсек, часть 1 ...совершенно незаслуженно задвинутый на второй план ИИ-х…
  5. Sep 23, 2026Post #153
  6. Sep 21, 2026👨‍💻 Продуктивные выходные Как-то незаметно (и неожиданно) для себя с удовольствием провё…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →