
Avviso: CVE-2026-38360 attraversamento di percorso (CWE-22) in dash-uploader (Python/PyPI)
Path traversal non autenticato in fohrloop/dash-uploader (Python, PyPI) che consente la scrittura arbitraria di file, portando, ma non solo, a Remote Code Execution (RCE), sovrascrittura del codice sorgente dell'applicazione, XSS persistente e installazione di backdoor persistenti.
Il repository è stato archiviato il 19 luglio 2025 senza un maintainer attivo. Ogni versione pubblicata (da 0.1.0 a 0.7.0a2) è vulnerabile e lo resterà. Il pacchetto registra ancora circa 28.000 download mensili.
Chiunque esegua dash-uploader in produzione deve applicare una mitigazione autonomamente. La soluzione consigliata è migrare al componente dcc.Upload integrato in Plotly Dash. Vedi Mitigazione per tutte le opzioni.
| ID CVE | CVE-2026-38360 (NVD) |
| Vulnerabilità | Path Traversal (CWE-22) |
| CVSS 3.1 | 9.8 / Critico (AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H) |
| Prodotto | dash-uploader |
| Versioni interessate | da 0.1.0 a 0.7.0a2 (tutte le 18 release) |
| Versione corretta | nessuna (progetto archiviato il 19 luglio 2025) |
| Vettore d'attacco | Remoto, non autenticato |
| Scopritore | Muhammad Fitri Bin Mohd Sultan |
| Assegnato da | MITRE, 2026-05-07 |
| Correlato | CVE-2026-38361 (DoS nella stessa libreria) |
Tre parametri controllabili dall'utente prelevati da request.form.get() in dash_uploader/httprequesthandler.py vengono passati direttamente a os.path.join() e os.makedirs() senza alcuna sanitizzazione o validazione:
upload_id (riga 57 → riga 161, BaseHttpRequestHandler.get_temp_root): controlla la directory di destinazione. Un attaccante può inviare upload_id=../../../../usr/local/lib/python3.10/site-packages e i file vengono scritti nella directory dei pacchetti di Python.
resumableFilename (riga 51 → riga 108, BaseHttpRequestHandler._post): controlla il nome file finale. Un attaccante può uscire dalla directory di upload tramite il nome file anche con un upload_id legittimo.
resumableIdentifier (riga 54 → riga 64, BaseHttpRequestHandler._post): usato con os.makedirs() per creare la directory temporanea. Un attaccante può creare directory arbitrarie in qualsiasi punto del filesystem.
L'endpoint di upload (/API/dash-uploader di default) non richiede autenticazione. L'hook http_request_handler, aggiunto nella v0.5.0, consente controlli pre-richiesta tramite post_before(), ma il metodo vulnerabile _post() legge tutti i parametri direttamente da request.form dopo che l'hook è terminato. L'hook non può sanificare i parametri prima che la libreria li elabori. Uno sviluppatore che aggiunge l'autenticazione tramite l'hook resta comunque vulnerabile al path traversal da parte di un utente autenticato.
# dash_uploader/httprequesthandler.py
def _post(self):
resumableFilename = request.form.get("resumableFilename", default="error", type=str)
resumableIdentifier = request.form.get("resumableIdentifier", default="error", type=str)
upload_id = request.form.get("upload_id", default="", type=str)
...
temp_root = self.get_temp_root(upload_id) # upload_id flows in here
temp_dir = os.path.join(temp_root, resumableIdentifier) # raw user input -> os.path.join
if not os.path.isdir(temp_dir):
os.makedirs(temp_dir) # raw user input -> os.makedirs
def get_temp_root(self, upload_id):
return os.path.join(self.upload_folder, upload_id) # no sanitization: ../../ escapes upload_folder
Tre sink di traversal indipendenti condividono la stessa causa principale: i valori del form raggiungono os.path.join e os.makedirs senza alcuna validazione.
Un attaccante remoto non autenticato invia una richiesta HTTP POST multipart all'endpoint di upload. Iniettando sequenze di path traversal (../) nel parametro del form upload_id, l'attaccante controlla la directory di destinazione del file caricato. Ad esempio, upload_id=../../../../usr/local/lib/python3.10/site-packages scrive i file nella directory dei pacchetti di Python, consentendo RCE al successivo avvio dell'interprete tramite l'esecuzione automatica di .pth.
Non è richiesta alcuna autenticazione, né token di sessione, né token CSRF. Due parametri aggiuntivi (resumableFilename e resumableIdentifier) forniscono vettori di traversal indipendenti attraverso lo stesso endpoint. La configurazione predefinita della libreria, come mostrato nella documentazione ufficiale quickstart, è sfruttabile con un singolo comando curl.
Scrittura arbitraria di file in qualsiasi directory scrivibile dal processo del server. Ciò si traduce in Remote Code Execution (RCE) attraverso diverse primitive note.
Primitive RCE
.pth inserito in site-packages. Esegue il codice fornito dall'attaccante al successivo avvio dell'interprete.sitecustomize.py o usercustomize.py. Esegue a ogni avvio di Python.app.wsgi, wsgi.py). Esegue al successivo reload del worker./etc/cron.d/, /etc/cron.hourly/, spool crontab utente) quando il processo dispone dei privilegi necessari. Esecuzione programmata./etc/systemd/system/, ~/.config/systemd/user/). Esegue al successivo avvio del servizio o al riavvio./etc/ld.so.preload quando il processo viene eseguito come root. Precarica il codice dell'attaccante in ogni successiva esecuzione di binari.~/.bashrc, ~/.profile, ~/.bash_profile). Esegue al prossimo login interattivo dell'utente dell'applicazione.~/.ssh/authorized_keys. Concede accesso SSH persistente all'host come utente dell'applicazione.Impatto a livello web
site-packages), che colpisce ogni utente a ogni caricamento di pagina finché l'applicazione non viene riavviataImpatto sul filesystem
os.makedirs() con il parametro non sanificato resumableIdentifier (utilizzabile per l'esaurimento degli inode o per preparare scritture in alberi di directory inesistenti)dash_uploader/httprequesthandler.pyBaseHttpRequestHandler.get_temp_root()BaseHttpRequestHandler._post()Opzioni per gli utenti che lo hanno già distribuito, in ordine di preferenza:
dcc.Upload, il componente di upload ufficiale distribuito con Plotly Dash. I file arrivano al callback come stringa base64; non viene esposto alcun handler di scrittura sul filesystem e non esiste alcun percorso di destinazione controllato dal client, quindi questa classe di bug non si applica. Particolarmente indicato per file piccoli e medi. Per upload molto grandi, vedi il punto 2.werkzeug.utils.secure_filename() e una directory di destinazione lato server hardcoded. Non accettare mai i valori forniti dal client per upload_id, nome file o identifier come componenti del percorso.upload_id, resumableFilename e resumableIdentifier rispetto a una allowlist rigorosa (ad esempio, solo UUID) a un livello che riscriva o rifiuti la richiesta prima che l'handler della libreria la veda. L'hook http_request_handler della libreria NON previene la traversal perché i parametri vengono letti da request.form dopo che l'hook è terminato; la sanitizzazione deve avvenire a un livello superiore alla libreria..., varianti codificate (%2e%2e, ..%2f, %2e%2e%2f) o percorsi assoluti.| Data | Evento |
|---|---|
| 2026-03-17 | Vulnerabilità scoperta durante una ricerca di sicurezza su una distribuzione in produzione. |
| 2026-03-19 | Richiesta CVE inviata a MITRE. |
| 2026-05-07 | CVE-2026-38360 assegnato da MITRE. |
| 2026-05-07 | Pubblicato l'advisory pubblico. |
| 2026-05-09 | Scheda CVE pubblicata sul database CVE di MITRE e sul NVD. |
0.6.1 (linea stabile). Le pre-release arrivano fino a 0.7.0a2.dash. Dipendenza opzionale: pyyaml. Licenza: MIT.Muhammad Fitri Bin Mohd Sultan