eqqn Security blog

CTF write-ups

Home

EasyCTF2018 Zippity 80 points

Intro

This is my second year participating in EasyCTF,and I liked one challenge in particular.

I heard you liked zip codes! Connect via nc c1.easyctf.com 12483 to prove your zip code knowledge.

Image

Clearly looking up the codes manually is out of the question. One colleague of mine tried to enumerate enough responses to pass, but gave up after 80k unique entries ( 33.3k Zip codes * and 4 possible questions ~= 133.2k possibilities). At first trying to form a database using US Census website was proving futile, and some datasets were behind paywalls. After some more searching I found https://www.census.gov/geo/maps-data/data/gazetteer.html which contained the correct data, after testing a few questions and answers against the server.

Script

After messing about with Ruby CSV library, converting to JSON and Enumerables, I decided to just use grep . Ruby returns “MatchData” type to regex matches, so that was also a bother to manipulate. My C upbringing and noob scripting skills really show.

require 'socket'

def answerer ( zc, question)
    line=`grep '^#{zc}' db.csv|head -1` #match first zipcode in the file. ^ means line begins with 
    arrayified=line.split(",") #put the output into array 
    ans=(arrayified[question]) # return the answer to question ||1=LAND|| 2=WATER || 3=LAT|| 4=LONG
    puts "Answer is #{ans}"
    return ans
end

#regex for catching questions
landre=/land/
waterre=/water/
longitudere=/longitude/
latitudere=/latitude/

#TCP client
hostname = 'c1.easyctf.com'
port = 12483
string=""
s = Socket.new Socket::AF_INET, Socket::SOCK_STREAM
s.connect Socket.pack_sockaddr_in(port, hostname)

  
while char = s.recv(1)  # Read chars from the socket
   string+=char
   
   print char    
   if char=="?" # detect question
       
       zipcode = string.scan(/[0-9]{5}/)
       #zipcode=zipre.match(string)
       zipcode=zipcode[0].to_i.to_s #perhaps an ugly hack, but strips leading 0-es fast
       
       if landre.match(string)
            s.puts(answerer(zipcode,1))
       end    
       
       if waterre.match(string)
            s.puts(answerer(zipcode,2))
       end    
       
       if latitudere.match(string)
            s.puts(answerer(zipcode,3))
       end    
       
       if longitudere.match(string)
            s.puts(answerer(zipcode,4))
       end  
       
       string=""  # empty for next question
   end
end

puts(string)
s.close()

Look at it go!

Image

You can find the code and database on my GitHub

Problems encountered