coderrr

September 10, 2008

Get the physical location of wireless router from its MAC address (BSSID)

Filed under: network, ruby, security, slashdotted — Tags: , , , — coderrr @ 1:20 am

Shameless Plug: Protect yourself with public wifi security while connected to public hotspots with a VPN Service.

Update: Here’s a coverage map showing what areas they have data on.

A nice company called SkyHook Wireless has been war driving the country for years. Basically they’ve been paying people to ride around in cars and record the unique IDs (BSSID, aka MAC address) that your wireless routers broadcast. Doesn’t matter if your router has encryption on or not, its BSSID is still public and in their database (if they’ve driven past your house). They’ve then taken all this information and put it in a huge database. They’ve even made a nice little javascript API which given a BSSID will tell you its longitude and latitude. But it will only let you do this for yourself, only sending BSSIDs which you are in range of.

For their API to work it requires you to install a browser extension. Which contains, along with the extension source code (which is fully viewable, for Firefox at least), some compiled c++ code (loki.dll for windows). So what does the proprietary stuff do? It does the actual query to their API. And what does it send? It asks your wireless card to list all of the BSSIDs that you are in range of and sends those along with the signal strength of each.

So why can’t you just send any BSSID you want? Simple, because they don’t tell you how. The actual query is done inside of their compiled code, so it’s a secret and no one will ever figure it out. Well, only the people that try at least. After reverse engineering their code I did a google search on one of the unique-ish terms in the XML that is used as part of the API call and it seems there are others who know how to use this secret API of theirs.

So to keep things short. Here’s how to query their service to find the physical location of any wireless router whose BSSID you know.

Send an HTTPS POST request to api.skyhookwireless.com/wps2/location with XML in the following format:

<?xml version='1.0'?>
<LocationRQ xmlns='http://skyhookwireless.com/wps/2005' version='2.6' street-address-lookup='full'>
  <authentication version='2.0'>
    <simple>
      <username>beta</username>
      <realm>js.loki.com</realm>
    </simple>
  </authentication>
  <access-point>
    <mac>00AA11BB22CC</mac>
    <signal-strength>-50</signal-strength>
  </access-point>
</LocationRQ>

You’ll receive back either this (success!):

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<LocationRS version="2.6" xmlns="http://skyhookwireless.com/wps/2005"><location nap="1"><latitude>49.2422507</latitude><longitude>11.4624963</longitude><hpe>150</hpe></location></LocationRS>

or this (failure!):

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<LocationRS version="2.6" xmlns="http://skyhookwireless.com/wps/2005"><error>Unable to locate location</error></LocationRS>

Here’s a dirty little ruby script which does the query based on a BSSID you pass in from the command line:

require 'net/https'

mac = ARGV.first.delete(':').upcase

req_body = %{
<?xml version='1.0'?>
<LocationRQ xmlns='http://skyhookwireless.com/wps/2005' version='2.6' street-address-lookup='full'>
  <authentication version='2.0'>
    <simple>
      <username>beta</username>
      <realm>js.loki.com</realm>
    </simple>
  </authentication>
  <access-point>
    <mac>#{mac}</mac>
    <signal-strength>-50</signal-strength>
  </access-point>
</LocationRQ>
}.gsub(/^\s+|[\r\n]/, '')

http = Net::HTTP.new('api.skyhookwireless.com', 443)
http.use_ssl = true

http.start do |h|
  resp = h.post '/wps2/location', req_body, 'Content-Type' => 'text/xml'

  if resp.body =~ /<latitude>([^<]+).+<longitude>([^<]+)/
    puts "#$1, #$2"
  else
    puts "not found"
  end
end

Usage:
getloc.rb aa:bb:cc:dd:ee:ff

Or here’s a one liner for UNIX thanks to George:

MYMAC=AABBCCDDEEFF && curl --header "Content-Type: text/xml" --data "<?xml version='1.0'?><LocationRQ xmlns='http://skyhookwireless.com/wps/2005' version='2.6' street-address-lookup='full'><authentication version='2.0'><simple><username>beta</username><realm>js.loki.com</realm></simple></authentication><access-point><mac>$MYMAC</mac><signal-strength>-50</signal-strength></access-point></LocationRQ>" https://api.skyhookwireless.com/wps2/location

* note: This API is how the iPhone’s “Locate me” feature works

The Silver is the New Black Theme. Create a free website or blog at WordPress.com.

Follow

Get every new post delivered to your Inbox.

Join 27 other followers