Spoofing W3C Geolocation from a Different Angle

Filed under Firefox, Hacking, Internet
Tagged as , , ,

The other day I watched an episode of Hak5 – Spoofing the W3C Geolocation API were Darren was introducing us to the W3C geolocation API. (Btw. Hak5 is so awesome, you definitely have to check them out right now if your don’t know them already). This API uses certain informations like IP address, RFID, WiFi and Bluetooth MAC addresses, and GSM/CDMA cell IDs to determine the exact location of a users computer. It’s implemented by browsers like Firefox and Chrome.

One can test the geolocation API by going to Google Maps and clicking on the “Show MyLocation” icon. The geolocation API is actually pretty simple. Browsers make it available through a JavaScript object, which can be queried by the web application for the location. For privacy reasons, the browser will ask you if it is allowed to detect your location, since it has to transfer WiFi addresses and the likes over the net.

    <script type="text/javascript">
    // locate position

    // callback function
    function displayPosition(pos) {
        var mylat = pos.coords.latitude;
        var mylong = pos.coords.longitude;
        alert('Your longitude is :' + mylong + ' and your latitude is ' + mylat);

A more complete example script including showing your position on a map can be found here.

Darren presented his ongoing work on spoofing the API by generating fake 802.11 beacon frames to fool the API into finding a fake location. His marvelous approach was not quite successful yet, but it definitely made me think about it. Since the API is implemented in the browser, it should be possible to hack the browser to spit out wrong data. The easiest approach would be to use Firefox since it is open source.

So I downloaded the latest source of Firefox and started my own build. Since Darren is using BackTrack 5 for a while,  and I was just playing with it too, I was going to compile the source on BackTrack 5. I am explaining the process of compiling Firefox in another post Building Firefox on BackTrack 5. There were  a couple showstoppers at first, but it finally worked out and in the end I had my first self made build of Firefox. I also had built it with debug symbols, so I could debug it with ddd.

Moving Somewhere Else

After looking around for a while it seemed like some stuff was happening in mozilla-central/dom/src/geolocation/nsGeolocation.cpp. But the actual location data was coming from somewhere else. After examining the call stack it seemed to be coming from something called NetworkGeolocationProvider. After searching around a bit, I realized that it was actually JavaScript code that was located in mozilla-central/dom/system/NetworkGeolocationProvider.js. This was just being too good, since debugging C++ is somewhat painful. Now I could modify this JavaScript file and only needed to restart Firefox. There was no further recompilation required from now on.

But changing NetworkGeolocationProvider.js didn’t work out that well at first. After playing around with it for a while I found out that there is a startupCache folder, which holds components like NetworkGeolocationProvider.js in a compact form. It’s located in Firefox’s user directory. That cache has to be deleted in order for the changes in NetworkGeolocationProvider.js to take effect.

NetworkGeolocationProvider.js is actually not that big. It basically makes a json rpc call to Google’s Geolocation API at https://www.google.com/loc/json. Most of the stuff happens in method onChange. I changed the method to skip the rpc call altogether:

onChange: function(accessPoints) {
    LOG("onChange called");
    this.hasSeenWiFi = true;

    var obj = {
        location: {
            latitude: 38.897669,
            longitude: -77.03655,
            accuracy: 18000

    var newLocation = new WifiGeoPositionObject(obj.location);
    var update = Cc["@mozilla.org/geolocation/service;1"].getService(Ci.nsIGeolocationUpdate);

Now let’s see what Google Maps thinks where my computer lives:

Holy cow, it worked. Google Maps was telling me that my machine was somewhere inside the white house.I just hope the secret service is not going to knock on my door for this.

More Debug Output

By studying NetworkGeolocationProvider.js I found out some interesting things. There is a Firefox preference setting that enables debug output to the console. It can be enabled by adding following line to your Firefox prefs.js:

user_pref("geo.wifi.logging.enabled", true);

The output looks something like this (values changed for privacy reasons):

*** WIFI GEO: startup called.  testing mode isfalse
*** WIFI GEO: watch called
*** WIFI GEO: onChange called
*** WIFI GEO: provider url = https://www.google.com/loc/json
*** WIFI GEO: client sending: {"version":"1.1.0","request_address":true,"access_token":"2:HgigENDl1b5hI9PH:KgYfCECwwjxHjuRX","wifi_towers":[]}
*** WIFI GEO: xhr onload...
*** WIFI GEO: service returned: {"location":{"latitude":-78.070714,"longitude":15.439504,"address":{"country":"Austria","country_code":"AT","region":"xxx","county":"xxx","city":"xxx","street":"xxx","postal_code":"xxx"},"accuracy":18000.0}}
*** WIFI GEO: shutdown  called

People like Darren might be interested in the values transmitted inside the parameter “wifi_towers”. Since I do not have wifi radio in this machine, it’s empty.

There is also another pref setting called geo.wifi.uri. One can specify a different server instead of the default server on https://www.google.com/loc/json per default. It would not be very hard to implement a simple JSON server which would spit out fake data. That way we wouldn’t even need to modify the browser.

Using the Geolocation API without a Browser

It might be useful to play around with Google’s API without having to go through the browser. I wrote a simple python script, that makes it possible to request the geo location data without even using the browser:


import json
import urllib2

url = "https://www.google.com/loc/json"
headers = {'Content-Type': 'application/json'}
args = {"version" : "1.1.0", "request_address" : True, "wifi_towers" : []}

# create arguments in json format
data = json.dumps(args)
print 'Sending:  ' + data

# perform the http post request
req = urllib2.Request(url, data, headers)
f = urllib2.urlopen(req)

# read the response
response = f.read()
print 'Response: ' + response

The script returns the same data that we already saw in the browsers debug log:

Sending:  {"request_address": true, "version": "1.1.0", "wifi_towers": []}
Response: {"location":{"latitude":-78.070714,"longitude":15.439504,"address":{"country":"Austria","country_code":"AT","region":"xxx","county":"xxx","city":"xxx","street":"xxx","postal_code":"8010"},"accuracy":25000.0},"access_token":"2:NoLQveYeYxfkNboW:efp2-9AbcWBLNbcE"}


I only scraped the surface of how the browser is obtaining the geo location data. Web apps should obviously not rely on the data returned by the geo location API for live and death situations. The W3C Spec clearly states:

No guarantee is given that the API returns the device's actual location.

It is nevertheless a very useful tool and learning about what the browser is doing under the hood to acquire the geo data was certainly fun.


  1. nonpublished says:

    I am trying to write a custom application using geoLocaiton.

    Please respond if anyone would want to know more about this task?

    We are an Investigative company that needs to be able to send email, or text message with a link, when user clicks on the link,
    We would get IP location, or GeoLocaiton sent back to our location without the users knowledge.

    We know the limitations of IP vs Geo,

    We would like to find an exploit that would bypass the geolocaiton notification for approval.

    We have money for this project, if you are qualified and want to talk to us please contact us here.

    this email will above is a disposable to eliminate spam.

    contact us so we can open a dialogue and our wallet to your expertise in this area.


  1. Geo Relocate Firefox Extention — netzgewitter

Post a Comment

Your email is never published nor shared. Required fields are marked *