| Field | Details |
|---|---|
| Platform | PortSwigger Web Security Academy |
| Type | Path Traversal (Null Byte Bypass) |
| Difficulty | Practitioner |
| Objective | Retrieve the contents of /etc/passwd |
File Path Traversal, Validation of File Extension with Null Byte Bypass¶
A shopping app displaying item images loads something like:
<img src="/image?filename=70.jpg">
Intercepting the request on Burp, the request looks like:
GET /image?filename=70.jpg HTTP/2
Trying earlier approaches:
GET /image?filename=../../../../../etc/passwd HTTP/2
returns "No such file", and:
GET /image?filename=/etc/passwd HTTP/2
also "No such file", and double-encoded traversal:
GET /image?filename=..%252f..%252f..%252f..%252f..%252f..%252fetc%252fpasswd HTTP/2
also returns "No such file". So plain traversal works at the filesystem level (presumably), but something else is rejecting the request — most likely a check on the file extension, since the app validates that filename ends with the expected extension (e.g. .png).
A null byte can terminate the string as far as the filesystem API is concerned, while the extension check on the original string still sees .png at the end:
GET /image?filename=../../../etc/passwd%00.png HTTP/2
The extension validation sees ...passwd%00.png ending in .png and passes it, but the underlying file read stops at the null byte and opens /etc/passwd. A PHP null byte (\0 or %00 in URL encoding) is a special character used to denote the end of a string in low-level languages like C.
While modern PHP natively handles null bytes, vulnerabilities arise when user input interacts with underlying C-based system libraries that blindly stop reading strings at the null byte. The legacy PHP null byte vulnerability (where a null byte chr(0) prematurely terminated strings in native C filesystem functions) was officially patched in PHP 5.3.4, released in December 2010 — but legacy systems still exist.
And with that, session finished.