OSINTFortID 2026
Melankonimir
Generation metadata in an AI image names its Civitai post. The uploader's matching Tumblr blog links shared ChatGPT chats, one of which leads to a public Hevy workout profile whose custom exercise image carries the flag.
What this one teaches
- Generation metadata in an image's EXIF or XMP can point straight at the post it came from and the account that uploaded it.
- Reusing one handle across platforms (here Civitai and Tumblr) ties the accounts together with no further work.
- A shared ChatGPT page embeds the whole conversation with timestamps, so the one chat where the persona talks about themselves, and predates the staged posts, stands out from the props.
- Fitness apps serve routines, the latest workout and uploaded custom images to logged-out visitors through the same API the web app uses.
- When exact handle lookups fail, try a leetspeak spelling of the name.
Hints
Stuck? Open one hint, then go back and try again before reading the next.
Hint 1 of 3Show
The handout's metadata block is worth reading in full, not just the visible prompt.
Hint 2 of 3Show
Four of the five shared chats are scenery. One is written in the first person and is older than the rest.
Hint 3 of 3Show
The account you want uses a leetspeak spelling of the persona's first name.
Full solution below. Spoilers ahead.
Task description
She dwells with Beauty—Beauty that must die.
The handout is handout.jpg, an AI-generated image of an abandoned hospital room. The task text is a line from Keats’s Ode on Melancholy, and the title is a pun on melankolija (melancholy) plus a name ending in -mir. The job is to identify the persona behind the image, follow them across the platforms they use, and find where they left the flag. I keep the persona’s handles, the ChatGPT share IDs, the storage URL and the app’s embedded API key out of this writeup, since they are live identifiers; the one identifier I show is the Civitai image number, because the handout itself hands it to you.
From the image to Civitai
The visible part of the handout is the generation prompt, but the useful clue is in the metadata. Reading the file’s EXIF and XMP shows the prompt again plus one added field, an XMP dc:identifier reading:
Civitai image reference 144093319
Apart from that one injected tag, the handout is byte-identical to the Civitai original (same length, same bytes around it), so nothing else is hidden in the file: the tag is the whole payload. Civitai image 144093319 belongs to a user whose handle puns on “prompt” and “generative.” The account has four AI images (the hospital, an old gym, a bike on a railway, a bike in the mountains) and a bio about loving abandoned places and trying to capture quiet decay and forgotten memories.
Why generation metadata is such a reliable pivot
Image generators and the sites that host their output routinely stamp provenance into the file: the prompt, the model, sampler settings, and increasingly a link back to the hosting post. It rides in EXIF UserComment, in a PNG tEXt/iTXt chunk, or, as here, in an XMP packet. People scrub the obvious EXIF (camera, GPS) and forget the rest, so a single surviving dc:identifier can tie an anonymous-looking image straight to a named account. Reading the metadata in full, rather than eyeballing the picture, is the first move on any image handout for exactly this reason.
From Civitai to Tumblr
The same handle exists on Tumblr. The blog is titled “Lost and forgotten,” was created on 2026-10-01, and all five of its posts went up that one evening. Each post pairs a ChatGPT re-render of an abandoned-place image with a one-line caption and the ID of a public ChatGPT share. Reusing the handle is what links the two accounts; nothing more subtle is needed.
One of the chats is personal
A ChatGPT share page does not just show a transcript, it embeds the entire conversation. The page ships it as a React Router “turbo-stream” inside a sequence of streamController.enqueue(...) calls in the HTML. Decoding those reconstructs the full mapping tree: every message, its author role, its metadata and its Unix timestamps. That matters because the timestamps survive even when the displayed text looks generic.
Four of the five chats are interchangeable questions about abandoned places, each created on 2026-10-01 shortly before the matching Tumblr post, which marks them as props built for the blog. The gym chat is different in two ways:
- Its messages date from 2026-09-24, a week before the blog existed, so it is a real moment in the persona’s life and not set dressing for a post.
- It is the only chat where the persona talks about themselves: returning to weight training, wanting a workout log that tracks weights, reps, sets and PRs over time, and specifically the ability to “add custom exercises for unusual gym equipment.”
ChatGPT’s reply recommends Hevy, a workout tracker with public user profiles. The shares carried nothing else useful: no edited branches, no custom GPT, no sharer name, and tool output redacted as usual. The custom-exercise detail is the one that pays off two hops later.
Finding the Hevy account
Exact lookups for the Civitai and Tumblr handle, for the title pun, and for dozens of variants all returned unknown-user. The account uses a leetspeak spelling of the persona’s first name (the display name in the app is just the first name), which only turned up once I tried the leetspeak form rather than the handle used elsewhere. That one mismatch, same person, different spelling, was the step most likely to stall the whole chain.
Hevy’s web app loads profile data from its API using a public key that ships in its JavaScript bundle, so the public profile endpoint answers without any login:
curl -s -H "x-api-key: <public web key from the JS bundle>" -H "Hevy-Platform: web" \
https://api.hevyapp.com/public_user_profile/<leetspeak-name>
{"private_profile": false, "workout_count": 8, "follower_count": 5,
"routines": [{"title": "Upper body A"}, "..."],
"last_workout": {"name": "Upper body B", "exercises": [ "..." ]}}
The account’s activity lines up with the ChatGPT timeline: workouts in the weeks of 2026-09-21 and 2026-09-28, routines created a day or two after the gym chat. The four routines use only standard exercises with no notes, so the payload is not in them.
Reading a private flag off a "public by default" app
Many consumer apps expose more to logged-out visitors than their users expect, because the same read API that powers the signed-in web client also answers unauthenticated requests. A key embedded in a public JS bundle is not a secret, it only identifies the client, so anyone can replay the call. Here the public profile object includes the routines and the most recent workout in full, custom exercises and their image URLs included. The response also distinguishes “does not exist” (an error object) from “exists but private” (a profile object with private_profile: true), which made it possible to tell wrong guesses apart from a real-but-hidden account while hunting for the handle.
The custom exercise
The last_workout object has three normal exercises and one custom one:
{
"title": "Hacking",
"exercise_type": "duration",
"muscle_group": "full_body",
"sets": [{"duration_seconds": 2880}],
"custom_exercise_image_url": "https://<bucket>.s3.<region>.amazonaws.com/profile-images/<user>-<uuid>.jpg"
}
This is the “custom exercise for unusual gym equipment” the gym chat asked about. Its uploaded image is a Hevy-style anatomy illustration of a man at a computer desk with his brain highlighted as the working muscle, a 600x600 JPEG, and the flag is printed along the bottom edge. I downloaded it and enlarged the strip to settle the characters, in particular that every 1 (including the one in the final word) shows the numeral’s flag and base rather than a lowercase l:
curl -s -o hacking_exercise.jpg "<custom_exercise_image_url>"
ffmpeg -i hacking_exercise.jpg -vf "crop=340:26:0:567,scale=1700:130:flags=lanczos" flag_zoom.png
I did not embed the image, because it shows the flag. I confirmed the flag against the decoded image (the phrase reads “Hacking is the heaviest exercise”).
Dead ends
I looked for the “songs” mentioned in the Civitai bio across the usual music platforms, and for the persona on Instagram, Threads, Strava, Pinkbike, Pinterest and TikTok. Nothing turned up. Reverse image search found no matches, and neither the Wayback Machine nor archive.today had snapshots of the edited Civitai and Tumblr posts. About fifty wrong Hevy usernames returned unknown-user; since a private profile returns an object with private_profile: true instead, that error meant the name simply did not exist, which kept the handle search honest.
The flagReveal
FortID{H4ck1n9_15_7h3_Hev1357_3xerc1s3}This CTF was possibly solved, and the writeup most likely written by an LLM. Apologies for inconsistencies.