
Dimostrazione di Path Traversal CWE-22 in Flask-Uploads 0.2.1. Solo per scopi educativi e di ricerca sulla sicurezza. Testato su Python 3.11.
Esiste una vulnerabilità critica di path traversal in Flask-Uploads versione 0.2.1. La libreria non sanitizza correttamente i nomi dei file caricati prima di salvarli sul filesystem.
In particolare, il metodo save() in flask_uploads.py concatena direttamente il percorso di destinazione dell'upload con il nome file fornito dall'utente usando os.path.join(). Questo consente a un attaccante di includere sequenze di directory traversal (es. ../../) nel nome del file, permettendogli di scrivere file in posizioni arbitrarie del filesystem del server.
File: flask_uploads.py (Riga 132 in v0.2.1)
def save(self, storage, filename=None):
if filename is None:
filename = storage.filename
# VULNERABLE CODE: No sanitization of 'filename'
target = os.path.join(self.destination, filename)
storage.save(target)
return filename
L'applicazione fa affidamento sullo sviluppatore per sanitizzare il nome del file, ma la documentazione della libreria implica che gestisca in modo sicuro i caricamenti dei file. Di default, se uno sviluppatore passa un nome file grezzo da una richiesta, si arriva a una vulnerabilità.
ssh_host_key, file di configurazione)..php, .py) in una directory eseguita dal server web (come cgi-bin o una cartella statica nota), un attaccante può eseguire comandi arbitrari sul server.Un attaccante può sfruttare questa vulnerabilità inviando una richiesta POST con un nome file appositamente costruito:
POST /upload HTTP/1.1
Host: target.com
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary
------WebKitFormBoundary
Content-Disposition: form-data; name="file"; filename="../../../../../tmp/pwned.txt"
Content-Type: text/plain
HACKED
------WebKitFormBoundary--
Se il server usa Flask-Uploads per salvare questo file, verrà scritto in /tmp/pwned.txt invece che nella directory di upload prevista.
La libreria dovrebbe verificare che il percorso unito sia all'interno della directory prevista usando os.path.abspath e startswith, oppure usare sempre una funzione di sanitizzazione come werkzeug.utils.secure_filename.
Correzione consigliata:
from werkzeug.utils import secure_filename
# ...
filename = secure_filename(filename)
target = os.path.join(self.destination, filename)