Simovits

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:

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:

Nackdelar:


Mylnikov [3]
Fördelar:

Nackdelar:

Google [5]:

Fördelar:

Nackdelar:

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)