Neven Marić
FortID 2026

OSINTFortID 2026

OSINT Exam II

A six-question exam about the organisers' own team, answered from stripped photos and public sources: a race result by bib, a conference agenda, a YouTube description, a leaderboard API, venue cladding and mirrored vehicle lettering.

  • geolocation
  • reverse-image-search
  • race-results
  • conference-agenda
  • github
  • youtube
  • vehicle-identification
  • exam

What this one teaches

  • Visible content leaks as much as EXIF does. A bib URL, a browser address bar in a screenshot and vehicle decals each handed over a pivot the stripped metadata would have.
  • Race organisers publish name, bib and time together, so a readable bib number in a photo effectively publishes the runner's result.
  • Identify the operator, venue or URL from hard evidence first, and only then reason about candidate answers. Guessing a region first burned most of the attempts.
  • Individually harmless sources (results pages, personal repos, company blogs, branded vehicles) combine into a picture of people, travel and internal tooling.

Hints

Stuck? Open one hint, then go back and try again before reading the next.

Hint 1 of 2Show

Metadata is stripped from every file. Work from what is visible in the frame.

Hint 2 of 2Show

For the last question, read the lettering on the vehicle windscreen before thinking about which peak it was.

Full solution below. Spoilers ahead.

Task description

We regret to inform you that the OSINT certification you’ve received last year has expired. You can renew your certification by passing our new OSINT exam.

It links to last year’s exam in the organisers’ public CTF repository. The handout is data.zip with q1.png, q2.jpg, q3.jpeg, q5.jpg, q6.mp4, q6_1.jpeg and q6_2.jpeg. There is deliberately no q4. A remote service asks the six questions in order and, once all are right, prints the flag as a “certificate.”

How the service works

Each question has an exact answer format. A wrong answer ends the session with Try harder!, so every run has to replay all the earlier answers in order, which is what solve.py does over TLS. An expired instance replies with an HTTP 400 instead of the exam banner, which is a quick way to tell a dead instance from a wrong answer.

# Asks for Format
1 City, bib number and chip time of the race in q1.png <city> <bib> <hh:mm:ss>
2 A talk a team member gave at a large European identity conference <talk_title>
3 Composer of the concerto the sketched member played at her music-school final exam, plus the postal address of her family’s apartments on the Croatian coast <composer> <street> <no.> <postcode> <city> <country>
4 Best, worst and most active player on the office foosball table <best> <worst> <most_games>
5 Venue of the interoperability event in q5.jpg, plus the city and name of the CTF final a member reached by train the next morning <venue> <CTF_city> <CTF_name>
6 Peak the team hiked towards after the pub on the last team-building day <peak_name>

All EXIF and GPS data is stripped from the files. The only surviving metadata is a Samsung trailer in q2.jpg with a capture date of 2026-05-20, so every answer has to come from visible content. That one date turns out to be enough to anchor the conference in Q2.

Q1: the race, by bib

q1.png shows a running shirt from the team and a “MEN’S 10K” finisher medal. The bib carries a URL for an “Event Information Pack” page that is password protected, but its form posts the race number, which at least confirms the bib digits are read correctly. The race organiser’s public results search is a plain GET form that takes a race and a running number, so the same bib can be queried across years. It appears in several, with unrelated runners in the other years; the 2026 Edinburgh entry belongs to a team engineer, which supplies the city, the bib and the time.

GET https://<results-site>/results?rs=<race-id for 2026 Edinburgh>&running_number=<bib>&gender=any&age_category=any

The results row lists both a chip time and a gun time. Chip time (from crossing the start mat to the finish mat) is shorter than gun time (from the starting pistol), and the question asks for the chip time, so the shorter of the two is the answer.

Why a readable bib is as good as published results

Mass-participation races publish a searchable table tying each bib number to the runner’s name and finishing times, and the search is usually an open GET form with no authentication. A finisher photo that shows a legible bib therefore leaks the runner’s full name and result to anyone who types the number in, even with every scrap of file metadata removed. The defensive version of this lesson is to blur bib numbers before posting race photos, the same way you would blur a boarding pass or a parcel label.

Q2: the conference talk

