Secure Code Review - Path Traversal Bugs
·3 mins
Table of Contents
Introduction #
- Secure code review is not just a box to check off in the development process; it’s a proactive approach to identifying and mitigating potential vulnerabilities before they can be exploited.
- Through careful examination of code, developers can catch vulnerabilties, such as injection flaws, authentication flaws, and, as we will explore in this blog post, path traversal bugs.
Can you spot the vulnerability in the code below? #
from flask import abort, Flask, request, send_file
import os
app = Flask(__name__)
app.route('/download')
def download():
file = request.args.get('file', 'default.png')
if '..' in file:
abort(400, 'Directory Traversal Detected')
absolute_path = os.path.join(app.root_path,'downloads', file)
if os.path.isfile(absolute_path):
return send_file(absolute_path)
else:
abort(404, 'Requested File Not Found')
This code was obtained from Tib3rius. You can find the original code here
Source code analysis #
- The code starts by importing the necessary modules:
Flask
for creating the web application,abort
for handling HTTP error responses,request
for accessing incoming request data, andsend_file
for sending files to the client. Theos
module is used for working with file paths. - The code then creates a Flask app instance, specifying
__name__
to set the root path. - It then defines an endpoint for file downloads with the route
/download
. - We then retrieve the requested file name from the query parameters. If not provided, it defaults to ‘default.png’
- To prevent directory traversal attacks, we check if the file name contains ‘..’. If detected, we abort the request with a 400 Bad Request response.
- We construct the absolute file path using os.path.join, combining the root path of the app with the ‘downloads’ directory and the requested file name.
- We check if the file exists at the specified path. If it does, we use send_file to send the file to the client. If not, we abort the request with a 404 Not Found response.
The vulnerability #
- We have the line below in the code:
absolute_path = os.path.join(app.root_path,'downloads', file)
- From the Python documentation the
os.path.join(path, *paths)
joins one or more path segments intelligently. - The return value is the concatenation of path and all members of *paths, with exactly one directory separator following each non-empty part, except the last. That is, the result will only end in a separator if the last part is either empty or ends in a separator.
If a segment is an absolute path (which on Windows requires both a drive and a root), then all previous segments are ignored and joining continues from the absolute path segment.
Impact #
- In our code, if the file parameter contains an absolute path, the traversal starts from that absolute path effectively bypassing the check for directory traversal using
..
. So if an attacker supplied ‘/etc/passwd’ in the file parameter, the passwd file is returned.
Proof of concept #
Setting up the server #
Exploitation #
- We pass ‘/etc/passwd’ as the payload in the request and we recieve the passwd file in the response.
Conclusion #
- The path traversal bug we found is one among many and serves as a reminder of the vigilance required in every step of the development process, ensuring that code stands strong against potential exploits.
- See you in the next blog :)