Customers are increasingly relying on multi-factor authentication and location services for fine-grain access control. This IoT project shows how a group of IoT devices can spoof known Wi-Fi access points, making a user appear to be in a different location.
The impact? Access that Google/Apple/MS location services say is in your data center or office could actually be from anywhere in the world.
Location services use a combination of:
- GPS – The latest GPS receivers can pinpoint a device within 30cm! This degrades when indoors and the signal is impaired, but that smartwatch you are wearing could potentially tell an employer if your hands are on the keyboard, how often you check your smartphone and take breaks. It could also be used to identify if you are physically at the device you are attempting to login to.
- Wi-Fi Hotspots and Cell Towers – Google, Microsoft, Apple, and others maintain crowdsourced locations for WIF hotspots (routers) and cell towers. Crowdsourced in this case means that the OS of the device records nearby hotspots and towers, then periodically send this back to the vendor.
- Bluetooth – Typically used to identify devices up to 10 meters away. This is famously being used by both Google Android phones and Apple iPhones in their COVID contact tracing apps.
- IP address and network – City level location can be inferred by checking the client IP address for a web request. ISPs have a unique range of IP addresses that they assign to routers, and that IP is included in a web request. This can be hidden with a VPN, making the user appear to be in a different location. VPNs are typically used to avoid tracking or to access geo-restricted content.
I will cover two scenarios for Wi-Fi spoofing. Part 1 of this post is where no other wifi hotspots are present, and part 2 will show wifi spoofing in a dense environment with over 60 other wifi spots.
Crowdsourced Wi-Fi Network Locations (wigle.net)
To spoof a location, you first need to find Wi-Fi signals to copy. One option is to do wardriving where a laptop detects and records the wifi signal in the area. A simpler option is to use a crowdsourced database of known Wi-Fi routers, such as wigle.net.

Zooming in shows the WIFI broadcast name (SSID) and the router MAC address. An example from the Vancouver cruise terminal and conference centre is shown below:

If you are in a location with no other Wi-Fi signals, you could just set up a few routers with the same SSIDs and cloned MAC addresses. You would need a hardwired internet connection or some other non-location enabled connection. Here’s the solution I chose:

The routers in this picture are small IoT microcontrollers that have wifi and are running open-source WIFI access point software. The ESP8266s don’t actually have any internet connection, they only broadcast as access points. I use my iPhone as a hotspot, but have location services disabled so the iPad cannot find location from the iPhone. The iPad instead uses Wi-Fi location services at the iOS level to check for nearby Wi-Fi routers, and only sees the 4 spoofed SSIDS/MACs from the ESP8266 devices.
The ESP8266 is an ideal device in this scenario for several reasons:
- Very low cost: An ESP8266 microcontroller is about $4.
- Low signal: The antenna is small and does not broadcast far, which is perfect for our use case.
- Low power: An ESP8266 running access point software consumes roughly 0.3 watts.
- Compact: I use 4 ESP8266s in my example, but this could easily be scaled up to 100 devices using only 30 watts combined power.
- Sample Wi-Fi AP software: The Arduino IDE has sample code to enable the ESP8266 as an access point.

