overpass

overpassql

osinst

Competetion

iCTF24

Challenge Author

zebck

Date

Jan. 4, 2025

Flip phone beeps.\n\n.. .. ..\n.. .. ..\n.. .. ..\n\nHey (insert CTF players name)!\n\ndamn, yesterday was rough… remember that me and a couple of friends went out for partying?\nApparently, the combination of non-alcoholic beverages, good vibes and burgers made me lose control… seems like I got on a plane and went some place in the United States.\nNow that I have recovered from my party induced coma, I have absolutely no clue where I am.\n\nAll I know is that I can see a something high, maybe a monument? I would say it's within around 100m of the building's roof I woke up on.\nMy watch tells me that the building is 35.9 meters high. Now listen, I'm kinda lost and I need to get to this thing tomorrow. Can you help me figure out where I am?\n\nAlso, can you find the nearest airport that only has two runways?\n\nFlag format: The name of the building I am on, followed by the name of the monument nearby and the IATA code of the airport. All lower case, replace spaces with '_'\n\nictf{first_building_the_thing_airportcode}


Hints

None

Solution

The prompt asks us to find a building that is 35.9 meters high and 100m from a monument. We also need the nearest airport that has two runways. We can find this information using the Overpass API. This API has detailed information of various structures throughout the world. The API has a [website](https://overpass-turbo.eu/) where you can feed it requests. After getting a request, the website will display the results on a map. While this is useful, for this challenge, I mainly prompted the API through python so I could easily parse the results. Sending the request through python allowed me to not have to comb through as much of the API syntax as I would have needed to while still getting the flag. Website: ![](/media/writeup_images/ictf24/overpass/2025-01-03-22-49-57_overpass_.png) Since the first thing we needed to find was buildings that were 35.9 meters high, I created a request to give me all of those buildings. ```python overpass_query = """ [out:json]; ( way["building"]["height"="35.9"]; ); out body; """ ``` This query looks for all "ways" that are buildings whose height property is equal to 35.9. A way is just a sequence of connected points that represent an object in the world. A way can be any large physical structure, like roads, rivers, or walls. They come with a bunch of useful properties that we can sort through to get our desired result. After we get a response from our request, we can parse the json and throw all of the elements into a python list. I made each element a dictionary and put all the useful data in it that we would need later. I also created a helper function to grab the position of one of the points on the "way". This will be helpful later when we need to find close by monuments. ```python response = requests.get(overpass_url, params={'data': overpass_query}) data = response.json() buildings = [] print("Buildings that are 35.9 meters tall:") for element in data['elements']: try: building = { "name": element["tags"]["name"], "coords": get_node_by_id(element['nodes'][0]) } buildings.append(building) print(building["name"]) except Exception as e: continue ``` Here's the helper function to get the node (point) position. We just feed it the first node in the way which won't give us an exact position, but will get us close enough for our purposes. ```python def get_node_by_id(node_id): url = "http://overpass-api.de/api/interpreter" query = f""" [out:json]; node({node_id}); out body; """ try: response = requests.get(url, params={'data': query}) response.raise_for_status() data = response.json() if 'elements' in data and data['elements']: node = data['elements'][0] return { 'lat': node.get('lat'), 'lon': node.get('lon'), } else: print("node not found") return None ``` Here's all the buildings that the API grabs that are 35.9 meters tall: ``` Buildings that are 35.9 meters tall: Courvoisier Centre I Wasserturm Rüthen Steinman Hall New York Law School Marseilles Church of the Epiphany The Renaissance East River Plaza Garage Ashford 1000 峰景大廈 Edifício Bela Vista 警視庁野方庁舎 頤園大廈 Edifício I Un 7号棟 Griffith Observatory 永順閣 Edifício Weng Son ヴィラージュヴェール1番館 威龍閣 Edifício Vai Long Kok あいち造形デザイン専門学校1号館 KAビルディング 成運大廈 Edifício Seng Van 豪景大廈 Edifício Hou Keng Hong Tai Building B Hong Tai Building C Hong Tai Building A Hong Tai Building D Hong Tai Building E Hong Tai Building F Hong Tai Building G 104 ヴィラージュヴェール6番館 ヴィラージュヴェール10番艦 Saint Dominique Saint Honore Edifício Pimentel ``` Since we have the positions for all of these buildings, all we need to do now is sort through each one and send a request for monuments 100m around that point. The building that returns a valid monument is our correct building. I used the "around" keyword in my request to receive all of the nodes that were considered monuments. ```python def get_nearest_monu(building): radius = 100 lat = building['coords']['lat'] lon = building['coords']['lon'] query = f""" [out:json]; node["historic"="monument"](around:{radius},{lat},{lon}); out body; """ response = requests.get(url, params={'data': query}) data = response.json() if 'elements' in data: monuments = [] for element in data['elements']: monuments.append({ 'id': element['id'], 'lat': element.get('lat'), 'lon': element.get('lon'), 'tags': element.get('tags', {}) }) return monuments else: # no monuments around that point return [] ``` I then ran this function for every building we had and printed out the building that returned a monument. ```python for building in buildings: monu = get_nearest_monu(building) if monu: building["monuments"] = monu[0]['tags']['name'] print() print(building['name'],"Is 100 meters away from", building["monuments"]) print("Located at:", building['coords']['lat'], building['coords']['lon']) break else: continue ``` Output: ``` Griffith Observatory Is 100 meters away from Astronomers' Monument Located at: 34.1184286 -118.3000385 ``` Now that we have the building and the monument, all we need is the airport. We are looking for the nearest airport from our building that has two runways. Instead of making a request that takes the runways into consideration, I just tried to grab all the runways from a radius and see if I could get lucky. ```python query = """ [out:json]; ( way["aeroway"="aerodrome"](around:10000, 34.118, -118.300); ); out body; >; out skel qt; """ response = requests.get(overpass_url, params={'data': query}) data = response.json() print(data['elements'][0]['tags']) ``` I sent out this request which looks for airports 10000 meters around our building and it only returned one airport: ```python {'addr:state': 'CA', 'aeroway': 'aerodrome', 'alt_name': 'Bob Hope Airport', 'ele': '221', 'faa': 'BUR', 'gnis:feature_id': '1653290', 'iata': 'BUR', 'icao': 'KBUR', 'internet_access': 'yes', 'internet_access:fee': 'no', 'name': 'Hollywood Burbank Airport', 'old_name': 'Burbank-Glendale-Pasadena Airport', 'source_ref': 'geonames.usgs.gov', 'wikidata': 'Q598817'} ``` Looking up this airport, it had two runways. ![](https://lh7-rt.googleusercontent.com/docsz/AD_4nXdqnbLgPEVVX0Ixq3bJ-xEVGx5xdxsM1qBD0U-wLdF6u_UYUZGW4UH7QLJ20r2we4FDFKY4e9aQPTqACXrvjengzjrqtczYV0VxRxCRVbsOe2bPGolZWCx5BEGgW9MF04jX1_0wyQ?key=sIXekCBtfHx5rLGzWpDSSAJb) Since it was the only airport that returned, we know it's the closest one. All that's left to do is construct our flag from the rules given. ``` Flag format: The name of the building I am on, followed by the name of the monument nearby and the IATA code of the airport. All lower case, replace spaces with '_' ``` The IATA code is included in the response for the airport, combining all of this information together gives us our flag: ``` Ictf{griffith_observatory_astronomers_monument_bur} ```