EasyCTF 2018: Zippity
| Event | Challenge | Category | Points | Solves |
|---|---|---|---|---|
| EasyCTF 2018 | Zippity | Divers | 80 | ? |
Description
I heard you liked zip codes! Connect via nc c1.easyctf.com 12483 to prove your zip code knowledge.
TL;DR
Dans cette épreuve, il fallait se connecter à c1.easyctf.com et répondre à 50 questions au sujet des codes postaux, comme la superficie en eau, en moins de 30 secondes.
D’abord, il m’a fallu trouver la bonne base de données des codes postaux américains sur census.gov.
Ensuite, il fallait programmer le bot pour répondre aux questions en moins de 30 secondes.
Comprendre le challenge
Pour résoudre le challenge, il fallait répondre à 50 questions en 30 secondes à travers une connexion tcp.
Pour chaque code postal fourni, il y avait l’une des questions suivantes :
- Superficie terrestre (en m²)
- Superficie en eau (en m²)
- Longitude (avec 6 chiffres)
- Latitude (avec 6 chiffres)
Trouver la bonne base de données de codes postaux
Pour répondre aux questions, il me fallait trouver la bonne base de données.
Au début, j’ai essayé de récupérer des informations depuis différents sites web.
La plupart étaient incomplètes ou partiellement erronées.
Puis j’ai découvert le site census.gov qui stocke des données sur les ZIP Code Tabulation Areas :
https://www.census.gov/geo/maps-data/data/gazetteer2010.html
| GEOID | ALAND | AWATER | INTPTLAT | INTPTLONG |
|---|---|---|---|---|
| 00601 | 166659789 | 799296 | 18.180555 | -66.749961 |
| 00602 | 79288158 | 4446273 | 18.362268 | -67.176130 |
| … | … | … | … | … |
Le site stocke les différentes versions de la base de données (1 par an).
Après quelques tests, la bonne était celle de 2010.
Place au script
Pour finir, il fallait scripter en raison des 30 secondes imparties.
Un script commenté vaut mieux qu’un long texte, voici donc le script que j’ai réalisé (en python3).
import socket
import requests
# Socket connection
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('c1.easyctf.com', 12483))
# Making dictionary from file "ZIP Code Tabulation Area"
# https://www.census.gov/geo/maps-data/data/gazetteer2010.html
with open("Gaz_zcta_national.txt","r") as f:
l = f.readlines()
l = l[1:] # Remove first line (header)
dico = {} # dico[zipcode] = [Land Area, Water Area, Latitude, Longitude]
for elt in l:
elt = elt.replace(" ","")
elt = elt.replace("\r","")
elt = elt.replace("\n","")
m = elt.split("\t")
dico[m[0]] = [m[3],m[4],m[7],m[8]] # Land Area, Water Area, Latitude, Longitude
# Dictionary ready :)
rec = ""
while "easyctf" not in rec: # Keep answering until we get the flag
rec = s.recv(2048).decode("utf-8") # Get response from server
print(rec)
rep = None # Set client response to None
if "Round" in rec: # Get zipcode from servers response
zipcode = rec[-7:-2]
print("Zipcode: "+zipcode)
# Answering questions:
if " land area (m^2)" in rec:
rep = dico[zipcode][0] # Land Area
print("Land Area of "+zipcode+" is "+rep+"m².")
elif " water area (m^2)" in rec:
rep = dico[zipcode][1] # Water Area
print("Water Area of "+zipcode+" is "+rep+"m².")
elif " latitude (degrees)" in rec:
rep = dico[zipcode][2] # Latitude
print("Latitude of "+zipcode+" is "+rep+".")
elif " longitude (degrees)" in rec:
rep = dico[zipcode][3] # Longitude
print("Longitude of "+zipcode+" is "+rep+".")
if rep is not None: # If there is a response to send
s.send((rep+"\r\n").encode("utf-8")) # Send it
FLAG
easyctf{hope_you_liked_parsing_tsvs!}
Zeecka