Wargame: Natas
Level: 13
Category: Web Exploitation
Description
Same as natas12 but the server now calls exif_imagetype() on the uploaded file before storing it. Only files that pass the image type check are saved. The goal remains uploading an executable PHP file.
Overview
- PHP’s
exif_imagetype()reads only the first few bytes of a file to identify its type by magic number. - A JPEG file starts with the magic bytes
\xFF\xD8\xFF\xE0. - Prepending these four bytes to a PHP script makes
exif_imagetype()classify the file as a JPEG. - Combined with the filename override from natas12, the server stores and executes the PHP payload.
Background: Magic Bytes
- File type detection by magic number reads a fixed header at the start of the file.
- This check is separate from the file extension; a file named
.phpcan still pass a JPEG magic number check. - Functions like
exif_imagetype()do not verify the rest of the file content, only the header bytes.
Solution
Step 1: Prepend JPEG Magic Bytes to the PHP Shell
- Open the PHP payload file and insert the four-byte JPEG signature at the very beginning.
jpeg_magic = b"\xFF\xD8\xFF\xE0"
with open("inject.php", "r") as f:
content = f.read()
with open("inject.php", "wb") as f:
f.write(jpeg_magic + b"\n" + content.encode())
Step 2: Upload with .php Extension Override
- Use the same technique as natas12: set the
filenamePOST field toinject.php. - The server runs
exif_imagetype(), sees a JPEG header, and allows the upload. - The file is stored with the
.phpextension we specified.
Step 3: Execute the Shell
- Request the link returned in the upload response.
- PHP executes the script; the JPEG header bytes are output as garbage but the
file_get_contentsresult follows. - The password for natas14 is in the output.
Password
qPazSJBmrmU7UQJv17MHk1PGC4DxZMEP