Geolocation Using BSSID

Posted: 22nd September 2010 by Matt in code, hacks, news, security
Tags: , , , , , , , , , ,

This was discussed at DefCon 18 in a talk by Sammy Kamkar, but as far as I know, Sammy didn’t release his code, so I had to come up with something on my own.

First, one big difference. His version of this uses the Google Location Services API. I’ve opted to use the Skyhook service instead because there’s far more documentation and sample code that exists using this API, whereas I was unable to find anything too terribly helpful when it came to using the GLS API for this particular purpose. If anyone has any insight on this, please, please, let me know. I’d like to incorporate that into this script for comparison data.

Ok, so, how does this work, exactly? Both companies (Google & Skyhook) have employed a large number of people to drive around with laptops, GPS’s, and cameras attached to the roofs of their car in order to create a database. Everyone is aware of street view by Google, but were you aware of the fact that they also record wireless information? Well, I guess probably most people are aware of that now, considering the issues they had in Germany, but what you probably weren’t aware of is what this data is used for. Google and Skyhook both provide this database for software based location systems.

So, whats in the database? Skyhook is pretty open about the fact that they’re collecting wifi data in order to provide better location services. They call it “XPS”, which combines information from wifi, GPS, and cell phone towers to pin point an exact location.

Anyway, what does that mean for us? It means that we can query this database with a BSSID from a wireless network and get the nearest address and coordinates returned. Thanks SkyHook! :-)

Here’s an example. Not too long ago I was in the cities and did some driving with a buddy of mine. I’ll demonstrate this using a BSSID that was in the log created on that drive:

$ ./getloc.pl 00:24:B2:1E:24:FE
490 Robert St N
Ramsey county
St. Paul, Minnesota 55101
Latitude: 44.95063
Longitude: -93.0940583

http://maps.google.com/maps?f=q&source=s_q&hl=en&geocode=&q=44.95063+-93.0940583&sll=37.0625,-95.677068&sspn=57.815136,114.169922&ie=UTF8&t=h&z=17

So, using just the BSSID I’m able to get a house number (in this case, a building number), street address, and the coordinates.

Here’s my code.. feel free to modify it/add to it/whatever.. but if you add anything cool, please let me know. :-)

#!/usr/bin/perl
# www.attackvector.org
# 
use LWP::UserAgent;
use XML::LibXML;
 
$url = "https://api.skyhookwireless.com/wps2/location";
$ua = LWP::UserAgent->new;
$handler = XML::LibXML->new();
 
$bssid= $ARGV[0];
$bssid =~ s/\://g;
 
if($bssid eq "") {
 print "Usage: $0 <bssid>\n";
 print "Example: $0 AA:BB:CC:DD:EE:FF\n";
 exit(0);
}
 
sub response {
    my ($response) = @_;
    $xml = $response->content;
    $xml =~ s/\n//g;
    $page = $handler->parse_string($xml);
    if((@{$page->getElementsByTagName('longitude')}[0]) ne "") {
        $lat = $page->getElementsByTagName('latitude');  
        $long = $page->getElementsByTagName('longitude');
        $streetnum = $page->getElementsByTagName('street-number');
        $streetname = $page->getElementsByTagName('address-line');
        $city = $page->getElementsByTagName('city');
        $zip = $page->getElementsByTagName('postal-code');
        $co = $page->getElementsByTagName('county');  
        $state = $page->getElementsByTagName('state');
        print "$streetnum $streetname\n";
        print "$co county\n";
        print "$city, $state $zip\n";
        print "Latitude: $lat\n";  
        print "Longitude: $long\n";
        print "http://maps.google.com/maps?f=q&source=s_q&hl=en&geocode=&q=" . $lat . "+" . $long . "&sll=37.0625,95.677068&sspn=57.815136,114.169922&ie=UTF8&t=h&z=17\n";
    } else {
        print "No results for $bssid\n";
    }
}
 
