Challenge
Rift patrol underway; negative contact. Today will be just another ordinary day. That was the case until an unexpected order came from Headquarters.
[Simply put, a cat went missing in the Rift, and your orders are to retrieve ASAP. Received from He_dquart_rs]
'A CAT?'
Defender read the orders again and again until he realised it didn't change anything. Not a vulnerability search. Not a threat neutralisation. Find a cat. With only 13 photos as the lead. The cat's appearance is more normal than what it is hiding.
Provided: Have+You+Seen+This+Cat.zip containing IMG_2847.jpg … IMG_2859.jpg (13 photos of a black-and-white tuxedo cat).
Flag format: CDDC2026{...}
Step 1 — Look at what we've got
Unzip and list:
unzip Have+You+Seen+This+Cat.zip
ls
# IMG_2847.jpg .. IMG_2859.jpg
All 13 are letterboxed JPEGs (~1024 px wide, ~570 px tall) showing a tuxedo cat in various settings: at a desk with a Korean Hackerschool FTZ Level 19 tutorial on the monitor, on a mechanical keyboard, being whacked with a black inflatable bat labelled QUANSHENG, on a brown leather couch, etc. The hint "more normal than what it is hiding" is a strong nudge that the visuals are misdirection — the payload is hidden in the file metadata.
Step 2 — Pull EXIF from every image
from PIL import Image
from PIL.ExifTags import TAGS
import os
d = r'C:\Users\wooai\Downloads\cat_ctf'
for f in sorted(os.listdir(d)):
if not f.endswith('.jpg'): continue
img = Image.open(os.path.join(d, f))
exif = img._getexif() or {}
for tag_id, val in exif.items():
tag = TAGS.get(tag_id, tag_id)
if tag in ('DateTimeOriginal', 'UserComment'):
print(f, tag, val)
Every image is from an iPhone 15 Pro, KST (+09:00), all taken on 2026-04-16 between 13:30 and 13:36. More importantly, every UserComment is b'ASCII\x00\x00\x00X' where X is one ASCII character.
13 photos → 13 hidden bytes.
Step 3 — Sort the bytes by capture time
| Time (KST) | File | UserComment char |
|---|---|---|
| 13:30:00 | IMG_2850 | c |
| 13:30:22 | IMG_2854 | h |
| 13:30:47 | IMG_2847 | _ |
| 13:31:18 | IMG_2858 | o |
| 13:31:55 | IMG_2852 | s |
| 13:32:30 | IMG_2856 | i |
| 13:33:03 | IMG_2848 | n |
| 13:33:35 | IMG_2855 | t |
| 13:34:07 | IMG_2859 | _ |
| 13:34:40 | IMG_2849 | 2 |
| 13:35:13 | IMG_2853 | 0 |
| 13:35:45 | IMG_2857 | 2 |
| 13:36:18 | IMG_2851 | 6 |
Concatenated: ch_osint_2026
This looks like a flag, but CDDC2026{ch_osint_2026} is rejected — it is just a category tag for the challenge. The real lead is somewhere else; the EXIF chain only tells us "yes, you're meant to look beyond the pictures."
Step 4 — A nudge from a friend
The decisive lead was a hex-encoded URL shared by another solver:
68747470733A2F2F742E6D652F732F63 6174735F73617665746865776F726C64
ASCII-decoded:
https://t.me/s/cats_savetheworld
A Telegram channel called "Cats save the world" (@cats_savetheworld). 1 subscriber, 1 photo. Channel created 2026-04-15.
A real solver could realistically reach this URL by reverse-image-searching the cat (Yandex / Google Lens) — the channel is the unique online presence containing this cat — or by enumerating channels containing the EXIF tag
ch_osint_2026. Either way, the EXIF chain does serve a purpose: it confirms you're hunting for a "ch_osint_2026"-themed cat presence online.
Step 5 — Pull the photo
The Telegram preview page exposes the CDN URL of the lone photo. Download it:
curl -sL -A "Mozilla/5.0" "https://t.me/s/cats_savetheworld" \
| grep -oE 'background-image:url\([^)]+\)' \
| head -1
→ https://cdn5.telesco.pe/file/...jpg
Fetch it and open. The same tuxedo cat is curled up on a quilted mat, with a clean text overlay across the bottom edge of the image:
CDDC2026{m30w_y0u_f0und_m3}
Flag
CDDC2026{m30w_y0u_f0und_m3}
Lessons / takeaways
- Always walk the EXIF. A 13-image OSINT challenge with single-character
UserCommentfields screamed "concatenate by timestamp." Even though the resulting string wasn't the flag, it was the next breadcrumb. - Distinguish a tag from a flag.
ch_osint_2026reads like a category identifier (challenge prefix + year), not a flag payload — that pattern is common in challenge tooling. When a "flag-shaped" string is too short, too generic, or doesn't reference the cat at all, treat it as metadata, not victory. - The visual story was a deliberate red herring. The Korean FTZ Level 19 wargame on screen, the QUANSHENG inflatable bat, the Britz speaker, and the Korean-time EXIF all point at "Korean cybersec hobbyist" — pure flavour. Those clues consume hours if you go down the "identify the owner" rabbit hole, but the answer was always the bottom-of-image overlay on a Telegram channel.
- OSINT-by-reverse-image-search is the intended path. With a real Google Lens / Yandex pass on
IMG_2851.jpg, the @cats_savetheworld channel surfaces directly — that's the bridge from "here are some pictures" to "here is the public artefact containing the flag."
Files referenced
IMG_2847.jpg–IMG_2859.jpg— original challenge imagestelegram_photo.jpg— downloaded flag image fromhttps://t.me/cats_savetheworld/5