darkosancanin / apple_bssid_locator

Simple python tool which finds the location of a wifi access point from its BSSID (Basic Service Set Identifier) which is the MAC address of the wifi access point and optionally displays the location on Google Maps. This is implemented by calling the Apple Location Services API.
42 stars 7 forks source link

Extract Accuracy/Radius #3

Closed mrx23dot closed 3 months ago

mrx23dot commented 5 months ago

Does anyone know which field encodes Radius?

    _descriptor.FieldDescriptor(
      name='latitude', full_name='WifiDevice.Location.latitude', index=0,
      number=1, type=3, cpp_type=2, label=1,
      has_default_value=False, default_value=0,
      message_type=None, enum_type=None, containing_type=None,
      is_extension=False, extension_scope=None,
      options=None),
    _descriptor.FieldDescriptor(
      name='longitude', full_name='WifiDevice.Location.longitude', index=1,
      number=2, type=3, cpp_type=2, label=1,
      has_default_value=False, default_value=0,
      message_type=None, enum_type=None, containing_type=None,
      is_extension=False, extension_scope=None,
      options=None),
    _descriptor.FieldDescriptor(
      name='unknown_value3', full_name='WifiDevice.Location.unknown_value3', index=2,
      number=3, type=3, cpp_type=2, label=1,
      has_default_value=False, default_value=0,
      message_type=None, enum_type=None, containing_type=None,
      is_extension=False, extension_scope=None,
      options=None),
    _descriptor.FieldDescriptor(
      name='unknown_value4', full_name='WifiDevice.Location.unknown_value4', index=3,
      number=4, type=3, cpp_type=2, label=1,
      has_default_value=False, default_value=0,
      message_type=None, enum_type=None, containing_type=None,
      is_extension=False, extension_scope=None,
      options=None),
    _descriptor.FieldDescriptor(
      name='unknown_value5', full_name='WifiDevice.Location.unknown_value5', index=4,
      number=5, type=3, cpp_type=2, label=1,
      has_default_value=False, default_value=0,
      message_type=None, enum_type=None, containing_type=None,
      is_extension=False, extension_scope=None,
      options=None),
    _descriptor.FieldDescriptor(
      name='unknown_value6', full_name='WifiDevice.Location.unknown_value6', index=5,
      number=6, type=3, cpp_type=2, label=1,
      has_default_value=False, default_value=0,
      message_type=None, enum_type=None, containing_type=None,
      is_extension=False, extension_scope=None,
      options=None),
    _descriptor.FieldDescriptor(
      name='unknown_value7', full_name='WifiDevice.Location.unknown_value7', index=6,
      number=7, type=3, cpp_type=2, label=1,
      has_default_value=False, default_value=0,
      message_type=None, enum_type=None, containing_type=None,
      is_extension=False, extension_scope=None,
      options=None),
    _descriptor.FieldDescriptor(
      name='unknown_value8', full_name='WifiDevice.Location.unknown_value8', index=7,
      number=8, type=3, cpp_type=2, label=1,
      has_default_value=False, default_value=0,
      message_type=None, enum_type=None, containing_type=None,
      is_extension=False, extension_scope=None,
      options=None),
    _descriptor.FieldDescriptor(
      name='unknown_value9', full_name='WifiDevice.Location.unknown_value9', index=8,
      number=9, type=3, cpp_type=2, label=1,
      has_default_value=False, default_value=0,
      message_type=None, enum_type=None, containing_type=None,
      is_extension=False, extension_scope=None,
      options=None),
    _descriptor.FieldDescriptor(
      name='unknown_value10', full_name='WifiDevice.Location.unknown_value10', index=9,
      number=10, type=3, cpp_type=2, label=1,
      has_default_value=False, default_value=0,
      message_type=None, enum_type=None, containing_type=None,
      is_extension=False, extension_scope=None,
      options=None),
    _descriptor.FieldDescriptor(
      name='unknown_value11', full_name='WifiDevice.Location.unknown_value11', index=10,
      number=11, type=3, cpp_type=2, label=1,
      has_default_value=False, default_value=0,
      message_type=None, enum_type=None, containing_type=None,
      is_extension=False, extension_scope=None,
      options=None),
    _descriptor.FieldDescriptor(
      name='unknown_value12', full_name='WifiDevice.Location.unknown_value12', index=11,
      number=12, type=3, cpp_type=2, label=1,
      has_default_value=False, default_value=0,
      message_type=None, enum_type=None, containing_type=None,
      is_extension=False, extension_scope=None,
      options=None),
    _descriptor.FieldDescriptor(
      name='unknown_value21', full_name='WifiDevice.Location.unknown_value21', index=12,
      number=21, type=3, cpp_type=2, label=1,
      has_default_value=False, default_value=0,
      message_type=None, enum_type=None, containing_type=None,
      is_extension=False, extension_scope=None,
      options=None),
biemster commented 4 months ago

@mrx23dot did you figure this one out? I only recently found out about this when some paper got quite a lot of attention, but this would nicely combine with the FindMy stuff I would say.

mrx23dot commented 4 months ago

Hello again, no idea about this protocol, I hardcoded to be 20meters for now.

From FindMy you could reuse the map, but the tag would need to do wifi scanning and deliver the result to server by other means, so it's like substituting the GPS with nearby wifi but still needing data connection.

