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

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

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

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

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

Категории

Все категории
Loading categories
CVE-2026-3304 — # Лаборатория воспроизведения CVE-2026-3304 Лаборатория воспроизведения для CVE-2026-3304 — гонки в асинхронном fileFilter в Multer, приводящей к исчерпанию диска через осиротевшие временные файлы. Включает уязвимые и пропатченные Docker-серверы, эксплойт-скрипты и анализ первопричины. | Kitploit
Инструменты/GitHubGitHub/mkway/cve-2026-3304
Анализ уязвимостейЭксплуатацияВеб-безопасностьОбучение и ОбразованиеЛаборатории и Практика
GitHubmkway/cve-2026-3304

CVE-2026-3304

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

Популярное

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

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

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

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

Смотреть все инструменты →

Описание

# Лаборатория воспроизведения CVE-2026-3304 Лаборатория воспроизведения для CVE-2026-3304 — гонки в асинхронном fileFilter в Multer, приводящей к исчерпанию диска через осиротевшие временные файлы. Включает уязвимые и пропатченные Docker-серверы, эксплойт-скрипты и анализ первопричины.

Поделиться

Лабораторная среда CVE-2026-3304

Только для образовательных и исследовательских целей. Атака на системы без авторизации незаконна.

Эта лабораторная среда была создана на основе анализа патча Multer 2.1.0 и его официального тестового кода. Уязвимый сервер воспроизводит точные условия, описанные в тесте патча, а пропатченный сервер запускает Multer 2.1.0 для подтверждения работоспособности исправления.

Обзор уязвимости

ПолеДетали
CVECVE-2026-3304
ЦельMulter < 2.1.0 (промежуточное ПО Node.js для multipart/form-data)
ТипDoS — осиротевший файл (неполная очистка временных файлов)
CWECWE-459: Неполная очистка
CVSS 4.08.7 ВЫСОКИЙ
Исправленная версияMulter 2.1.0

Некорректный multipart-запрос с отсутствующим атрибутом name в файловой части приводит к тому, что Multer создает временный файл на диске, но никогда его не удаляет. Повторные запросы исчерпывают дисковое пространство, что приводит к отказу в обслуживании.


Анализ первопричины

Уязвимость возникает в потоке обработки обратного вызова fileFilter внутри multer/lib/make-middleware.js.

Основная проблема: тайминг setImmediate + отсутствующая проверка errorOccured

Когда Multer потоково обрабатывает и разбирает multipart-запрос по частям, происходит следующая последовательность:

[Последовательность событий разбора — уязвимая версия]

1. Получен заголовок части 1
   → вызывается fileFilter(req, file, cb)
   → setImmediate(cb) → обратный вызов откладывается до следующего тика цикла событий

2. Получено тело части 1
   → открывается /tmp/uploads/<uuid>, начинается запись данных

3. Получен заголовок части 2 (атрибут name отсутствует)
   → Multer обнаруживает 'name missing'
   → errorOccured = true  ← установлен флаг ошибки
   → вызывается abortWithCode('LIMIT_FIELD_KEY') → запланирован HTTP 500

4. Срабатывает обратный вызов setImmediate (следующий тик цикла событий)
   → результат fileFilter: includeFile = true (обычный поток)
   → [ОШИБКА] флаг errorOccured НЕ проверяется
   → вызывается storage._handleFile() → временный файл сохраняется на диск

5. Отправлен ответ HTTP 500
   → временный файл остается на диске (осиротевший файл)

Уязвимый код (make-middleware.js — Multer < 2.1.0)

// Обратный вызов завершения fileFilter (отложен через setImmediate)
fileFilter(req, file, function (err, includeFile) {
  if (err) {
    appender.removePlaceholder(placeholder)
    return abortWithError(uploadedFiles, err)
  }

  if (!includeFile) {
    appender.removePlaceholder(placeholder)
    return fileStream.resume()
  }

  // ❌ errorOccured здесь никогда не проверяется
  //    даже если ошибка была установлена при разборе части 2, выполнение продолжается
  storage._handleFile(req, file, function (err, info) {
    if (err) {
      appender.removePlaceholder(placeholder)
      return abortWithError(uploadedFiles, err)
    }
    // временный файл регистрируется в uploadedFiles и остается на диске
    appender.replacePlaceholder(placeholder, assign(file, info))
    checkFinished()
  })
})

Почему setImmediate является проблемой?

