Skip to content
KitploitKITPLOIT
HerramientasExploitsBlog
Log in
Enviar
HerramientasExploitsBlog
Enviar

¡Herramientas de Hacking, PenTest y Ciberseguridad para tu Arsenal de Seguridad!

Kitploit es un directorio de herramientas de hacking, ciberseguridad y pentesting. Descubre las últimas actualizaciones de proyectos para encontrar vulnerabilidades, analizar sistemas, automatizar pruebas y fortalecer tu seguridad.

FeedsContactoPrivacidad© 2026 Kitploit

Directorio de Herramientas

Categorías

Ver todas las categorías
Loading categories
CVE-2026-3304 — Laboratorio de reproducción para CVE-2026-3304, una condición de carrera en el fileFilter asíncrono de Multer que provoca el agotamiento del disco mediante archivos temporales huérfanos. Incluye servidores Docker vulnerables y parcheados, scripts de explotación y análisis de la causa raíz. | Kitploit
Herramientas/GitHubGitHub/mkway/cve-2026-3304
Análisis de VulnerabilidadesExplotaciónSeguridad WebAprendizaje y EducaciónLabs y Práctica
GitHubmkway/cve-2026-3304

CVE-2026-3304

Ver Repositorio
21hace 7 mesesAún no revisado

Más Populares

Ver todos →

Descubre las herramientas más usadas por nuestra comunidad.

Explora todas las herramientas

Explora nuestra colección de herramientas

Ver todas las herramientas →

Acerca de

Laboratorio de reproducción para CVE-2026-3304, una condición de carrera en el fileFilter asíncrono de Multer que provoca el agotamiento del disco mediante archivos temporales huérfanos. Incluye servidores Docker vulnerables y parcheados, scripts de explotación y análisis de la causa raíz.

Compartir

Entorno de Laboratorio CVE-2026-3304

Solo con fines educativos y de investigación. Atacar sistemas sin autorización es ilegal.

Este entorno de laboratorio fue construido analizando el parche de Multer 2.1.0 y su código de prueba oficial. El servidor vulnerable replica las condiciones exactas descritas en la prueba del parche, y el servidor parcheado ejecuta Multer 2.1.0 para confirmar que la corrección funciona.

Resumen de la Vulnerabilidad

CampoDetalles
CVECVE-2026-3304
ObjetivoMulter < 2.1.0 (middleware multipart/form-data de Node.js)
TipoDoS — Archivo Huérfano (limpieza incompleta de archivos temporales)
CWECWE-459: Limpieza Incompleta
CVSS 4.08.7 ALTA
Versión ParcheadaMulter 2.1.0

Una solicitud multipart malformada con un atributo name faltante en una parte de archivo hace que Multer cree un archivo temporal en el disco pero nunca lo limpie. Las solicitudes repetidas agotan el espacio en disco, resultando en una Denegación de Servicio.


Análisis de la Causa Raíz

La vulnerabilidad se origina en el flujo de manejo del callback fileFilter dentro de multer/lib/make-middleware.js.

Problema central: temporización de setImmediate + verificación faltante de errorOccured

Cuando Multer transmite y analiza una solicitud multipart parte por parte, ocurre la siguiente secuencia:

[Secuencia de eventos de análisis — versión vulnerable]

1. Se recibe el encabezado de la Parte 1
   → se llama a fileFilter(req, file, cb)
   → setImmediate(cb) → callback diferido al siguiente tick del bucle de eventos

2. Se recibe el cuerpo de la Parte 1
   → se abre /tmp/uploads/<uuid>, los datos comienzan a escribirse

3. Se recibe el encabezado de la Parte 2 (falta el atributo name)
   → Multer detecta 'name faltante'
   → errorOccured = true  ← se establece la bandera de error
   → se llama a abortWithCode('LIMIT_FIELD_KEY') → se programa HTTP 500

