Path Traversal — jak atakujący czyta pliki z Twojego serwera
Path traversal (directory traversal) to jeden z prostszych ataków webowych, a mimo to nadal trafia do top 10 podatności. Pozwala atakującemu czytać dowolne pliki z serwera — konfigurację, klucze SSH, hasła, kod aplikacji. W niektórych przypadkach umożliwia też zapis.
Jak to działa?
Wyobraź sobie aplikację, która wyświetla obrazki:
https://example.com/loadImage?filename=photo.jpg
Serwer bierze parametr filename i dokłada go do ścieżki bazowej:
/var/www/images/ + photo.jpg → /var/www/images/photo.jpg
Co jeśli zamiast photo.jpg podamy ../../../etc/passwd?
/var/www/images/../../../etc/passwd → /etc/passwd
Sekwencja ../ cofa się o katalog wyżej. Trzy takie sekwencje z /var/www/images/ docierają do roota filesystem. Serwer zwraca zawartość /etc/passwd.
Co atakujący chce przeczytać?
Na Linuxie typowe cele:
/etc/passwd # lista użytkowników
/etc/shadow # hashe haseł (jeśli serwer działa jako root)
/etc/hosts # konfiguracja DNS
/proc/self/environ # zmienne środowiskowe (mogą zawierać klucze API)
/home/user/.ssh/id_rsa # klucz prywatny SSH
/var/log/auth.log # logi logowania
~/.bash_history # historia komend
Na aplikacji webowej:
/var/www/html/.env # hasła do bazy, klucze API
/var/www/html/config/database.yml # konfiguracja Rails
/etc/nginx/nginx.conf # konfiguracja serwera
Techniki obchodzenia zabezpieczeń
Naiwna obrona to filtrowanie ../ ze stringa. Atakujący mają na to kilka sposobów.
Podwójne sekwencje
Jeśli serwer usuwa ../ raz, wystarczy zagnieżdżenie:
....//....//....//etc/passwd
Po usunięciu wewnętrznego ../ zostaje ../../../etc/passwd.
URL encoding
%2e%2e%2f → ../
%2e%2e/ → ../
..%2f → ../
Podwójne kodowanie
Jeśli serwer dekoduje URL dwa razy:
%252e%252e%252f → %2e%2e%2f → ../
Kodowanie niestandardowe
..%c0%af → ../ (UTF-8 overlong encoding)
..%ef%bc%8f → ../ (fullwidth slash)
Null byte
Jeśli serwer wymusza rozszerzenie pliku (np. .jpg):
../../../etc/passwd%00.jpg
Null byte (%00) przycina string — serwer otwiera /etc/passwd, ignorując .jpg. Ten trik działa głównie na starszych wersjach PHP i C.
Ścieżka bezwzględna
Czasem nie trzeba ../ — wystarczy podać pełną ścieżkę:
filename=/etc/passwd
Rozpoczęcie od wymaganego katalogu
Jeśli serwer sprawdza, czy ścieżka zaczyna się od /var/www/images:
filename=/var/www/images/../../../etc/passwd
Walidacja przechodzi, bo ścieżka zaczyna się od wymaganego katalogu.
Jak się bronić?
Dwie warstwy obrony — obie potrzebne jednocześnie.
1. Walidacja inputu
Najlepiej: whitelist dozwolonych nazw plików. Jeśli to niemożliwe — sprawdź, czy input zawiera tylko alfanumeryczne znaki:
import re
def is_safe_filename(filename):
return bool(re.match(r'^[a-zA-Z0-9._-]+$', filename))
To blokuje ../, %2e, null bytes i wszystko inne.
2. Kanonizacja ścieżki
Nawet z walidacją inputu — sprawdź rozwiązaną ścieżkę:
import os
BASE_DIR = '/var/www/images'
def safe_path(filename):
full_path = os.path.realpath(os.path.join(BASE_DIR, filename))
if not full_path.startswith(BASE_DIR):
raise ValueError('Path traversal detected')
return full_path
os.path.realpath() rozwiązuje symlinki i ../, więc nawet jeśli atakujący obejdzie walidację inputu, kanonizacja to złapie.
3. Minimalne uprawnienia
Serwer webowy nie powinien działać jako root. Jeśli działa jako www-data, nawet udany path traversal nie przeczyta /etc/shadow.
# Sprawdź jako kto działa serwer
ps aux | grep nginx
ps aux | grep apache
4. chroot / kontenery
W środowisku produkcyjnym — chroot, Docker, lub namespace’y. Atakujący może podać ../../../../, ale jeśli root filesystem jest izolowany, nie ucieknie dalej niż kontener.
Testowanie
Szybki test na swojej aplikacji:
# Podstawowy test
curl "https://twoja-strona.com/api/file?name=../../../etc/passwd"
# Z encodingiem
curl "https://twoja-strona.com/api/file?name=%2e%2e%2f%2e%2e%2f%2e%2e%2fetc%2fpasswd"
# Null byte
curl "https://twoja-strona.com/api/file?name=../../../etc/passwd%00.jpg"
Jeśli którykolwiek zwraca treść /etc/passwd — masz problem.