Обертывание fileFilter в setImmediate откладывает его обратный вызов до следующего тика цикла событий. В этом окне busboy (парсер multipart) продолжает разбирать заголовки следующей части, обнаруживает отсутствующий name и устанавливает errorOccured = true. Когда обратный вызов возобновляется, состояние ошибки уже установлено — но код никогда его не проверяет, поэтому storage._handleFile вызывается безусловно, и временный файл записывается на диск.


Анализ кода патча (Multer 2.1.0)

Коммит исправления: 739919097d

Одна проверка if (errorOccured) была добавлена сразу после входа в обратный вызов fileFilter, до достижения storage._handleFile.

// Обратный вызов завершения fileFilter (Multer 2.1.0)
fileFilter(req, file, function (err, includeFile) {
  if (err) {
    appender.removePlaceholder(placeholder)
    return abortWithError(uploadedFiles, err)
  }

  // ✅ [ПАТЧ] Проверка errorOccured перед продолжением
  if (errorOccured) {
    appender.removePlaceholder(placeholder)
    return fileStream.resume()   // дренаж потока — файл не записывается на диск
  }

  if (!includeFile) {
    appender.removePlaceholder(placeholder)
    return fileStream.resume()
  }

  storage._handleFile(req, file, function (err, info) {
    if (err) {
      appender.removePlaceholder(placeholder)
      return abortWithError(uploadedFiles, err)
    }
    appender.replacePlaceholder(placeholder, assign(file, info))
    checkFinished()
  })
})

Сводка изменений

ЭлементУязвимый (< 2.1.0)Исправленный (2.1.0)
Проверка errorOccured❌ Не проверяется✅ Проверяется сразу при входе в обратный вызов
_handleFile вызывается при ошибкеДаЗаблокирован
Очистка временных файлов❌ Отсутствует✅ Через fileStream.resume()
Осиротевшие файлы на запрос10

Почему fileStream.resume() выполняет очистку: Вызов fileStream.resume() дренирует и отбрасывает поток без передачи его в DiskStorage, поэтому файл не записывается и на диске ничего не остается.


Официальный тестовый код патча

Команда Multer включила следующий тест Mocha в патч 2.1.0 для проверки исправления. Этот тест стал основой для данной лабораторной среды — уязвимый сервер воспроизводит точную конфигурацию, описанную здесь (setImmediate fileFilter + некорректный multipart-запрос), а ожидаемое поведение проверяется как на уязвимой, так и на исправленной версиях.

/* eslint-env mocha */

var assert = require('assert')
var fs = require('fs')
var os = require('os')
var path = require('path')
var http = require('http')

var express = require('express')
var multer = require('../')

describe('async fileFilter cleanup', function () {
  it('does not leave orphan files when request aborts with missing field name', function (done) {
    var uploadDir = fs.mkdtempSync(path.join(os.tmpdir(), 'multer-orphan-'))
    var app = express()

    // Vulnerability trigger: async fileFilter via setImmediate
    var upload = multer({
      dest: uploadDir,
      fileFilter: function (req, file, cb) {
        setImmediate(function () { cb(null, true) })
      }
    })

    app.post('/upload', upload.any(), function (req, res) {
      res.json({ success: true })
    })

    // Error handler: respond with 400
    app.use(function (err, req, res, next) {
      res.status(400).json({ error: err.code })
    })

    var server = app.listen(0, function () {
      var port = server.address().port
      var boundary = 'TestBound'

      // Malicious body: Part 1 valid, Part 2 missing name attribute
      var body =
        '--' + boundary + '\r\n' +
        'Content-Disposition: form-data; name="f"; filename="a.bin"\r\n' +
        'Content-Type: application/octet-stream\r\n\r\nORPHAN FILE DATA\r\n' +
        '--' + boundary + '\r\n' +
        'Content-Disposition: form-data; filename="b.bin"\r\n' +   // ← name= missing
        'Content-Type: application/octet-stream\r\n\r\nx\r\n' +
        '--' + boundary + '--\r\n'

      var req = http.request({
        hostname: 'localhost',
        port: port,
        path: '/upload',
        method: 'POST',
        headers: {
          'Content-Type': 'multipart/form-data; boundary=' + boundary,
          'Content-Length': Buffer.byteLength(body)
        }
      }, function (res) {
        res.resume()
        res.on('end', function () {
          setTimeout(function () {
            var files = fs.readdirSync(uploadDir)

            // Assert 1: server must respond with 400 (error)
            assert.strictEqual(res.statusCode, 400)

            // Assert 2: no orphaned files must remain on disk
            assert.strictEqual(files.length, 0)
Скачать инструмент