Perfect Picture

Writeup for Perfect Picture (Web) - Imaginary (2023) 💜

Description

Someone seems awful particular about where their pixels go...

Source code is provided, so let's review it before we check the site.

Recon

There's 75 LOC in app.py so let's breakdown the important parts.

The storage location of uploaded images and allowed extensions are configured.

app.config['UPLOAD_FOLDER'] = '/dev/shm/uploads/'
app.config['ALLOWED_EXTENSIONS'] = {'png'}

When we upload a file, it splits on a . and looks at the rightmost split (extension). If the lowercase string matches the allowed extension (png) then the filename is allowed.

return '.' in filename and filename.rsplit('.', 1)[1].lower() in app.config['ALLOWED_EXTENSIONS']

Next, a random image name is generated.

img_name = f'{str(random.randint(10000, 99999))}.png'

A check function is called which will first read the flag into a variable.

with open('flag.txt', 'r') as f:
	flag = f.read()

The dimensions of the image must be 690 x 420 (w x h) and specific pixels need match the expected colours.

with Image.open(app.config['UPLOAD_FOLDER'] + uploaded_image) as image:
	w, h = image.size
	if w != 690 or h != 420:
		return 0
	if image.getpixel((412, 309)) != (52, 146, 235, 123):
		return 0
	if image.getpixel((12, 209)) != (42, 16, 125, 231):
		return 0
	if image.getpixel((264, 143)) != (122, 136, 25, 213):
		return 0

Next, exiftool confirms that the metadata is as expected.

if metadata["PNG:Description"] != "jctf{not_the_flag}":
	return 0
if metadata["PNG:Title"] != "kool_pic":
	return 0
if metadata["PNG:Author"] != "anon":
	return 0

If all the checks pass, the flag will be returned!

Solution

OK, so based on our analysis we need to create an image with the following properties:

  • Dimension (w x h) of 690 x 420

  • Pixel (412, 309) is (52, 146, 235, 123)

  • Pixel (12, 209) is (42, 16, 125, 231)

  • Pixel (264, 143) is (122, 136, 25, 213)

  • Image description is jctf{not_the_flag}

  • Image title is kool_pic

  • Image author is anon

I'm lazy, so asked ChatGPT to make a python script (note: exif packages failed for me, as they were strict on keys so used subprocess with exiftool instead).

from PIL import Image, ImageDraw
import subprocess

# Define the image dimensions
width = 690
height = 420

# Create a blank image with a white background
image = Image.new("RGBA", (width, height), (255, 255, 255, 255))
draw = ImageDraw.Draw(image)

# Set the specified pixel values
pixels = {
    (412, 309): (52, 146, 235, 123),
    (12, 209): (42, 16, 125, 231),
    (264, 143): (122, 136, 25, 213)
}

for (x, y), color in pixels.items():
    draw.point((x, y), fill=color)

# Save the image without metadata
image.save("generated_image.png")

# Close the image
image.close()

# Add image description, title, and author as metadata using exiftool
description = "jctf{not_the_flag}"
title = "kool_pic"
author = "anon"

subprocess.run([
    "exiftool",
    "-Description=" + description,
    "-Author=" + author,
    "-Title=" + title,
    "generated_image.png"
])

We upload the generated image and receive the flag in return.

Flag: ictf{7ruly_th3_n3x7_p1c4ss0_753433}

Last updated