Path traversal is often described as “put ../ in the filename.” That is the first lab, not the model.
The model is:
The string you validate is not necessarily the path the filesystem opens.
The six PortSwigger path traversal labs are a compact tour of canonicalization mistakes: raw traversal, absolute paths, non-recursive stripping, double decoding, prefix validation, and null-byte suffix bypasses.
The map
| Broken assumption | Payload shape |
|---|---|
| user filename stays under image directory | ../../../etc/passwd |
blocking ../ is enough |
/etc/passwd |
| one strip pass removes traversal | ....//....//....//etc/passwd |
| validation before final decode is safe | ..%252f..%252f..%252fetc/passwd |
| raw prefix means containment | /var/www/images/../../../etc/passwd |
| suffix check survives truncation | ../../../etc/passwd%00.png |
Start with the filesystem, not the string
The simple case is direct:
GET /image?filename=../../../etc/passwd
The application joins the filename to an image directory, then the filesystem resolves the traversal segments.
If traversal sequences are blocked, an absolute path may still work:
GET /image?filename=/etc/passwd
This shows why a blacklist of ../ is incomplete. The path can escape without containing a traversal sequence at all.
Filters are not canonicalization
Non-recursive stripping is a classic trap:
....//....//....//etc/passwd
Remove ../ once and the remaining characters collapse back into traversal. Filtering strings is not the same thing as resolving paths.
Double decoding is the same problem in a different layer:
..%252f..%252f..%252fetc/passwd
One decode turns %25 into %, leaving %2f. A later decode turns it into /. If validation happens between those steps, it validates the wrong representation.
Prefix and suffix checks are not containment
This string starts with the expected directory:
/var/www/images/../../../etc/passwd
The canonical path does not stay there. Directory containment must be checked after resolving the path.
The null-byte lab demonstrates the suffix version:
../../../etc/passwd%00.png
The validation layer sees .png. A lower-level file API or compatibility layer may truncate at the null byte and open /etc/passwd.
Defender notes
Hardening:
- prefer opaque file IDs mapped to server-side allowlists;
- decode and normalize exactly once before validation;
- resolve the canonical path with
realpathor an equivalent API; - check that the canonical path is inside the intended base directory;
- reject absolute paths, drive letters, UNC paths, mixed separators, encoded separators, and null bytes;
- treat extension checks as file-type checks, not directory containment;
- keep the web process unable to read sensitive OS paths.
Detection:
filename,path,file,image, oravatarparameters containing../,..\\,%2e,%2f,%5c, or%00;- nested traversal strings such as
....//; - double-encoded separators such as
%252f; - valid directory prefixes followed by traversal segments;
- image endpoints returning
/etc/passwd-like content; - file errors that disclose local base directories.
The rule is simple: validate the canonical path, not the user-supplied string.