FindMy is better because it provides both position+delivery.

biemster commented 4 months ago

i was thinking about tags just broadcasting a wifi bssid instead / in addition to a ble advertisement..

would require a different radio but many mcu's nowadays do wifi as well (with esp32 the best known example)

mrx23dot commented 3 months ago

radius solved in https://github.com/hubert3/iSniff-GPS/issues/40

Yeah but you would have to connect to an open wifi with raw net access (no captive portal) to send up what bssid it sees, and those are rare these days, or you would need GSM/LoraWAN/Sigfox.

You are better off doing going for the Google BLE location network instead, I started a ticket about it in one of FindMy projects.

biemster commented 3 months ago

The tag would just pretend to be a router by sending out a bssid, then the script from this repo can be used to get it's location based on the mac address it uses for that. Am I missing something here?

acheong08 commented 3 months ago

The tag would just pretend to be a router by sending out a bssid, then the script from this repo can be used to get it's location based on the mac address it uses for that. Am I missing something here?

That's what I'm currently attempting. It seems that it isn't immediately reported. I've been MITMing my iPhone for a day or two now and haven't caught any requests with the GPS location of a hotspot I've set up (and requests for that BSSID isn't returning anything). It might take a while for it to gain confidence in the location which makes sense considering confidence shows up a ton in the CoreLocation/locationd binaries.

Edit: I turned off "Improve Maps" and "iPhone Analytics" in Privacy → Location Services → System Services. Turning them back on to test.

mrx23dot commented 3 months ago

I see you mean txing bssid, not scanning for it on tag. You can freely try it on wigle.net network it's lot more open. Other providers like google don't allow to lookup one single bssid for privacy reasons. Weirdly apple only allows one bssid to be looked up. (cannot triangulate)

Do let us know about the results.

acheong08 commented 3 months ago

Weirdly apple only allows one bssid to be looked up. (cannot triangulate)

What do you mean? You can send multiple BSSIDs at once & Apple returns the GPS locations for all of them. There is also an option in the protobuf which makes Apple return all BSSIDs and their locations within the "neighborhood group" (I get ~200 from my BSSID) which allows you to triangulate based on signal strength

mrx23dot commented 3 months ago

Is there an example how to query multiple bssids? In this repo I only found single query: results = query_bssid(args.bssid)

acheong08 commented 3 months ago

In Golang: https://github.com/acheong08/apple-corelocation-experiments/blob/main/lib/appledb.go

acheong08 commented 3 months ago

I've trying all sorts of things to register a BSSID onto their database to no avail. My iPhone with all analytics turned on isn't reporting my hotspot at all with MAC spoofed to a valid vendor.

Did find something interesting though: https://gspe85-ssl.ls.apple.com/wifi_request_tile returns a bunch of GPS locations and BSSIDs (encoded in int64). The request includes a tile ID which means you can literally look at what BSSIDs are in any given area (currently trying to figure out the logic behind tileIDs)

mrx23dot commented 3 months ago

Try to find your BSSID on wigle.net someone might have picked it up, although it's not that common as iphones are, you can also install the scanner app.

Apple might need many days of seeing BSSID at one location by multiple phones to register it. Also try to spoof a home router, maybe one that's already in the system and see if it start moving.

acheong08 commented 3 months ago

wigle.net

Oh I'm not trying to do anything practical. Reverse engineering is just fun for me. Also, wigle has terrible rate limits, sell your data for commercial purposes, and doesn't provide data to researchers. I have a slight distaste for them

Also try to spoof a home router, maybe one that's already in the system and see if it start moving.

Yup doing that now. Waiting for something to happen.

I'm also trying to brute force the API to see which fields might allow me to upload data. Currently most of them just returns 400s or default missing values.

mrx23dot commented 3 months ago

Speaking of which, https://www.theregister.com/2024/05/23/apple_wifi_positioning_system/ https://krebsonsecurity.com/2024/05/why-your-wi-fi-router-doubles-as-an-apple-airtag/ https://www.cs.umd.edu/~dml/papers/wifi-surveillance-sp24.pdf

The design of Apple's system allowed Rye and Levin to compile a database of 490 million BSSIDs around the world, which they could then use to track the movements of individuals and groups of people over time.

The authors also alerted GL-iNet, the maker of travel routers, and found it less receptive. "They acknowledged the concern and our proposed fix of randomizing BSSIDs, but told us they have no plans to deploy that defense," said Rye.

we found that by querying 10 million BSSIDs
daily only 0.06% moved more than one kilometer over the
course of a month.

so it should pick up single BSSID eventually. It might be mandatory to set string SSID too. Wigle is no good for moving target, a scanner app rarely comes by.

acheong08 commented 3 months ago

The design of Apple's system allowed Rye and Levin to compile a database of 490 million BSSIDs around the world, which they could then use to track the movements of individuals and groups of people over time.

I actually found an API that allows me to request BSSIDs for any arbitrary GPS location. Just finished figuring out the decoding process for x-tilekey in GEOTileKeyMake. I've documented everything here including an interactive demo which allows you to click anything on a map and it'll return the closest cluster of BSSIDs.

Will try to reproduce their experiment more efficiently and write something up. Will get back to reverse engineering the process for how BSSIDs are picked up next.