4. El callback de setImmediate se ejecuta (siguiente tick del bucle de eventos)
   → resultado de fileFilter: includeFile = true (flujo normal)
   → [ERROR] la bandera errorOccured NO se verifica
   → se llama a storage._handleFile() → el archivo temporal se confirma en el disco

5. Se envía la respuesta HTTP 500
   → el archivo temporal permanece en el disco (archivo huérfano)

Código vulnerable (make-middleware.js — Multer < 2.1.0)

// callback de finalización de fileFilter (diferido mediante 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 nunca se verifica aquí
  //    incluso si se estableció un error durante el análisis de la Parte 2, la ejecución continúa
  storage._handleFile(req, file, function (err, info) {
    if (err) {
      appender.removePlaceholder(placeholder)
      return abortWithError(uploadedFiles, err)
    }
    // el archivo temporal se registra en uploadedFiles y se deja en el disco
    appender.replacePlaceholder(placeholder, assign(file, info))
    checkFinished()
  })
})

¿Por qué setImmediate es el problema?

Envolver fileFilter con setImmediate difiere su callback al siguiente tick del bucle de eventos. En esa ventana, busboy (el analizador multipart) continúa analizando los encabezados de la siguiente parte, descubre el name faltante y establece errorOccured = true. Cuando el callback se reanuda, el estado de error ya está establecido — pero el código nunca lo verifica, por lo que storage._handleFile se llama incondicionalmente y el archivo temporal se escribe en el disco.


Análisis del Código del Parche (Multer 2.1.0)

Commit de corrección: 739919097d

Se agregó una única protección if (errorOccured) inmediatamente después de la entrada del callback de fileFilter, antes de que se alcance storage._handleFile.

// callback de finalización de fileFilter (Multer 2.1.0)
fileFilter(req, file, function (err, includeFile) {
  if (err) {
    appender.removePlaceholder(placeholder)
    return abortWithError(uploadedFiles, err)
  }

  // ✅ [PARCHE] Verificar errorOccured antes de continuar
  if (errorOccured) {
    appender.removePlaceholder(placeholder)
    return fileStream.resume()   // drenar el flujo — no se escribe ningún archivo en el disco
  }

  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()
  })
})

Resumen de cambios

ElementoVulnerable (< 2.1.0)Parcheado (2.1.0)
Verificación de errorOccured❌ No verificada✅ Verificada inmediatamente en la entrada del callback
_handleFile llamado en caso de errorSíBloqueado
Limpieza de archivos temporales❌ Faltante✅ Mediante fileStream.resume()
Archivos huérfanos por solicitud10

Por qué fileStream.resume() limpia: Llamar a fileStream.resume() drena y descarta el flujo sin pasarlo a DiskStorage, por lo que no se escribe ningún archivo y no queda nada en el disco.


Código de Prueba Oficial del Parche

El equipo de Multer incluyó la siguiente prueba Mocha en el parche 2.1.0 para verificar la corrección. Esta prueba se convirtió en el modelo para este entorno de laboratorio — el servidor vulnerable replica la configuración exacta descrita aquí (setImmediate fileFilter + solicitud multipart malformada), y el comportamiento esperado se verifica tanto en las versiones vulnerable como parcheada.

/* 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('limpieza de fileFilter asíncrono', function () {
  it('no deja archivos huérfanos cuando la solicitud se aborta con nombre de campo faltante', function (done) {
    var uploadDir = fs.mkdtempSync(path.join(os.tmpdir(), 'multer-orphan-'))
    var app = express()

    // Disparador de vulnerabilidad: fileFilter asíncrono mediante 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 })
    })

    // Manejador de errores: responder con 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'

      // Cuerpo malicioso: Parte 1 válida, Parte 2 sin atributo name
      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= faltante
        'Content-Type: application/octet-stream\r\n\r\nx\r\n' +
        '--' + boundary + '--\r\n'
Descargar herramienta