A bottle of Club-Mate (a German soft drink common at European hacker events), Berlin street architecture in the background and the 2026-05-20 capture date together point to the EIC 2026 identity conference in Berlin (19 to 22 May), where the organiser was a sponsor. The conference publishes its agenda, and it lists a team member giving the talk “From EU Digital Identity Architecture to Real-World Services,” which is the exact string the exam wants.

Q3: composer and apartments

The caricature shows a woman with a violin at a pink-carpeted trade-fair stand in Amsterdam. A reverse image search led nowhere, and the platforms that might have carried the photo (LinkedIn, Instagram) don’t serve anonymous requests. But the whole exam is about the organiser’s own team, so I went through the women working there until one matched the caricature, which gave me the name. From there, two public footprints answered both halves of the question:

  • Her personal GitHub account hosts a small website for her family’s holiday apartments on the Croatian coast, and the site’s contact page lists the street address in exactly the format the exam asks for.
  • Her YouTube channel has an upload of the Bruch Violin Concerto No. 1 in G minor, op. 26, captioned as footage of her music-school final exam. That names the composer, Max Bruch.

Neither step needed a login. A public repo for a family business tied an employee to a home address, and a video caption supplied the composer.

Q4: foosball

A company blog post about integrating the product into the office foosball app includes a screenshot of the tablet. Its address bar shows the development hostname of the in-house Elo-rating app. The app’s leaderboard page calls a public api/leaderboard_single endpoint, which answers unauthenticated GET requests with each player’s name, rating, wins and losses. I made only read-only requests and did not touch the app’s forms. Sorting the returned JSON by rating gives the best and worst players, and sorting by wins plus losses gives the most active, in the format <best> <worst> <most_games>.

Q5: the interop venue and the CTF final

The organiser attended several EU digital-identity wallet interoperability events in 2026. Two were ruled out directly from the photo: one was held at a chateau-style building and one at a steel-lattice sports arena, and q5.jpg matches neither. The third, a proximity testing day in Paris on 12 June, fits. The host’s headquarters is clad in pale speckled granite, and street-level photos on Wikimedia Commons show the same cladding as the handout. A partner’s post that the organiser reshared confirms they were present.

The CTF came with a hint. The Midnight Flag CTF final was held in person in Rennes on 13 June 2026, about 1.5 hours from Paris by high-speed train, so a member could reach it the morning after the interop day. The “by train the next morning” phrasing in the question is itself the clue that the two cities are a short rail hop apart.

Q6: the peak

q6.mp4 shows right-hand-drive, ex-military canvas-top Land Rovers on a woodland track, and the two stills (q6_1, a grassy slope up to a rocky crest; q6_2, a rocky top above town lights) are a night hike up a small fell above a town.

We didn’t spot the windscreen lettering at first, so we did it the hard way and started guessing: a run of roughly twenty peaks across the Peak District and the Lake District, Mam Tor, Win Hill, Kinder Scout, Helm Crag, Catbells, Orrest Head, Wansfell Pike, Loughrigg Fell and more, all rejected. This was the expensive question.

What broke it open was looking at the windscreen again. The lettering is mirrored in the footage, but read back it says KANKKU OFF ROAD ADVENTURE. Kankku is a 4x4 off-road tour operator based in Windermere, in the English Lake District, running exactly these ex-military Land Rovers. That pinned the pub and the hike to the Windermere and Ambleside area, and from there it went quickly: Brant Fell is the classic short walk up from Bowness, with a rocky summit that looks straight down on the town lights, which matches q6_2. It was accepted.

The order that would have saved the whole guessing spree is the lesson of the exam: read the hard evidence in the frame, the lettering, before reasoning about which peak fits.

The answers sent

solve.py replays these over TLS (python -I solve.py <instance-host>), and after the sixth the service prints the certificate:

Q1  Edinburgh 14480 00:53:14
Q2  From EU Digital Identity Architecture to Real-World Services
Q3  Max Bruch Augusta Senoe 31a 23250 Pag Croatia
Q4  Paljak Kiki Dino
Q5  Docaposte Rennes Midnight Flag CTF
Q6  Brant Fell
The flagRevealFortID{Y0u_H4v3_N0w_B3com3_4_C3rt1fi3d_0p3n_50urc3_1n73l1genc3_Gr4ndM45t3r_4_L1f3_4459629e12}

This CTF was possibly solved, and the writeup most likely written by an LLM. Apologies for inconsistencies.