The ESP8266 model I used is a WEMOS D1 Mini clone, with 4mb RAM and an 80mhz single-core 32-bit processor. This is easily able to run the access point code and feels like overkill for this scenario (if a $4 device using 0.3 watts could be considered overkill).
A brief overview of the Wi-Fi spoofer functions:
- setup() sets the Wi-Fi mode to soft AP, output power, SSID, and password, then starts the server. It also registers the two functions handleRoot() and handleUpdate()
- handleRoot() handles the initial connection from the client and displays a webform that accepts two inputs: SSID and MAC address.
- handleUpdate() handles the request from the input form and sets the SSID and MAC address.
- loop() runs continuously, calling handleClient() to manages any web requests.
- initVariant() is the post init() function that makes it possible to set the new MAC address and have it stick.
I initially set the SSID as ‘AP-Local’. You need to connect to AP-Local from the iPad, enter the password, and then go to 192.168.4.1. The webform is displayed, where you enter the spoof SSID and MAC address you found at wiggle.net. The ESP8266 will restart and you will see an AP with the spoofed SSID. Repeat the process by connecting to the next AP-Local, and set the next spoofed SSID/MAC. Repeat until you have set every ESP8266. Now you will have multiple spoofed access points and can connect to the internet using the iPhone hotspot. Start using any app with location services on the iPad, which will scan Wi-Fi for nearby access point and see only the spoofed SSIDs/MACs. (Your hotspot will not share location if its location services are disabled.) Your location will match the spoofed location.
/* Create a WiFi access point and provide a web server on it. */
extern "C" {
#include "user_interface.h"
}
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#ifndef APSSID
#define APSSID "AP-Local"
#define APPSK "mypwd"
#endif
// Set these to your desired credentials.
char *ssid = APSSID;
char *password = APPSK;
//init sw mac to intentionally invalid mac so defaults to hw mac.
uint8_t mac[] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66};
//Set power output. 0 to 20.5 dbm in 0.25 increments.
float dBm = 1; //set very low for proximity and low power use.
void initVariant() {
wifi_set_macaddr(SOFTAP_IF, &mac[0]);
}
ESP8266WebServer server(80);
// Initial setup form to enter SSID and MAC. (http://192.168.4.1)
void handleRoot() {
server.send(200, "text/html", "<h1>You are connected on the road to nowhere.</h1><form action=\"/update\" method=\"POST\"><input type=\"text\" name=\"ssid\" placeholder=\"ssid\"></br><input type=\"text\" name=\"mac\" placeholder=\"mac\"></br><input type=\"submit\" value=\"Update\"></form><p>Open Wifi settings and reconnect to new SSID after submitting form.</p>");
}
void handleUpdate() {
server.send(200, "text/html", "<h1>Updating ssid and mac. Reconnect to AP.</h1>");
delay(1000);
if(server.hasArg("ssid") && server.hasArg("mac")) {
const char *mac_new = (server.arg("mac")).c_str();
const char *ssid_new = (server.arg("ssid")).c_str();
unsigned char cmac[6];
sscanf(mac_new, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", &cmac[0], &cmac[1], &cmac[2], &cmac[3], &cmac[4], &cmac[5]);
Serial.print("");
Serial.print("Setting mac to: ");
Serial.println(server.arg("mac"));
wifi_set_macaddr(SOFTAP_IF, &cmac[0]);
WiFi.mode(WIFI_AP);
WiFi.softAP(ssid_new, password);
IPAddress myIP = WiFi.softAPIP();
Serial.printf("MAC STA address = %s\n", WiFi.softAPmacAddress().c_str());
Serial.println("Completed HTTP request.");
}
}
void setup() {
delay(1000);
Serial.begin(115200);
Serial.println();
WiFi.setOutputPower(dBm);
WiFi.mode(WIFI_AP);
WiFi.softAP(ssid, password);
Serial.print("ESP8266 MAC: ");
Serial.println(WiFi.macAddress());
IPAddress myIP = WiFi.softAPIP();
Serial.print("AP IP address: ");
Serial.println(myIP);
Serial.printf("MAC STA address = %s\n", WiFi.softAPmacAddress().c_str());
server.on("/", handleRoot);
server.on("/update", HTTP_POST, handleUpdate);
server.begin();
Serial.println("HTTP server started");
}
void loop() {
server.handleClient();
}
I was curious to see if this actually would work. It does with just 4 spoofed access points as long as there are no other APs in sight. If the iPad location services detects another AP that reports a different location, the iPad gives errors such as ‘cannot detect location’, or resets back to the actual location.
CAUTION: If you test this, be sure not to expose the spoofed APs to other devices. I believe it’s possible to poison the crowd sourced database by reporting a false location for the APs you are spoofing. Either test in a non Wi-Fi area where there are no other routers, or use a faraday cage to isolate the spoofed signals. (I’ll cover the faraday cage in part 2.)
Here are the results of a test I did while on a Vancouver Island ferry in the Bay of Georgia between Powell River and Cortenay BC. The spoofed location was Bellingham Washington.
The initial test of ‘Find My’ app on iPad reported the iPad in Bellingham, my location-disabled iPhone near Vancouver (a 3 days old location), and my Apple watch correctly on the ferry.

A test of Google Maps on the iPad shows the location as Bellingham:

Apple Maps also shows the location as Bellingham:

Interestingly, the ‘Find My’ app on the iPad later switched my iPhone and Apple watch to Bellingham, while my wife’s iPhone and Apple watch show our true location in the Strait of Georgia:

So, with only 4 spoofed Wi-Fi access points, I was able to make my iPad think it was breaking COVID quarantine across the US border! It was a surprise to see the watch and iPhone also report they were in Bellingham, obviously accepting the iPad reported location as their own. I did have the iPhone’s location services turned off and the watch in airplane mode to hopefully disable GPS.
This test shows it is easy and inexpensive to defeat location services when only WiFi is used. Companies should not rely on location services to confirm a user’s location, especially if used to restrict logins to specific locations. A hacker in another country could easily use the crowd sourced WiFi data to spoof their location and attempt a ‘local’ login.
What can be done to mitigate this security risk? 1. Ask wigle.net to unlist your sensitive routers. 2. Opt out of Google Location Services database by appending ‘_nomap’ to your router SSIDs. 3. Reduce router broadcast strength to not broadcast outside the premises, and 4. Change your router MACs frequently if needed. Finally, only use location along with at least two other authentication components (2FA + location) for sensitive operations.
Part 2 will cover Wi-Fi spoofing using a faraday cage in a dense Wi-Fi location.