Identifiering av geografisk position av av trådlösa accesspunkter
Denna blogg handlar om att lokalisera den faktiska geografiska positionen av trådlösa accesspunkter utifrån accesspunktens BSSID (MAC adress).
Bakgrund
Det vanligaste tillvägagångssättet för att lokalisera en accesspunkt är att använda någon av de fritt tillgängliga databaserna på internet som kopplar BSSID från trådlösa accesspunkter till den geografiska positionen av accesspunkten. Dessa databaser har skapats av att olika entusiaster har rört sig runt WiFi-utrustning och loggat sin position då de upptäcker ett nytt BSSID. Positionerna är alltså ungefärliga eftersom det är mottagarens position som loggas, inte accesspunktens faktiska position.
Dessa databaser kan exempelvis användas för att bestämma var ägaren av en dator eller telefon bor och arbetar utifrån data som kan hittas på enheten. Naturligtvis kan annan data finnas som ger samma information. För ett antal år sedan så gick det att fråga Google direkt var en accesspunkt var lokaliserad, på den tiden fick Google denna information från att de samlade in data om BSSID och positioner med samma bil som samlade bilder till Google maps street view. Se exempelvis detta föredrag från Blackhat 2010 [1]. På grund av negativ publicitet har denna funktionalitet tagits bort.
I praktiken så är Google bättre än någonsin på att veta var trådlösa accesspunkter är lokaliserade, eftersom alla Androidanvändare och personer som använder någon av Googles tjänster på sina mobiltelefoner konstant skickar information till dem om vilka trådlösa accesspunkter som finns i närheten, deras signalstyrka, osv. Men Google tillåter alltså inte längre allmänheten att fråga vart en viss accesspunkt är lokaliserad, men APP utvecklare har fortfarande tillgång till ett API som använder Googles kunskap för att triangulera var en användare är. Bilden nedan visar exempelvis informationen som skickas när Google maps ska centrerars vid en användares position.
Missbruk av Googles API för att lokalisera accesspunkter
Googles API svarar på en POST request där man skickar med olika data som är till för att ge en kombinerad bild om vart användaren är lokaliserad. Vi vill missbruka funktionaliteten för att hämta ut information om var en accesspunkt är lokaliserad, istället för att triangulera oss som användare.
Detta kan åstadkommas genom att man skickar minimalt med information. I princip så ställer man frågan vart man är om det BSSID man vill lokalisera har en mycket stark signalstyrka, samt att några andra accesspunkter finns i närheten men har mycket svag signalstyrka.
Låt A beteckna accesspunkten vi söker, låt Bi beteckna någon annan accesspunkt, tester visar att:
- Om endast A och B1 skickas med i POST requesten så returnerar Google ett felmeddelande om den sanna positionen av B1 är väldigt långt borta.
- Om A, B1 och B2 skickas med POST requesten och A är väldigt långt borta från B1 och B2 så returnerar Google ett felmeddelande i normala fall. Om B1 och B2 är mycket nära varandra så returnerar Google en position nära B1 och B2.
- Om A är lokaliserad i alla fall i samma stad som minst en av de Bi som skickas med i POST requesten så ger Google den typ av svar vi söker, dvs svarar med koordinater för A. Det finns en tendens till att svaret är bättre ifall flera stycken Bi som är i samma stad skickas med.
Det är naturligtvis tråkigt att detta inte fungerar felfritt, men det är att vänta sig eftersom det är frågan om ett missbruk av ett API som medvetet designats för att inte ge ut denna information. Men i scenariot då man vet vilken stad en accesspunkt befinner sig i så fungerar det ganska bra och det är ganska enkelt att identifiera false positives, eftersom dessa alltid är i form av koordinater for accesspunkterna Bi enligt ovan. Om de öppet tillgängliga databaserna inte innehåller ett visst BSSID så ger denna metod en ytterligare chans att lokalisera accesspunkten.
Jämförelse
Nedan är en jämförelse mellan de tre alternativen för att lokalisera accesspunkter som jag själv använder:
Wigle [2]
Fördelar:
- Stor öppen databas
- Ger positionsdata till accesspunkter direkt.
- Komplement till Mylnikov.
Nackdelar:
- Mindre databas än Google,
- Begränsat antal frågor per dag,
- Ägaren av tjänsten kan koppla ditt konto och IP till de BSSID du söker.
Mylnikov [3]
Fördelar:
- Stor öppen databas (annat innehåll än Wigle),
- Tillåter obegränsat antal frågor per dag,
- Tillgänglig för nedladdning [4] (på egen risk), bra eftersom ingen tredje part vilka BSSID som eftersöks.
Nackdelar:
- Mindre databas än Google.
- Oklart om databasen kommer att underhållas framöver.
- Vid online användning kan ägaren av tjänsten kan koppla ditt konto och IP till de BSSID du söker.
Google [5]:
Fördelar:
- Störst databas,
- Data uppdateras oftare än alternativen,
- Kan fråga obegränsat antal gånger,
- Väldigt lågt pris och helt gratis under ett års tid när man registrerar sig som utvecklare [6].
Nackdelar:
- Ger inte positionsdata direkt, måste använda kompletterande information för att lura ut rätt information med rätt ställda frågor.
- Ägare av tjänsten kan koppla din identitet till de BSSID du söker.
Exempel
Om du har kommit över en backup av en Iphone och är intresserad av en
accesspunkt som telefonens ägare varit ansluten till, då är
Googles geolocation API lämpligt eftersom annan information på
telefonen troligtvis kan säga vilken stad personen rört sig i. Det
vi ska använda i detta exempel är att det är troligt att personen
anslutit sig till någon annan accesspunkt i samma stad. Informationen
om vilka accesspunkter som telefonen har anslutit till finns sparade
i filen:
/private/var/preferences/SystemConfiguration/com.apple.wifi.plist.
Ifall backupen är en Itunes kopia så är filnamnet för varje fil SHA1(”Domain-[subdomain-]fullpath/filename.ext”), vilket i detta fall blir innebär: ed5e2f2b498a21c090466493c74856e036fce88c.
För att konvertera till xml:
sudo apt-get install libplist-utils
plistutil -i ade0340f576ee14793c607073bd7e8e409af07a8 -o wifidata.xml
Scriptet nedan läser in xml filen och frågar användaren om vilken BSSID som ska lokaliseras. Programmet jämför resultat från de tre alternativen Wigle, Mylnikov och Google. Google frågas genom att det BSSID som användaren valt antar rollen A och varje annat BSSID antar rollen B. A påstås ha en stark signal och B påstås ha en svag signal.
Referenser
[1]– https://youtu.be/O5xRRF5GfQs
[2]-https://wigle.net
[3]- https://find-wifi.mylnikov.org/
[4]- https://www.mylnikov.org/download (Egen risk)
[5]- https://developers.google.com/maps/documentation/geolocation/intro
[6]- https://cloud.google.com/free/docs/gcp-free-tier
import requests, argparse, xml.etree.cElementTree as ET
#User input
parser = argparse.ArgumentParser()
parser.add_argument('-f', help='XML file to read')
datafile = vars(parser.parse_args())['f']
#MACs from phone xml file
tree = ET.parse(datafile)
root = tree.getroot()
MacDict={}
i=0
for item in root.iter():
if i == 1 and item.text not in MacDict.values():
MacDict[len(MacDict)+1] = item.text
i=0
if item.text == "BSSID":
i=1
else:
i=0
print("Pick AP to locate; \n", MacDict)
choice = int(input())
MacPick = MacDict[choice]
MacDict.pop(choice)
def GooglePost(Key,Mac,NotMac):
URL="https://www.googleapis.com/geolocation/v1/geolocate?key="+Key[0]
head= {'Content-Type': 'application/json'}
body={
"considerIp": "false",
"wifiAccessPoints": [
{
"macAddress": Mac,
"signalStrength": -1,
"signalToNoiseRatio": 0
},
{
"macAddress": NotMac,
"signalStrength": -85,
"signalToNoiseRatio": 0
}
]
}
r = requests.post(url = URL, headers=head, json = body)
return r
#Read Google key from file
with open ("Key", "r") as myfile:
Key=myfile.read().splitlines()
j=0
for value in MacDict.values():
r=GooglePost(Key,MacPick,value)
if r.status_code != 404:
print("Google: \n " + r.text)
j=1
if j==0:
print("Google: Nothing found.")
def wigle(KeyW,Mac):
Mac.replace(":","%3")
head= {'Authorization': "Basic " + KeyW[0]}
URL="https://api.wigle.net/api/v2/network/detail?netid="+Mac
r = requests.get(url = URL, headers=head)
return r
#Read Wigle key from file
with open ("KeyW", "r") as myfile:
KeyW=myfile.read().splitlines()
r=wigle(KeyW, MacPick)
print("Wigle: \n " + r.text)
def mylnikov(Mac):
URL="https://api.mylnikov.org/wifi?v=1.1&bssid="+Mac
r = requests.post(url = URL)
return r
r=mylnikov(MacPick)
print("Malnykov: \n " + r.text)