Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion vulnerable_path_traversal.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,11 @@ def download_file():
def read_file():
file_name = request.args.get('filename', 'default.txt')

with open(file_name, 'r') as f:
base_dir = os.path.realpath('/var/www/files/')
safe_path = os.path.realpath(os.path.join(base_dir, file_name))
if not safe_path.startswith(base_dir):
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Path traversal bypass via startswith check without trailing separator

os.path.realpath('/var/www/files/') strips the trailing slash, so base_dir is /var/www/files. The check safe_path.startswith(base_dir) can be bypassed by accessing sibling directories whose names share the same prefix (e.g., /var/www/files_secret/). For example, filename=../files_secret/passwd resolves to /var/www/files_secret/passwd, which passes the startswith('/var/www/files') check, allowing reads outside the intended directory.

Example bypass

GET /read?filename=../files_secret/sensitive.txt

  • base_dir = /var/www/files
  • safe_path = os.path.realpath('/var/www/files/../files_secret/sensitive.txt') = /var/www/files_secret/sensitive.txt
  • '/var/www/files_secret/sensitive.txt'.startswith('/var/www/files')True → access granted
Suggested change
if not safe_path.startswith(base_dir):
if not safe_path.startswith(base_dir + os.sep):
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Path traversal fix bypassable via sibling directory prefix

High Severity

os.path.realpath strips the trailing slash from '/var/www/files/', so base_dir becomes '/var/www/files'. The startswith check then incorrectly permits access to sibling directories with a common prefix (e.g., /var/www/files_secret/), because '/var/www/files_secret/foo'.startswith('/var/www/files') is True. The check needs to compare against base_dir + os.sep to ensure the resolved path is actually within the intended directory.

Fix in Cursor Fix in Web

return 'Access denied', 403
with open(safe_path, 'r') as f:
content = f.read()

return content
Expand Down
Loading