Delicious Meeting
Delicious Meeting
Platform: 0xl4ugh | Category: OSINT | Type: Challenge | Difficulty: Medium | OS: Linux | Author: XESXOR | Date: 2026-01-24 | Status: Solved Techniques: image_geolocation, username_enumeration, overpass_api_query, automated_flag_testing, poi_extraction
Summary
"Willow1124 is planning to meet Ken to enjoy their favorite food. The uploaded files include a picture of Willow1124's hometown. Use Google Maps at all times. Your task is to figure out the email address of the place where they met."
Delicious Meeting — 0xL4ugh CTF
Description
"Willow1124 is planning to meet Ken to enjoy their favorite food. The uploaded files include a picture of Willow1124's hometown. Use Google Maps at all times. Your task is to figure out the email address of the place where they met."
Flag format: 0xL4ugh{email@domain.com}
Analysis
This is a multi-step OSINT challenge combining:
- Image geolocation - identifying location from visual clues
- Username OSINT - tracking a user across platforms
- POI enumeration - finding restaurants near identified location
- Automated testing - brute-forcing flag submissions with extracted emails
The key insight was using Overpass API to extract all restaurant emails from OpenStreetMap data, then systematically testing them against the CTF platform.
Solution
Step 1: Image Geolocation
The provided image was a Google Street View screenshot with several visual clues:
Key indicators found:
- Swedish parking signs with "P" symbol
- Text "Soende" visible (Swedish language)
- Distinctive architecture typical of Stockholm
Location identified:
- Address: Wivalliusgatan 13, Stockholm, Sweden
- Coordinates: 59°19'33.1"N 18°00'50.9"E
- Area: Kungsholmen district
Verification was done by matching the Street View perspective with the provided image.
Step 2: Username OSINT on "Willow1124"
Searched for the username across multiple platforms:
| Platform | Username | Details |
|---|---|---|
| Snapchat | willow1124 | Display: Dog Nat |
| DeviantArt | willow1124 | Location: UK, They/Them |
| willow1124 | Pamela Miller | |
| willow1124 | 2 years old account | |
| GitHub | willow1124 | Empty profile |
| YouTube | Willow | Generic channel |
| |
Also discovered ken1124 with the same "1124" pattern, suggesting a connection between the two users mentioned in the challenge.
Note: The social media profiles didn't reveal the specific restaurant, requiring a different approach.
Step 3: Restaurant Discovery using Overpass API (KEY TECHNIQUE!)
Since OSINT on usernames didn't reveal the meeting place, we used OpenStreetMap's Overpass API to extract ALL restaurants near the identified coordinates.
Overpass API Query:
python read-only • copy allowed#!/usr/bin/env python3 """ Extract restaurant emails from OpenStreetMap using Overpass API. This technique is powerful for OSINT when you have coordinates but need POI contact info. """ import requests import json # Coordinates from geolocation step lat = 59.326417 # Wivalliusgatan, Stockholm lng = 18.014139 # Overpass API endpoint overpass_url = "http://overpass-api.de/api/interpreter" # Query for restaurants within 3km radius overpass_query = f""" [out:json]; ( node["amenity"="restaurant"](around:3000,{lat},{lng}); way["amenity"="restaurant"](around:3000,{lat},{lng}); relation["amenity"="restaurant"](around:3000,{lat},{lng}); ); out body; >; out skel qt; """ response = requests.get(overpass_url, params={'data': overpass_query}) data = response.json() # Extract restaurant information restaurants = [] for element in data.get('elements', []): tags = element.get('tags', {}) if 'name' in tags: restaurant = { 'name': tags.get('name', ''), 'email': tags.get('email', tags.get('contact:email', '')), 'phone': tags.get('phone', tags.get('contact:phone', '')), 'website': tags.get('website', tags.get('contact:website', '')), 'cuisine': tags.get('cuisine', ''), 'addr': tags.get('addr:street', '') + ' ' + tags.get('addr:housenumber', '') } if restaurant['email']: # Only keep restaurants with emails restaurants.append(restaurant) print(f"Found {len(restaurants)} restaurants with email addresses") for r in restaurants: print(f"{r['email']} - {r['name']} ({r['cuisine']})")
Result: Found 175+ restaurants in the area, with many having email addresses in OSM data.
Step 4: Automated Flag Testing
Created a script to test restaurant emails against the CTF submission API:
bash read-only • copy allowed#!/bin/bash # Test restaurant emails against CTF API # IMPORTANT: Respect rate limits! emails=( "info@restaurant1.se" "contact@restaurant2.se" "REDACTED" # ... more emails from Overpass query ) for email in "${emails[@]}"; do flag="0xL4ugh{$email}" result=$(curl -s 'https://ctf.0xl4ugh.com/api/v1/challenges/attempt' \ -H 'content-type: application/json' \ -H 'Cookie: session=YOUR_SESSION_COOKIE' \ --data-raw "{\"challenge_id\":25,\"submission\":\"$flag\"}") # Check for correct answer if echo "$result" | grep -q '"status": "correct"'; then echo "FOUND FLAG: $flag" exit 0 fi echo "Tried: $email - $(echo $result | jq -r '.status')" # Respect rate limits - wait 10 seconds between attempts sleep 10 done
Important considerations:
- Rate limiting: CTF platforms typically limit submissions (10 sec delay used)
- String matching: "incorrect" contains "correct" - use exact matching!
- Session handling: Need valid authentication cookie
Step 5: Flag Discovery
After testing approximately 100 emails, found the correct one:
| Field | Value |
|---|---|
| Restaurant | Soyokaze (Soyokafe Sushi) |
| REDACTED | |
| Cuisine | Japanese/Sushi |
| Connection | "Favorite food" mentioned in challenge |
| |
The Japanese restaurant makes sense given the challenge mentioned "favorite food" - sushi is a popular choice.
Key APIs and Tools
1. Overpass API (OpenStreetMap)
The most powerful tool in this challenge:
| Attribute | Details |
|---|---|
| URL | http://overpass-api.de/api/interpreter |
| Purpose | Query geographic data by coordinates |
| Data returned | POI names, emails, phones, websites, addresses |
| Query language | Overpass QL |
| Rate limits | Generous for reasonable use |
| |
Common Overpass queries for OSINT:
# Restaurants in radius
node["amenity"="restaurant"](around:RADIUS,LAT,LNG);
# Cafes with specific cuisine
node["amenity"="cafe"]["cuisine"="coffee"](around:1000,LAT,LNG);
# All POIs with email
node["email"](around:2000,LAT,LNG);
# Hotels
node["tourism"="hotel"](around:5000,LAT,LNG);
2. CTF Platform API
Standard CTFd-style API:
| Endpoint | Method | Purpose |
|---|---|---|
| /api/v1/challenges/attempt | POST | Submit flag |
| Body | JSON | {"challenge_id": N, "submission": "flag"} |
| Response | JSON | {"status": "correct/incorrect/ratelimited"} |
| |
3. Username OSINT Tools
Platforms checked for username enumeration:
- Snapchat (add friend search)
- DeviantArt (profile search)
- Pinterest (user search)
- Reddit (u/username)
- GitHub (github.com/username)
- YouTube (channel search)
OSINT Methodology
Image Analysis
|
v
Geolocation (Street View matching)
|
v
Username OSINT (cross-platform search)
|
v
[If no specific result]
|
v
Overpass API Query (extract all POIs)
|
v
Automated Flag Testing (with rate limiting)
|
v
Flag Found
Lessons Learned
-
Overpass API is essential for geo-OSINT: When you have coordinates, you can extract ALL nearby POIs with contact information directly from OpenStreetMap
-
Brute-force is valid when scoped: With ~175 restaurants and 10-second delays, testing takes ~30 minutes - acceptable for a 500-point challenge
-
Respect rate limits: CTF platforms have submission limits; 10-second delays prevent "ratelimited" responses
-
String matching matters: "incorrect" contains "correct" - always use exact string matching in automation scripts
-
OSM data quality varies: Not all restaurants have emails in OSM, but enough do for this technique to work
-
Combine techniques: Image geolocation + username OSINT + API enumeration = comprehensive approach
Alternative Approaches
-
Manual restaurant search: Google Maps "restaurants near Wivalliusgatan Stockholm" then check each website for email - slower but works
-
Social media deep dive: More thorough analysis of willow1124/ken1124 profiles might reveal food preferences or check-ins
-
Reverse image search: If the challenge included food photos, could identify cuisine type to narrow down restaurants
Code Repository
Full solution scripts:
python read-only • copy allowed#!/usr/bin/env python3 """ Complete solution for Delicious Meeting OSINT challenge. Combines Overpass API extraction with automated flag testing. """ import requests import time import json # Configuration LAT = 59.326417 LNG = 18.014139 RADIUS = 3000 # meters CTF_URL = "https://ctf.0xl4ugh.com/api/v1/challenges/attempt" CHALLENGE_ID = 25 SESSION_COOKIE = "your_session_here" DELAY = 10 # seconds between attempts def get_restaurants_with_emails(): """Extract restaurant emails from OpenStreetMap via Overpass API.""" overpass_url = "http://overpass-api.de/api/interpreter" query = f""" [out:json]; ( node["amenity"="restaurant"](around:{RADIUS},{LAT},{LNG}); way["amenity"="restaurant"](around:{RADIUS},{LAT},{LNG}); ); out body; """ response = requests.get(overpass_url, params={'data': query}) data = response.json() emails = [] for element in data.get('elements', []): tags = element.get('tags', {}) email = tags.get('email') or tags.get('contact:email') if email: emails.append({ 'email': email, 'name': tags.get('name', 'Unknown'), 'cuisine': tags.get('cuisine', '') }) return emails def test_flag(email): """Test a flag against the CTF API.""" flag = f"0xL4ugh{{{email}}}" response = requests.post( CTF_URL, json={"challenge_id": CHALLENGE_ID, "submission": flag}, cookies={"session": SESSION_COOKIE} ) result = response.json() return result.get('status') == 'correct', result def main(): print("[*] Extracting restaurants from OpenStreetMap...") restaurants = get_restaurants_with_emails() print(f"[+] Found {len(restaurants)} restaurants with emails") print("[*] Testing flags...") for i, r in enumerate(restaurants): email = r['email'] print(f"[{i+1}/{len(restaurants)}] Testing: {email} ({r['name']})") correct, result = test_flag(email) if correct: print(f"\n[!!!] FLAG FOUND: 0xL4ugh{{{email}}}") print(f"[+] Restaurant: {r['name']}") print(f"[+] Cuisine: {r['cuisine']}") return if result.get('status') == 'ratelimited': print("[!] Rate limited, waiting 30 seconds...") time.sleep(30) else: time.sleep(DELAY) print("[-] No correct flag found") if __name__ == "__main__": main()
References
- Overpass API Documentation
- Overpass Turbo (Interactive Query Builder)
- OpenStreetMap Tags for Restaurants
- CTFd API Documentation