Skip to content
KitploitKITPLOIT
OutilsBlog
Log in
Soumettre
OutilsBlog
Soumettre

Outils de Hacking, PenTest et Cybersécurité pour votre Arsenal de Sécurité !

Kitploit est un répertoire d'outils de hacking, de cybersécurité et de pentesting. Découvrez les dernières mises à jour des projets pour trouver des vulnérabilités, analyser des systèmes, automatiser les tests et renforcer votre sécurité.

FluxContactConfidentialité© 2026 Kitploit

Répertoire d'outils

Catégories

Voir toutes les catégories
Loading categories
CVE-2026-3304 — Laboratoire de reproduction pour CVE-2026-3304, une condition de course asynchrone dans fileFilter de Multer provoquant l'épuisement du disque via des fichiers temporaires orphelins. Inclut des serveurs Docker vulnérables et corrigés, des scripts d'exploitation et une analyse de la cause racine. | Kitploit
Outils/GitHubGitHub/mkway/cve-2026-3304
Analyse des VulnérabilitésExploitationSécurité WebApprentissage et ÉducationLabs et Pratique
GitHubmkway/cve-2026-3304

CVE-2026-3304

Voir le dépôt
21il y a 7 moisPas encore vérifié

Populaires

Voir tout →

Découvrez les outils les plus utilisés par notre communauté.

Explorer tous les outils

Parcourez notre collection d'outils

Voir tous les outils →

À propos

Laboratoire de reproduction pour CVE-2026-3304, une condition de course asynchrone dans fileFilter de Multer provoquant l'épuisement du disque via des fichiers temporaires orphelins. Inclut des serveurs Docker vulnérables et corrigés, des scripts d'exploitation et une analyse de la cause racine.

Partager

Environnement de laboratoire CVE-2026-3304

Uniquement à des fins éducatives et de recherche. Attaquer des systèmes sans autorisation est illégal.

Cet environnement de laboratoire a été construit en analysant le correctif de Multer 2.1.0 et son code de test officiel. Le serveur vulnérable reproduit les conditions exactes décrites dans le test du correctif, et le serveur corrigé exécute Multer 2.1.0 pour confirmer que le correctif fonctionne.

Présentation de la vulnérabilité

ChampDétails
CVECVE-2026-3304
CibleMulter < 2.1.0 (middleware Node.js multipart/form-data)
TypeDoS — Fichier orphelin (nettoyage incomplet des fichiers temporaires)
CWECWE-459 : Nettoyage incomplet
CVSS 4.08.7 ÉLEVÉ
Version corrigéeMulter 2.1.0

Une requête multipart malformée avec un attribut name manquant sur une partie de fichier amène Multer à créer un fichier temporaire sur le disque sans jamais le nettoyer. Des requêtes répétées épuisent l'espace disque, entraînant un déni de service.


Analyse de la cause racine

La vulnérabilité provient du flux de gestion du rappel fileFilter dans multer/lib/make-middleware.js.

Problème principal : temporisation setImmediate + vérification errorOccured manquante

Lorsque Multer diffuse et analyse une requête multipart partie par partie, la séquence suivante se produit :

[Séquence d'événements d'analyse — version vulnérable]

1. En-tête de la partie 1 reçu
   → fileFilter(req, file, cb) appelé
   → setImmediate(cb) → rappel différé au prochain tick de la boucle d'événements

2. Corps de la partie 1 reçu
   → /tmp/uploads/<uuid> ouvert, les données commencent à être écrites

3. En-tête de la partie 2 reçu (attribut name manquant)
   → Multer détecte 'name manquant'
   → errorOccured = true  ← indicateur d'erreur défini
   → abortWithCode('LIMIT_FIELD_KEY') appelé → HTTP 500 planifié

4. Le rappel setImmediate se déclenche (prochain tick de la boucle d'événements)
   → Résultat de fileFilter : includeFile = true (flux normal)
   → [BUG] l'indicateur errorOccured n'est PAS vérifié
   → storage._handleFile() appelé → fichier temporaire validé sur le disque

5. Réponse HTTP 500 envoyée
   → le fichier temporaire reste sur le disque (fichier orphelin)

Code vulnérable (make-middleware.js — Multer < 2.1.0)

// Rappel de fin de fileFilter (différé via 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 n'est jamais vérifié ici
  //    même si une erreur a été définie lors de l'analyse de la partie 2, l'exécution continue
  storage._handleFile(req, file, function (err, info) {
    if (err) {
      appender.removePlaceholder(placeholder)
      return abortWithError(uploadedFiles, err)
    }
    // le fichier temporaire est enregistré dans uploadedFiles et laissé sur le disque
    appender.replacePlaceholder(placeholder, assign(file, info))
    checkFinished()
  })
})

Pourquoi setImmediate est-il le problème ?

Envelopper fileFilter avec setImmediate diffère son rappel au prochain tick de la boucle d'événements. Dans cette fenêtre, busboy (l'analyseur multipart) continue d'analyser les en-têtes de la partie suivante, découvre le name manquant et définit errorOccured = true. Lorsque le rappel reprend, l'état d'erreur est déjà défini — mais le code ne le vérifie jamais, donc storage._handleFile est appelé inconditionnellement et le fichier temporaire est écrit sur le disque.


Analyse du code du correctif (Multer 2.1.0)

Commit du correctif : 739919097d

Un simple garde if (errorOccured) a été ajouté immédiatement après l'entrée du rappel fileFilter, avant que storage._handleFile ne soit atteint.

// Rappel de fin de fileFilter (Multer 2.1.0)
fileFilter(req, file, function (err, includeFile) {
  if (err) {
    appender.removePlaceholder(placeholder)
    return abortWithError(uploadedFiles, err)
  }

  // ✅ [CORRECTIF] Vérifier errorOccured avant de continuer
  if (errorOccured) {
    appender.removePlaceholder(placeholder)
    return fileStream.resume()   // vider le flux — aucun fichier écrit sur le disque
  }

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

Résumé des modifications

ÉlémentVulnérable (< 2.1.0)Corrigé (2.1.0)
Vérification errorOccured❌ Non vérifié✅ Vérifié immédiatement à l'entrée du rappel
_handleFile appelé en cas d'erreurOuiBloqué
Nettoyage des fichiers temporaires❌ Manquant✅ Via fileStream.resume()
Fichiers orphelins par requête10

Pourquoi fileStream.resume() nettoie : Appeler fileStream.resume() vide et rejette le flux sans le transmettre à DiskStorage, donc aucun fichier n'est écrit et rien n'est laissé sur le disque.


Code de test officiel du correctif

L'équipe Multer a inclus le test Mocha suivant dans le correctif 2.1.0 pour vérifier la correction. Ce test est devenu le modèle de cet environnement de laboratoire — le serveur vulnérable reproduit la configuration exacte décrite ici (setImmediate fileFilter + requête multipart malformée), et le comportement attendu est vérifié à la fois sur les versions vulnérable et corrigée.

/* 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)
Télécharger l’outil