## Intro
YouTube recommended [this video](https://www.youtube.com/watch?v=fZtWRzcmh0Y) which introduced me to the possibility of using the browser's geolocating ability to track people - which is pretty sick.
With HTML5, browsers have the ability to determine your location very accurately. I'm interested in finding a way of getting this information from a target - without the user knowing.
Usually, for the browser to calculate the user's location, the user must first grant it access:
![[Pasted image 20220630133155.png]]
In the above example I created this HTML page:
```html
<!DOCTYPE html>
<html>
<body onload="getLocation()">
<p id="demo"></p>
<script>
var x = document.getElementById("demo");
function getLocation() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(showPosition);
} else {
x.innerHTML = "Geolocation is not supported by this browser.";
}
}
function showPosition(position) {
var lat = position.coords.latitude;
var long = position.coords.longitude;
var googleMaps = "https://maps.google.com/maps?q=" + lat + "," + long;
x.innerHTML =
"Latitude: " + lat +
"<br>Longitude: " + long +
"<br><a href=\"" + googleMaps + "\">Google Maps</a>";
}
</script>
</body>
</html>
```
With code taken from [here](https://www.w3schools.com/html/tryit.asp?filename=tryhtml5_geolocation) and slightly modified.
Granting access results in this:
***
Latitude: 32.1716224
Longitude: 34.8749824
[Google Maps](https://maps.google.com/maps?q=32.1716224,34.8749824)
***
Which is eerily accurate.
So, like I mentioned above, I want to find a way of getting this information without notifying the user - for example through a reverse shell.
There's always a possibility of [[PowerShell - Keystrokes Automation|automating keystrokes]] to open the browser, browse to a site that calculates your Geolocation ([like this one](https://www.w3schools.com/html/tryit.asp?filename=tryhtml5_geolocation), or [this one](http://html5demos.com/geo)) and saving the output somewhere.
Unless I were to do this when the user isn't looking, it would obviously raise suspicion.
I tried headless mode but it didn't work, probably because there was no user to click on the "Allow" button. But maybe there's a way around that...
Anyway, I don't necessarily need to use a browser to get the data.
At the end of the day, if the browser can do it, and the browser is just software interacting with an OS running on a computer, then having access to the OS should theoretically give me access to the same data. It's just a matter of understanding how the browser ends up calculating the coordinates.
Let's start with understanding how the browser does this calculation.
From [this post](https://stackoverflow.com/questions/4213410/how-does-html5-geolocation-work) and [this page](https://geotargetly.com/html5-geolocation-api), I understand that the browser will use different data sources to calculate the location:
"HTML5 geolocation detects latitude and longitude coordinates by using the device's GPS (if available on the device) or the device's mobile/WIFI signal (if GPS is not available. The mobile/WIFI signals are triangulated to work out the latitude and longitude."
## Browser Investigation
Let's dig into this a little deeper.
I set up Burp and proxied the computer's HTTP traffic through it. Then I checked the requests being made after granting access to my HTML page in each browser.
### Firefox
According to [this page](https://support.mozilla.org/en-US/kb/does-firefox-share-my-location-websites#:~:text=Your%20privacy%20is%20extremely%20important,your%20location%20without%20your%20permission.), Firefox uses Google Location Services and sends it:
- your computer’s IP address,
- information about the nearby wireless access points, and
- a random client identifier, which is assigned by Google, that expires every 2 weeks.
Firefox made this request:
```http
POST /geolocation/v1/geolocate?key=REDACTED HTTP/2
Host: www.googleapis.com
...
{"wifiAccessPoints":[
{"macAddress":"REDACTED","signalStrength":-52},
{"macAddress":"REDACTED","signalStrength":-56},
{"macAddress":"REDACTED","signalStrength":-66},
{"macAddress":"REDACTED","signalStrength":-70},
{"macAddress":"REDACTED","signalStrength":-77},
{"macAddress":"REDACTED","signalStrength":-77},
...
{"macAddress":"REDACTED","signalStrength":-91},
{"macAddress":"REDACTED","signalStrength":-92}]
}
```
We can see that for each Access Point it is sending the BSSID (MAC of the router) and signal strength which seems to be a number from -1 to -100, where the closer it is to 0 the stronger the signal is.
This is the response:
```http
HTTP/2 200 OK
...
{
"location": {
"lat": 32.17XXXXX,
"lng": 34.87XXXXX
},
"accuracy": 20.032
}
```
Which was very accurate.
I was curious how Firefox scans for the above information. It's open source so I figured it must be somewhere in [the code](https://github.com/mozilla/gecko-dev).
#### Code Analysis
I searched initially for "signalStrength" (which is indicative enough of the info I want to find) which lead me to `gecko-dev/dom/system/NetworkGeolocationProvider.jsm` ([link](https://github.com/mozilla/gecko-dev/blob/d3c2f51d89c3ca008ff0cb5a057e77ccd973443e/dom/system/NetworkGeolocationProvider.jsm)).
I noticed this comment:
```cpp
* @param {Array} wifiData Optional set of publicly available wifi networks
* in the following structure:
* <code>
* [
* { macAddress: <mac1>, signalStrength: <signal1> },
* { macAddress: <mac2>, signalStrength: <signal2> }
* ]
* </code>
*/
```
And in the comments a couple lines before the above (on line 394) it says this:
```text
/**
* After wifi (and possible cell tower) data has been gathered, this method is
* invoked to perform the request to network geolocation provider.
```
So I assume somewhere above is the code I'm looking for.
On line 287 there's this code:
```cpp
get isWifiScanningEnabled() {
return Cc["@mozilla.org/wifi/monitor;1"] && this._wifiScanningEnabled;
},
```
So I searched the whole repo for "wifi monitor", which led to this directory -
`gecko-dev/netwerk/wifi/`.
These files stood out:
```text
win_wifiScanner.cpp
win_wifiScanner.h
win_wlanLibrary.cpp
win_wlanLibrary.h
```
`win_wifiScanner.cpp` uses these header files:
```cpp
#include "nsWifiAccessPoint.h"
#include "win_wifiScanner.h"
// Moz headers (alphabetical)
#include "win_wlanLibrary.h"
```
And `win_wlanLibrary.h` in turn uses the Windows [wlanapi.h](https://docs.microsoft.com/en-us/windows/win32/api/wlanapi/) functions:
```cpp
#include <windows.h> // HINSTANCE, HANDLE
#include <wlanapi.h> // Wlan* functions
```
Lines 21-27 show the API functions being used:
```cpp
decltype(::WlanEnumInterfaces)* GetWlanEnumInterfacesPtr() const;
decltype(::WlanGetNetworkBssList)* GetWlanGetNetworkBssListPtr() const;
decltype(::WlanFreeMemory)* GetWlanFreeMemoryPtr() const;
decltype(::WlanCloseHandle)* GetWlanCloseHandlePtr() const;
decltype(::WlanOpenHandle)* GetWlanOpenHandlePtr() const;
decltype(::WlanRegisterNotification)* GetWlanRegisterNotificationPtr() const;
decltype(::WlanScan)* GetWlanScanPtr() const;
```
The functions that stand out to me are:
- [WlanScan](https://docs.microsoft.com/en-us/windows/win32/api/wlanapi/nf-wlanapi-wlanscan)
- [WlanEnumInterfaces](https://docs.microsoft.com/en-us/windows/win32/api/wlanapi/nf-wlanapi-wlanenuminterfaces)
- [WlanGetNetworkBssList](https://docs.microsoft.com/en-us/windows/win32/api/wlanapi/nf-wlanapi-wlangetnetworkbsslist)
The rest seem to be helper function to retrieve information for the above three.
If we want to create our own binary, these would be the functions we'd call.
#### Firefox Google Location Services API Key
Looking at the requests shows the following API key being used to query Google Location Services:
```bash
# On the Windows host
AIzaSyB2h2OuRcUgy5N-REDACTED
# On the Kali VM
91e66841-a83b-487f-REDACTED
```
Searching for the Windows key in the Firefox installation folder (`C:\Program Files\Mozilla Firefox\`) showed this:
```powershell
gci -Recurse | Select-String -Pattern 'AIzaSyB2h2OuRcUgy5N-REDACTED'
omni.ja:206965: MOZ_GOOGLE_LOCATION_SERVICE_API_KEY: "AIzaSyB2h2OuRcUgy5N-REDACTED",
```
And on Kali (`/usr/lib/firefox-esr`):
```bash
grep -air '91e66841-a83b-487f-REDACTED' ./*
./omni.ja: MOZ_MOZILLA_API_KEY: "91e66841-a83b-487f-REDACTED",
```
For some reason the request on Windows doesn't work with the `MOZ_MOZILLA_API_KEY` and the requests on the Kali VM don't work with the `MOZ_GOOGLE_LOCATION_SERVICE_API_KEY`.
I can just read the `omni.ja` file on the target and get both API keys and see which one works.
### Brave
After granting access Brave made this request:
```http
POST /v1/geolocate?key=REDACTED HTTP/2
Host: location.brave.com
...
{"wifiAccessPoints":[
{"age":0,"macAddress":"REDACTED","signalStrength":-51},
{"age":0,"macAddress":"REDACTED","signalStrength":-57},
{"age":0,"macAddress":"REDACTED","signalStrength":-68},
{"age":0,"macAddress":"REDACTED","signalStrength":-74},
{"age":0,"macAddress":"REDACTED","signalStrength":-74},
{"age":0,"macAddress":"REDACTED","signalStrength":-75},
{"age":0,"macAddress":"REDACTED","signalStrength":-77},
...
{"age":0,"macAddress":"REDACTED","signalStrength":-92},
{"age":0,"macAddress":"REDACTED","signalStrength":-92}]
}
```
And got this response:
```http
HTTP/2 200 OK
...
{"location": {"lat": 32.0717, "lng": 34.8153}, "accuracy": 1}
```
Plotting it on Google Maps showed the location to be in Giv'atayim, a completely different city - so this isn't accurate at all.
I saw some posts where others have complained about accuracy issues.
### Chrome
Sends this request:
```http
POST /geolocation/v1/geolocate?key=REDACTED HTTP/2
Host: www.googleapis.com
...
{"wifiAccessPoints":[
{"age":0,"macAddress":"REDACTED","signalStrength":-54}]
}
```
```http
HTTP/2 200 OK
...
{
"location": {
"lat": 32.17XXXXX,
"lng": 34.87XXXXX
},
"accuracy": 1609.0674222282175
}
```
Plotting it on Google Maps showed a point close to the roundabout in Givat Hen (the neighbourhood close to me).
I tried it a couple minutes later and the request had many more access points:
```http
POST /geolocation/v1/geolocate?key=REDACTED HTTP/2
Host: www.googleapis.com
...
{"wifiAccessPoints":[
{"age":0,"macAddress":"REDACTED","signalStrength":-50},
{"age":0,"macAddress":"REDCATED","signalStrength":-51},
{"age":0,"macAddress":"REDCATED","signalStrength":-54},
{"age":0,"macAddress":"REDCATED","signalStrength":-54},
{"age":0,"macAddress":"REDCATED","signalStrength":-59},
...
{"age":0,"macAddress":"REDCATED","signalStrength":-92},
{"age":0,"macAddress":"REDCATED","signalStrength":-92}]
}
```
And the response showed a much better accuracy rating:
```http
HTTP/2 200 OK
...
{
"location": {
"lat": 32.17XXXXX,
"lng": 34.87XXXXX
},
"accuracy": 13.436
}
```
This time the location was much better - only a couple meters from where I was.
## Replicating Browser Behaviour
Ok, so it looks like all the browsers are using the underlying OS to scan for surrounding access points and their signal strength, then are sending these data to Google Location Services with the relevant API key.
The data being sent is in JSON, and prettified, looks like this:
```json
{
"wifiAccessPoints": [
{
"macAddress": "MAC-ADDRESS-1",
"signalStrength": -71
},
{
"macAddress": "MAC-ADDRESS-2",
"signalStrength": -84
},
{
"macAddress": "MAC-ADDRESS-3",
"signalStrength": -90
}
]
}
```
From playing around with which APs (access points) were sent, it seems that the more APs get sent, and the closer our proximity to them, the better the accuracy of the response.
Makes sense.
What's cool is that I can get this exact same data through a reverse shell, then make my own request to the Google Geolocation Services API and get back the location of the target.
To scan for access points I'll use the [[NETSH#Enumerating Wi-Fi Access Points]] utility:
```text
$wshell = New-Object -ComObject wscript.shell; explorer.exe ms-availablenetworks:; Start-Sleep -Seconds 3; $wshell.SendKeys('{esc}')
netsh wlan show networks mode=bssid
...
SSID 1 : Ground Zero
Network type : Infrastructure
Authentication : WPA2-Personal
Encryption : CCMP
BSSID 1 : REDCATED
Signal : 86%
Radio type : 802.11n
Channel : 1
Basic rates (Mbps) : 1 2 5.5 11
Other rates (Mbps) : 6 9 12 18 24 36 48 54
BSSID 2 : REDCATED
Signal : 85%
Radio type : 802.11ac
Channel : 40
Basic rates (Mbps) : 6 12 24
Other rates (Mbps) : 9 18 36 48 54
...
```
(I added commands to first open the Network tab to start a new Wi-fi scan. For more options see [[NETSH#Enumerating Wi-Fi Access Points]]).
I came across the [[NETSH#^019d58|WifiInfoView]] tool which had a field called RSSI which looked very similar to what the browsers were sending in the POST request as opposed to the "Signal" parameter from the NETSH output.
"RSSI, or “Received Signal Strength Indicator,” is a measurement of how well your device can hear a signal from an access point or router. It’s a value that is useful for determining if you have enough signal to get a good wireless connection." ([Source](https://www.metageek.com/training/resources/understanding-rssi/))
To convert between the `netsh` signal strength value to the RSSI value, I found [this](https://www.thewindowsclub.com/signal-strength-wi-fi-connection-windows) formula:
```text
dBm (RSSI) = (quality / 2) – 100
```
I wrote a [Python script](https://github.com/MaroGol/pentestingScripts/blob/main/collection/getLocation.py) which:
- receives as input the NETSH output
- extracts the BSSID and Signal strength values
- converts the Signal value to an RSSI value
- sends the data to the Google Geolocation Services API
- prints the response together with a Google Maps link to the location.
Pretty much what the browser does.
```text
python3 getLocation.py netshOutput.txt
[+] Parsing NETSH output file
[+] Sending request to Google Location Services API
[+] Target acquired!
Latitude: 32.17XXXXX, Longitude: 34.87XXXXX
Accuracy: 16.432 (below 20 is good)
Google Maps URL: https://maps.google.com/maps?q=32.17XXXXX,34.87XXXXX
```
It's kind of creepy to think that just with a connection to a victim's machine, an attacker could potentially find the physical location of said victim.
What's interesting in this case is that, so long as the user is using Wi-Fi to connect to the internet, then using a VPN / proxy won't help because we're not dealing with IPs but rather with access points that are tied to physical locations around the world.
Some things can stop this - at least in this current version:
- If the target is in a remote location with a small number of BSSIDs, then the Google Geolocation Services API will probably not be able to pinpoint an accurate location.
- If the target uses Ethernet instead of Wi-Fi, then the Access Point scan won't necesserily work. A way around this would be to manually enable the `wlansvc` service (which requires admin rights) and then conduct the scan. That, or find another way to turn on the Wi-Fi on the machine (sending keystrokes would probably work for example).
- I wonder what the browsers do if the user is connected via Ethernet. I'll check it out tomorrow.
[[2022-07-01_Fri]] - Thought of another cool feature. If the target machine is a mobile device (laptop/tablet) then you can create a wrapper program to continuously call a new scan (and then print the NETSH results to a file) every couple minutes.
The next time you get a connection to the machine, exfiltrate the files (or add exfiltration capabilities whenever there is an internet connection) and run the above Python script on each file to track the user's whereabouts throughout the day.
## Windows Location Services
[[2022-07-08_Fri]] - I'm interested in understanding how Windows determines my location once I enable *Settings* > *Privacy* > *Location* >
1. *Allow access to location on this device*
2. *Allow apps to access your location*
3. *Allow desktop apps to access your location*
1 and 3 are required to access the location using PowerShell with this code (doesn't require admin to run, but turning on/off does require admin as we'll see later):
```powershell
# https://stackoverflow.com/questions/66786341/powershell-script-for-reverse-geo-coding-using-windows-location-services
Add-Type -AssemblyName System.Device #Required to access System.Device.Location namespace
# $watcher = New-object System.Device.Location.GeoCoordinateWatcher # Create the required object
$accuracy = (New-Object System.Device.Location.GeoPositionAccuracy).value__ = 1 # 1 is high, 0 is default. https://docs.microsoft.com/en-us/dotnet/api/system.device.location.geopositionaccuracy?view=netframework-4.8
$watcher = New-Object System.Device.Location.GeoCoordinateWatcher($accuracy) # Create the required object with high accuracy, https://docs.microsoft.com/en-us/dotnet/api/system.device.location.geocoordinatewatcher?view=netframework-4.8
$watcher.Start()
#$watcher.Status #NoData
#$watcher.Permission #Granted
while (($watcher.Status -ne 'Ready') -and ($watcher.Permission -ne 'Denied')) {
#Write-Host 'Entering while loop'
Start-Sleep -Milliseconds 100 #Wait for discovery.
}
if ($watcher.Permission -eq 'Denied'){
Write-Host 'Access Denied for Location Information'
} else {
$location = $watcher.Position.Location
$googleMaps = "https://maps.google.com/maps?q=$($location.Latitude),$($location.Longitude)";
Write-Host $googleMaps
}
```
The location is accurate:
```bash
.\geo1.ps1
https://maps.google.com/maps?q=32.17XXXXX,34.87XXXXX
```
But interestingly I don't see the traffic generated by this. It even works when the WIFI is on, but not connected to anything.
### Covertly enabling device location
[[Windows - Covertly Controlling Settings#Privacy#Location]]
## Geo Trackers
### PowerShell Script
[[2022-07-06_Wed]] - Today I visited the Technion and took the laptop with me. I had two PowerShell scripts ([GitHub](https://github.com/MaroGol/pentestingScripts/tree/main/collection)) running every 15 minutes (had to change the power settings so the laptop wouldn't go to sleep) and outputting the data to a file. The first (netshTracker.ps1) used the NETSH command to scan for surrounding BSSIDs and the second (wifiInfoViewTracker.ps1) used the NirSoft WifiInfoView tool.
Running the `getLocation.py` script on all the NETSH outputted files:
```text
for file in $(ls -1); do (echo $file; python3 /opt/pentestingScripts/collection/getLocation.py $file; echo '--------'; sleep 5); done
10-34-09.txt // Meeting at Taub CS building
Accuracy: 14.446 (below 20 is good)
Google Maps URL: https://maps.google.com/maps?q=32.7774993,35.0214777
--------
12-02-56.txt // Undergraduates admissions office
Accuracy: 13.068 (below 20 is good)
Google Maps URL: https://maps.google.com/maps?q=32.7766044,35.0227654
--------
12-18-02.txt // Lunch at cafe
Accuracy: 12.112 (below 20 is good)
Google Maps URL: https://maps.google.com/maps?q=32.7792497,35.0220904
--------
12-33-07.txt // Lunch at cafe
Accuracy: 19.898 (below 20 is good)
Google Maps URL: https://maps.google.com/maps?q=32.7791578,35.0223707
--------
13-03-19.txt // On the move
Accuracy: 18.214 (below 20 is good)
Google Maps URL: https://maps.google.com/maps?q=32.7787027,35.0187951
--------
13-18-25.txt // Gilboa 35 visit
Accuracy: 20.681 (below 20 is good)
Google Maps URL: https://maps.google.com/maps?q=32.7830452,35.016192
--------
13-48-36.txt // On the move
Accuracy: 37.476 (below 20 is good)
Google Maps URL: https://maps.google.com/maps?q=32.7790978,35.0154365
--------
15-19-11.txt // Adam Hacohen 25 visit
Accuracy: 11.655 (below 20 is good)
Google Maps URL: https://maps.google.com/maps?q=32.7792964,35.0158773
--------
16-19-34.txt // Meal at cafe
Accuracy: 17.407 (below 20 is good)
Google Maps URL: https://maps.google.com/maps?q=32.7792488,35.022091
--------
16-34-40.txt // Cable car
Accuracy: 49.75 (below 20 is good)
Google Maps URL: https://maps.google.com/maps?q=32.7740311,35.0266695
--------
16-49-46.txt // Hamifratz Train Center
Accuracy: 15.027 (below 20 is good)
Google Maps URL: https://maps.google.com/maps?q=32.793177,35.0352957
```
and manually plotting the points on a map, shows for the most part the locations I visited today with very high accuracy (to the building).
![[Pasted image 20241003125549.png]]
[[2022-07-09_Sat]] - A future modification would be to parse all the NETSH files, create a CSV with the time, longitude and latitude and them use something like [this](https://www.earthdatascience.org/courses/scientists-guide-to-plotting-data-in-python/plot-spatial-data/customize-raster-plots/interactive-maps/) or [this](https://www.google.com/earth/outreach/learn/visualize-your-data-on-a-custom-map-using-google-my-maps/) to for efficiently plot all the points on a map.
[[2022-07-12_Tue]] - Done.
The `getLocation.py` script accepts a directory path containing NETSH output files and a CSV output file path. Create a new map [here](https://www.google.com/mymaps) and upload the CSV to it.
I tested it by going cycling around the city with the laptop in my bag running the script.
![[Pasted image 20241003130021.png]]
[[2022-07-17_Sun]] - Running the netshTracker.ps1 script while using ProtonVPN seemed to mess with the adapter as it wouldn't show the Wi-Fi icon. I had to restart the computer for it to work again, but oddly it still collected the data. The lesson is to make sure no VPNs are connected while running the script.
### Wi-Fi Probe Requests to Hidden Network
[[2022-10-14_Fri]]
[YouTube, Hak5 - This is How Hackers Would Track You](https://youtu.be/H0Nwff0KDJ0)
By having the target connect/try to connect to a hidden Wi-Fi network, the phone continues looking for it (as it doesn't know whether it's there or not), and with a Wi-Fi scanner you can see these requests.
So to track someone, create a unique hidden network:
```bash
pip install wifi-qrcode-generator
export PATH=/home/kali/.local/bin:$PATH
python
import wifi_qrcode_generator as qr
a = qr.wifi_qrcode('TrackerTest', True, 'WPA', 'password')
a.show()
```
We get this image:
![[Pasted image 20221019142449.png]]
Once the device scans this QR code and the person connects, the phone will continuously send requests for that SSID.
```text
Wireshark query:
wlan.fc.type_subtype eq 4 && wlan.ssid != "" and frame contains "TrackerTest"
```
![[Pasted image 20221019143010.png]]
Additionally, in Wireshark under "802.11 radio information" there's a "Signal strength" field and that can be used to hone in on a more exact location of the target.
We can also use [[tool - tshark]]:
```bash
sudo tshark -i wlan0mon -Y 'wlan.fc.type_subtype eq 4 && wlan.ssid != "" and frame contains "TrackerTest"'
Running as user "root" and group "root". This could be dangerous.
Capturing on 'wlan0mon'
84 5.915054194 REDCATED → Broadcast 802.11 201 -58 dBm Probe Request, SN=3249, FN=0, Flags=........C, SSID=TrackerTest
512 26.646097214 REDCATED → Broadcast 802.11 201 -40 dBm Probe Request, SN=3321, FN=0, Flags=........C, SSID=TrackerTest
514 26.656205425 REDCATED → Broadcast 802.11 201 -41 dBm Probe Request, SN=3322, FN=0, Flags=........C, SSID=TrackerTest
516 26.785451116 REDCATED → Broadcast 802.11 201 -48 dBm Probe Request, SN=3327, FN=0, Flags=........C, SSID=TrackerTest
517 26.796787763 REDCATED → Broadcast 802.11 201 -46 dBm Probe Request, SN=3328, FN=0, Flags=........C, SSID=TrackerTest
```
### Apple MDNS Packets
[[2022-10-26_Wed]] - Apple devices seem to broadcast themselves over MDNS.
By searching these packets for the device name, we can tell the person is on the same network as us.
```bash
lower(dns.qry.name) contains "maor" || lower(dns.resp.name) contains "maor"
```
### Location Beacon
[[2022-11-16_Wed]]
Purpose:
Runs in the background and acts as a beacon by estimating and sending the laptop's location every X minutes.
Pseudo-code:
Every X minutes it:
- Scans for surrounding SSIDs
- Calculates the location using Google's DB
- If there is an existing network connection:
- If there is data in the file:
- Send data
- Clear file
- Send current location
- If there is no existing network connection:
- It scans for public networks
- If there is a public network:
- Connects to a public network
- If there is data in the file:
- Send data
- Clear file
- Sends current location
- Disconnects from the public network
- Else:
- Appends location to file which will be sent at next available internet connection
(or leverage Windows' location feature which doesn't require internet access)
The beacon location needs to be encrypted and accessible only by me.
***
## Footnotes
Resources
- [HTML5 Geolocation API](https://geotargetly.com/html5-geolocation-api)
- [W3Schools geolocation code](https://www.w3schools.com/html/html5_geolocation.asp)
- [MSDN - Windows.Devices.Geolocation Namespace](https://docs.microsoft.com/en-us/uwp/api/Windows.Devices.Geolocation?view=winrt-22621)