$request = "<?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>$bssid</mac>  
    <signal-strength>-50</signal-strength>  
  </access-point>  
</LocationRQ>";
$response = $ua->post( $url, 'Content-Type' => 'text/xml', Content => $request );
response($response);

If you come up with or know of any creative ways to remotely obtain a BSSID, please comment below. Sammy mentions using XSS, but this only works against the Verizon FiOS router. I’m thinking a Java applet or script and UPnP. I’ll let you know if I come up with anything interesting.

If you enjoyed this post, make sure you subscribe to my RSS feed! You can also follow me on Twitter here.
Share

Related posts:

  1. Invasion of Privacy.
  2. New DLL Hijacking Exploits (many!)
  3. Get Fined For Not Password Protecting Your Wireless Network.
  4. Favorite nmap NSE scripts
  5. Synflooding and iptables
  1. Joe Blow says:

    There may be an error in your code (of course I could be the one making an error). When I run this code I get a Perl error stating “parser error: Start tag expected, ‘<' not found" and "Can't connect to api.skyhookwireless.com:443 (Invalid argument)" at line 24.

    I assume the skyhookwireless web site is no longer valid, but I'm not sure why the parser error occurs. I didn't find any XML syntax errors, either on line 24 or elsewhere.

  2. HD says:

    Nice article.
    Thanks for sharing with the community!!!
    I hope to do the same for the community once I learn half of what you know.

    HD

  3. Matt says:

    @Joe: I just copied & pasted the script from the blog to a file and ran it and didn’t get any errors. Not sure what would be causing that error. Are you sure it copy & pasted correctly? (ie – no word wrapping/etc?)

    And, the url (api.skyhook…) is still valid.. the connect error is probably related to the parser error.

    Can you paste the entire output of the command?

  4. Erik says:

    @Joe: I copied/pasted the script into 2 locations (my work machine and my home ubuntu machine), both working now, however I did get the error you received on my ubuntu machine. I installed libnet-ssleay-perl, and libcrypt-ssleay-perl in order to fix the issue.

    @Matt: even with it working, I haven’t found a bssid that’ll give me any data. Not sure if the one you used was legit or not, or if you have one we can test. Otherwise, I’ll try again when i get out of work.

  5. Matt says:

    @Erik: I read a post today that said something about Skyhook only allowing you to look up information about BSSID’s “in your area”. They must use some sort of IP based geolocation to determine what “in your area” really is. Have you tried any BSSID’s that you’ve found around your place? I have a pretty high success rate from my drive through the cities.

    This gives me more motivation to figure out the Google API.. because I don’t believe GLS restricts results to areas.

  6. Erik says:

    @Matt: you are correct sir. I got it working on local ones no problem.

  7. Matt says:

    @Erik: Thanks for verifying that. I’m going to work on the Google API one to avoid the locale boundaries of Skyhook.

  8. [...] Geolocation Using BSSID – attackvector.org This was discussed at DefCon 18 in a talk by Sammy Kamkar, but as far as I know, Sammy didn’t release his code, so I had to come up with something on my own. [...]

  9. H. Carvey says:

    Very cool! I use something very similar in my most recent book (sadly, won’t be out until 15 Feb 2011) on Registry analysis; specifically, by getting the BSSID and MAC address for a WAP from the Registry, you can perform geolocation using SkyHook.

    I’ve mentioned that LE could use this…for example, determine that a suspect (by analyzing his computer) was in a particular location, then interview regulars in the area (baristas, etc.) or view security video footage. I even got a comment to one post on the topic that someone was able to determine that an employee had been to a competitor’s location for an interview when they took vacation. ;-)

  10. Matt says:

    @H. Carvey: That’s awesome.. but I’m a little bit let down by SkyHook, to be honest. When I get a chance, I’ll be researching the Google API and then using both services together.

    Whats the name of your book?

  11. Joe Blow says:

    @Erik: I installed libnet-ssleay-perl and libcrypt-ssleay-perl in Ubuntu as you described and it works great. The previous errors I described happened when running it under Mac OS 10.5.8 and Perl v5.8.8. I still haven’t gotten it working on my Mac, but here is the command output:

    :1: parser error : Start tag expected, ‘<' not found
    500 Can't connect to api.skyhookwireless.com:443 (Invalid argument)
    ^
    at ./getloc.pl line 24

  12. Matt says:

    @Joe Blow: This is totally a shot in the dark.. but.. that error looks like it’s being caused by the XML lib.. without doing any digging, it would appear that it’s searching for a starting point (ie, the “<"), and it's not finding it. This would lead me to believe one of three things..

    1) LibXML is broken
    2) the script is unable to connect to the website and since there's no error handling in the script for when a connection has failed, an empty $response is being sent to the response() function and it’s like, wtf?
    or 3) um.. actually, I'm pretty sure that it's 2.. so forget about the 3rd.

    Try adding this before the last line of the script (where it says "response($response)")

    Do something like, if($response eq "") { print "\$response is empty. wtf?\n"; exit; }

    If that turns out to be the problem, not sure what to tell you.. could really be anything.

  13. John Sat says:

    I find this whole thing extremely fascinating.

    Theoretically, if someone were able to develop an effective and universal method of returning the BSSID someone is currently connected to, or even better (assuming the computer they use to click the page is not connected wirelessly), one that can return the router MAC (or somehow even return the wireless MAC stored in its configuration), they could get most people’s almost-exact location at whim. I say “router MAC” because the factory defaults of almost all routers are to put the wireless MAC’s (and therefore the BSSID) last hex pair one or two digits above or below the router MAC, and so some simple experimenting could probably return an address.

    Samy does a good job of detailing the XSS, but it’s really impractical for this sort of thing. Creating a specific XSS URL for every imaginable router model and firmware version is not really possible. You can get the BSSID through some clever social engineering, but that too is hit and miss of course.

    I will be very interested in seeing if you can come up with any sort of web script (perhaps reverse engineering Firefox’s geo-location prompt so that it does not come up with a prompt?) that can return a BSSID.

  14. achillean says:

    As an alternative, I wrote a quick python module for people that want to use the Google Locations web service (what presumably Samy is using):

    http://www.surtri.com/2011/01/17/wifi-positioning-system/

  15. john says:

    first of all – A big thanks !!

    then.. a question :) . I modified this script so as to accept output from iwlist scan, respecting the XML format of : b l a h . but this doesn’t seems work. Is there a way I can provide multiple AP and get a better granularity in terms of location?

  16. Mendel says:

    Here in Brazil we have a broadband carrier giving modem+access point devices with an insecure firmware. The vulnerability is related to poor access control with the use of a standard “support” password in all devices. The concept behind support access is to give to an remote operator some information about the customer connection. But the separation is just visual. The admin pages can still be seen and modified even without its links.

    What I did was scan the operator’s IP range for random IPs, and try to connect to HTTP using the standard credentials, get some pages and regex them to acquire BSSID and SSID. After getting these data, I try to get the device location using Google Geolocation and a script finishes the process storing the data in a database and generating a KML file. So, I can see the vulnerable devices in Google Earth.

    I wrote two articles in my blog explaining this (in portuguese).

    I didn’t knew how to handle Skyhook’s API so I fellback to Google. I think Google’s WPS database is not stronger as Skyhook database is, and because of this article, I’ll write a proof of concept to compare both.

    Thanks!

  17. Leandro says:

    I could not use your perl program.

    C:\Perl64\bin>perl a.pl XX:XX:XX:XX:XX:XX
    Empty String at a.pl line 24

    Installled ActivePerl for Windows 7 64 bits
    Also, I added a new lib using “ppm install xml-libxml”.

Spam Protection by WP-SpamFree