Apple changed their documentation at my request but it proves they don't care about privacy

Sep 03, 2026 Last reply: 2 weeks ago 113 Replies

I understand, but that could be a problem to people that are already reading on a "different" group.

Your comments are easier to understand that Arlen (aka Maria) posts. He is not clearly explaining the issues and wants people to read a lot of documentation, instead of just posting an actual summary of the situation with explanations.

Ie, what was Apple doing, why is that bad, what have they changed, is that enough and why, what are the actual dangers to people.

Complete, and short text.

Most people here get a free (rented, not purchased) router with AP from their ISP. When they move house, the old router has to be returned to the ISP (nominally), and they get a new router automatically when contracting a connection at the new location. So they don't have a problem.

Some of those might have a secondary AP of their own.

Only people that buy a router or AP have a problem. And most do not care. I don't. Only those that are using _nomap or perhaps those that hide the SSID.

...

Cheers, Carlos. ESπŸ‡ͺπŸ‡Έ, EUπŸ‡ͺπŸ‡Ί;

I'm sorry, but no can do: Usenet/NetNews doesn't allow an empty 'Newsgroups:' header.

Ok, well to summarise what I have gathered then, which may or may not be Maria's opinion but reflects my opinion at this point: there are two completely separate issues here, which are unrelated except that they are both to do with WiFi location databases.

  1. Apple are storing the location of "hidden" WiFi Access Points. (My opinion: low priority.)

Multiple organisations are gathering and storing the physical co-ordinates of the MAC addresses ("BSSIDs") of WiFi Access Points around the world, whenever they are seen transmitting by, e.g. mobile phones. (Any WiFi-enabled device can see this information; it does not need to be connected to the network in question, nor does it need to know its password.)

The purpose of these databases of BSSID locations is to assist devices such as mobile phones in locating themselves. Maybe the device is indoors and cannot get a GPS signal, but also my understanding is that GPS can fix an accurate location much faster if it starts already knowing vaguely where on the planet it is.

There is broad agreement that these databases should not include BSSIDs which are broadcasting WiFi network names ("SSIDs") which end in the string "_nomap".

Allegedly: Apple are adhering to this exclusion, but are not also excluding BSSIDs which are not broadcasting any SSID at all (i.e. "hidden" networks). Other organisations (e.g. Google) do exclude such "hidden" networks.

My complete speculation: older Apple software, written before they added the "_nomap" exclusion, doesn't care at all about the SSID, so just reports from the phone to the central database the BSSID and location. The database thus has no way of excluding "hidden" networks, without excluding all reports from older devices. The "_nomap" exclusion is achieved by later software versions reporting the SSID if it is seen, and any BSSIDs associated with "_nomap" SSIDs then being blacklisted, maybe for 6 months or something like that. The likelihood of any particular BSSID *only* being seen by old Apple devices and *never* new ones is very low, and hence the "_nomap" exclusion more-or-less works.

My opinion: this failure to exclude "hidden" networks is unfortunate and should be fixed, and maybe Apple are indeed fixing it, but it's possible it may take some years to achieve due to the multitude of devices running old software.

  1. Apple's API is trivial to abuse. (My opinion: high priority.)

The Apple API to query their BSSID location database is remarkably unrestricted. It has little or no rate limiting, doesn't ask for an API key, and reports not only the location of the BSSID but also potentially a great many other BSSIDs in the locality. It is not beyond even an individual person's ability to get a list of most of the Access Points in the world and all their locations.

This makes it very easy for people to abuse this data in various ways, from stalkers tracking victims to state actors tracking military targets.

Google's API by contrast essentially reverses the process, and instead of the phone asking "Where is BSSID <x>?", it says "I can see BSSIDs <x>, <y>, and <z>, where am I?".

My opinion: the lack of restrictions on the Apple API is unacceptable, and if Apple are unwilling to do something about it then governments should pass laws (or enforce existing laws) to make them to do so.

My opinion: it would be difficult for Apple to rapidly switch completely to using an API more similar to Google's API, since this would remove functionality from devices running old software. However they could certainly aim to do this eventually, and there are steps they could take immediately to improve the situation (e.g. rate limiting, and not providing so many additional answers when asked about an individual BSSID). I cannot see any obvious reason why they couldn't make significant improvements almost immediately.

Nuno Silva snipped-for-privacy@invalid.invalid wrote: [...]

As others have mentioned, this all has been 'discussed' umpteen times Sofar, this thread is not bringing any new information, viewpoints, <whatever>.

A summary of the previous times:

At best, a home router's BSSID is tied to a *household*, *not* to a

*person*, which is 'Arlen''s 'privacy concern'.

'Arlen' has a wife. What is the BSSID's location 'tracking'? 'Arlen'? His wife? His dog?

Case in point: I'm sometimes 18000km away from the location of my router. Quite a bit of a (location) tracking error, don't you think?

My advice/request: Let it rest. The rest of us would appreciate not seeing the same non-arguments over and over again.

[...]

Thanks.

Only Apple?

Yes.

Yes. And they can give an approximate location without using the GPS chip.

Right.

And if it is hidden they would not see if the SSID ends in _nomap.

But is there a consensus that hidden SSIDs should not be listed? In writing? Maybe there is such a consensus now.

Right.

Related: What did Arlen (aka Maria) achieve? That Apple agreed to remove all hidden and _nomap entries, or that they removed only his entry?

Aha.

Well, only of the limited subset of people that carry their AP when they move.

Ah. Yes, I can see this is better.

Ok.

Ok, yes.

Thank you for the explanation. Easy to understand, and I agree with your conclusions.

Cheers, Carlos. ESπŸ‡ͺπŸ‡Έ, EUπŸ‡ͺπŸ‡Ί;

The new information, in not a clear form, is that Arlen got Apple to do "something". What, is not clear, but I guess they simple removed his router from the list, so that he stops complaining to his neighbour (the one that works for Apple or has high contacts at Apple).

Andy,

You forget that you're talking to Arlen here. He ignores everything that does not further his narrative, and than goes full-out on the crumbs that remain.

What you mention there has been explained to him in the thread here

[Tutorial: Query the Apple database with Python for your access point BSSID] <10h1qmr$2alo$ snipped-for-privacy@nnrp.usenet.blueworldhosting.com>

Regards, Rudy Wieser

After having read the prior responses in this Jon/Carlos tangent...

I apologize if I haven't explained the situation to the liking of people who haven't clicked on the references, but I am happy that Jon and Carlos (and Andy and Lawrence, all of whom I respect) are trying to understand.

I especially wish to thank Jon Ribbens and Carlos & any others who have bothered to read the links provided, because they explain the issue well.

If people haven't read the references, then they're stuck in the stone age of understanding, which, well, which I'm not the one to drag them out of.

I just don't have the skills to explain that an SSID is not a BSSID (as witnessed by those who claimed changing the SSID changes the BSSID).

These are basic networking concepts.

Regarding locations, I don't have the skills to explain what a set of GPS coordinates means, if people claim it's "just a map". I don't even know _how_ to _begin_ to explain that your home address is NOT "just a map".

I don't even know how to explain to people who claim that the router never travels as their belief system is so absurd as to warn me that if they don't understand that people take their routers with them when they move, then that kind of person will never be able to understand, well, anything.

I apologize that I don't have the skills set to bring folks out of the stone age, when they claim a home address is "just a location on a map".

People who make those claims will never be able to comprehend why it is so trivial for Jon & me to surveil their movements using the Apple WPS db.

In summary, if people haven't read this paper, I don't have the skill set to explain the very simple concepts which are described in that paper. *Surveilling the Masses with Wi-Fi-Based Positioning Systems*

formatting link

The paper is about surveillance of masses (not individuals, per se), knowing every single location of billions of router BSSIDs around the world, from anywhere in the world, by anyone in the world, with zero restrictions, which is only possible using Apple's WPS database (not Google's, which has huge restrictions on both the input and output).

Apple has zero restrictions on the input. And a restriction of the nearest 400 BSSIDs/GPS pairs on the output.

But... that restriction is meaningless, as I wrote and ran the Python code on Windows that took the furthest away of those 400 to run it again.

And again. And again. And again. I stopped at something like thousands. So now I have the nearest ten thousand BSSIDs starting at my home.

Notice I have the GPS location of each of those unique ten thousand BSSIDs.

And, since apartments basically do not exist in this suburban area, I actually know the names and address of the owners of record of those homes.

If any of those ten thousand people ever move, and take their router with them, I will instantly know exactly where they moved to, within seconds.

Not only that, but I will instantly know the nearest 400 BSSIds, so if they moved, oh, say, into someone else's home, I'll know exactly who that is.

To be very clear, this is only possible because of how Apple designed it. Google and Mozilla designed their systems completely differently.

Only Apple's design is this bad in terms of privacy.

As for the hidden-BSSID issue, I was unaware of that until I found, to my horror, my own hidden BSSIDs in Apple's WPS database. That was a shock!

At the time, I looked up Apple's public policy, and it was silent on the issue of what Apple does when it encounters a hidden-broadcast BSSID.

When I brought it up to my next-door neighbor, who runs Apple Maps, he said he'd check it out, which, after a few weeks of emails back and forth, we found out that Apple has no intention of honoring the intent of a hidden broadcast (even as we know Google & Mozilla certainly honor that intent).

The Apple lawyers made that very clear to me, as I was shut out from talking to the engineers once the lawyers were informed of the issue.

I had given up, but recently, I happened to look at Apple's documentation, and I realized Apple proved they had no intent of honoring the long-held intention of a hidden broadcast, by simply changing their documentation.

formatting link

Clearly, I know what I'm talking about, so my well informed assessment, based on those verifiable facts, is Apple doesn't care about our privacy.

On Usenet we can have intelligent discussions with well-meaning people.

Hi Keith Thompson,

Thank you for bringing up your point of view, as all points are valid here. This is Usenet where we welcome deep detailed discussion of tech topics.

Jon Ribbens clearly ran the code so we have proof that it was referenced properly enough for him to have run it on his machine to prove the point.

Nonetheless, it's good you're *asking* for the Python code, which I only ran on Windows 10, and which I'm happy to supply to you for all to review.

Please keep in mind the progression of this topic started with this paper: *Surveilling the Masses with Wi-Fi-Based Positioning Systems*

formatting link

As Jon Ribbens did, I simply ran the Python code in that article on my PC.

Since all the dozens of access points on my property have always had both a hidden broadcast & a _nomap prefix, I was horrified to see them on the map!

At that time, we discussed this issue at length, where the following code resulted which I will provide to the team to try themselves on their PC.

12/17/2025 02:28 PM 2,934 bssid.bat.txt 12/17/2025 02:28 PM 4,309 bssidcheck.bat.txt 12/17/2025 02:28 PM 1,979 bssidcompare.bat.txt 12/17/2025 02:28 PM 316 bssidgenerate.bat.txt 12/17/2025 02:28 PM 4,312 bssidgenerate.ps1.txt 12/17/2025 02:28 PM 1,348 bssidplot.py.txt 12/17/2025 02:28 PM 27,527 bssid_results.txt

Let's start with the first on that list, and proceed from there, so that anyone with this code can track every single router location in the world.

Here is bssid.bat

@echo off setlocal EnableDelayedExpansion :: C:\app\os\python\apple_bssid_locator\bssid.bat :: Use: bssid.bat <Enter> (then enter desired BSSID to look up) :: Sample values: :: 00:18:f8:c1:4a:65 :: 00:07:89:d7:82:e8 :: 04:09:A5:3B:34:67 :: :: Logs up to 400 BSSID:GPS pairs from Apple's WPS public database :: Loop until user types q :: :: Changelog: :: v1p0 20251205 - Query Apple's highly insecure WPS database :: v1p1 20251214 - Saves to time-date stamped results.txt log file :: v1p2 20251215 - Timestamp results.txt so it's not overwritten :: v1p3 20251219 - Limit the human-readable GPS to 6 decimal places :: v1p4 20251219 - Show original raw integers + converted decimals :: v1p5 20251219 - Tried to accomodate Google Maps query to new format :: v1p6 20251219 - Changed to block-aware parsing with debug output :: v1p7 20251219 - Enable delayed expansion to fix parsing inside loops set LOGDIR=%~dp0log if not exist "%LOGDIR%" mkdir "%LOGDIR%" :: Create a unique session log (YYYYMMDD_HHMMSS) for /f %%A in ('wmic os get localdatetime ^| find "."') do set dt0=%%A set "session_ts=%dt0:~0,8%_%dt0:~8,6%" set "session_log=%LOGDIR%\session_%session_ts%.log" echo === New BSSID lookup session started at %date% %time% === >>

"%session_log%" echo. echo === Nearby Wi-Fi Networks === netsh wlan show networks mode=bssid echo ============================= :loop echo. set /p BSSID=Enter the BSSID (or q to quit): if /I "%BSSID%"=="q" goto end :: --- Clean up input --- set "BSSID=%BSSID:"=%" set "BSSID=%BSSID: =%" :: --- Make filename-safe version --- set "safeBSSID=%BSSID::=-%" :: --- Generate timestamp for THIS lookup --- for /f %%A in ('wmic os get localdatetime ^| find "."') do set dt=%%A set "ts=%dt:~0,8%_%dt:~8,6%" :: --- Timestamped output file --- set "outfile=%LOGDIR%\bssid_%safeBSSID%_%ts%.txt" :: --- Clear previous coordinates --- set "LAT=" set "LON=" echo === Lookup started at %date% %time% === > "%outfile%" echo BSSID: %BSSID% >> "%outfile%" echo. >> "%outfile%" :: --- Run Python lookup --- python.exe apple_bssid_locator.py %BSSID% --all >> "%outfile%" :: --- Display results --- echo ----------------------------------------------- type "%outfile%" echo ----------------------------------------------- :: --- Block-aware parsing of coordinates (with delayed expansion) --- set "CAPTURE=" set "LAT=" set "LON=" for /f "usebackq delims=" %%L in ("%outfile%") do ( if /i "%%L"=="BSSID: %BSSID%" ( set "CAPTURE=1" set "LAT=" set "LON=" echo [DEBUG] Found block start for %BSSID% ) else if defined CAPTURE ( echo [DEBUG] Line in block: %%L echo %%L | findstr /i /c:"Latitude (degrees):" >nul if not errorlevel 1 ( for /f "tokens=2 delims=:" %%A in ("%%L") do set "LAT=%%A" if defined LAT set "LAT=!LAT: =!" echo [DEBUG] Parsed LAT candidate = "!LAT!" ) echo %%L | findstr /i /c:"Longitude (degrees):" >nul if not errorlevel 1 ( for /f "tokens=2 delims=:" %%B in ("%%L") do set "LON=%%B" if defined LON set "LON=!LON: =!" echo [DEBUG] Parsed LON candidate = "!LON!" ) if defined LAT if defined LON ( goto :gotCoords ) echo %%L | findstr /i /c:"BSSID:" >nul if not errorlevel 1 ( set "CAPTURE=" ) ) ) :gotCoords echo [DEBUG] Final LAT = "!LAT!" echo [DEBUG] Final LON = "!LON!" :: --- Validate parsed coordinates --- if not defined LAT echo [DEBUG][ERROR] Latitude not captured. Check Python output format near "BSSID: %BSSID%". if not defined LON echo [DEBUG][ERROR] Longitude not captured. Check Python output format near "BSSID: %BSSID%". :: --- Save results --- echo === Lookup finished at %date% %time% === >> "%outfile%" echo. >> "%outfile%" :: --- Append to session log --- echo [%date% %time%] BSSID: %BSSID% >> "%session_log%" echo Latitude: !LAT! >> "%session_log%" echo Longitude: !LON! >> "%session_log%" echo. >> "%session_log%" :: --- Append to master log --- echo [%date% %time%] BSSID: %BSSID% >> "%LOGDIR%\results.log" echo Latitude: !LAT! >> "%LOGDIR%\results.log" echo Longitude: !LON! >> "%LOGDIR%\results.log" echo. >> "%LOGDIR%\results.log" :: --- Open in Google Maps --- if defined LAT if defined LON start msedge "

formatting link
!" goto loop :end echo Exiting. Goodbye! endlocal :: end of C:\app\os\python\apple_bssid_locator\bssid.bat

bssid.bat

bssidcheck.bat

@echo off :: This is C:\app\os\python\apple_bssid_locator\bssidcheck.bat :: v1p0 20251217 :: reads a list of made-up BSSIDs from bssidcheck.txt :: outputs to the console and into a timestamped log file :: v1p1 20251217 :: added fullpath to output files in the console output :: v1p2 20251217 :: only log when a lookup is successful :: v1p3 20251217 :: added summary section of bssid's that were found :: Begin Log setup set LOGDIR=%~dp0log if not exist "%LOGDIR%" mkdir "%LOGDIR%" for /f %%A in ('wmic os get localdatetime ^| find "."') do set dt0=%%A set "session_ts=%dt0:~0,8%_%dt0:~8,6%" set "session_log=%LOGDIR%\session_%session_ts%.log" :: End Log setup :: --- begin Success list setup --- set "FOUND_LIST=" :: --- end Success list setup --- echo === New BSSID lookup session started at %date% %time% === >> "%session_log%" echo Session log: %session_log% :: Begin FILE MODE if exist "%~dp0bssidcheck.txt" ( echo Processing BSSIDs from bssidcheck.txt... for /f "usebackq tokens=*" %%X in ("%~dp0bssidcheck.txt") do ( set "BSSID=%%X" call :process_bssid ) goto end ) :: End FILE MODE :: Begin INTERACTIVE MODE :loop echo. set /p BSSID=Enter the BSSID (or q to quit): if /I "%BSSID%"=="q" goto end call :process_bssid goto loop :: End INTERACTIVE MODE :: Begin PROCESS ONE BSSID :process_bssid :: --- Clean up input --- set "BSSID=%BSSID:"=%" set "BSSID=%BSSID: =%" :: --- begin Make filename-safe version --- set "safeBSSID=%BSSID::=-%" :: --- end Make filename-safe version --- :: --- begin Generate timestamp for THIS lookup --- for /f %%A in ('wmic os get localdatetime ^| find "."') do set dt=%%A set "ts=%dt:~0,8%_%dt:~8,6%" :: --- end Generate timestamp for THIS lookup --- :: --- begin Timestamped output file --- set "outfile=%LOGDIR%\bssidlookup_%ts%_%safeBSSID%.log" :: --- end Timestamped output file --- :: --- begin Clear previous coordinates --- set LAT= set LON= :: --- end Clear previous coordinates --- :: --- begin Run Python lookup --- echo === Lookup started at %date% %time% === > "%outfile%" echo BSSID: %BSSID% >> "%outfile%" echo. >> "%outfile%" python.exe apple_bssid_locator.py %BSSID% --all >> "%outfile%" :: --- end Run Python lookup --- :: --- begin Display results --- echo ----------------------------------------------- type "%outfile%" echo ----------------------------------------------- if defined LAT if defined LON echo Log written to: %outfile% :: --- end Display results --- :: --- begin Extract coordinates --- for /f "tokens=2 delims=: " %%A in ('findstr /i "Latitude" "%outfile%"') do set LAT=%%A for /f "tokens=2 delims=: " %%B in ('findstr /i "Longitude" "%outfile%"') do set LON=%%B echo === Lookup finished at %date% %time% === >> "%outfile%" echo. >> "%outfile%" :: --- end Extract coordinates --- :: --- begin Append to session log --- if defined LAT if defined LON ( echo [%date% %time%] BSSID: %BSSID% >> "%session_log%" echo Latitude: %LAT% >> "%session_log%" echo Longitude: %LON% >> "%session_log%" echo. >> "%session_log%" :: Add to success list set "FOUND_LIST=%FOUND_LIST% %BSSID%" ) :: --- end Append to session log --- :: --- begin Append to master log --- if defined LAT if defined LON ( echo [%date% %time%] BSSID: %BSSID% >> "%LOGDIR%\bssidcheck_%session_ts%.log" echo Latitude: %LAT% >> "%LOGDIR%\bssidcheck_%session_ts%.log" echo Longitude: %LON% >> "%LOGDIR%\bssidcheck_%session_ts%.log" echo. >> "%LOGDIR%\bssidcheck_%session_ts%.log" ) :: --- end Append to master log --- :: --- begin Open in Google Maps --- if defined LAT if defined LON start msedge "

formatting link
" :: --- end Open in Google Maps --- goto :eof :: End PROCESS ONE BSSID :end :: --- begin Summary of successful lookups --- echo. echo ===== Summary of Successful BSSID Lookups ===== if defined FOUND_LIST ( echo %FOUND_LIST% ) else ( echo No BSSIDs were successfully located. ) echo =============================================== echo. :: --- end Summary of successful lookups --- echo Exiting. Goodbye! :: end of C:\app\os\python\apple_bssid_locator\bssidcheck.bat

bssidcheck.bat

bssidcompare.bat

@echo off :: This is C:\app\os\python\apple_bssid_locator\bssidcompare.bat :: v1p0 20251215 :: Outputs if there is movecment for any given BSSID in results.txt setlocal enabledelayedexpansion :: Threshold for movement (where 0.001 degrees is roughly 100 kilometers) set THRESH=0.001 :: Input file collected using apple_bssid_locator open source code :: These are actual BSSID:GPS pairs which you can test yourself! :: 28:6d:97:c8:5a:30 35.2948265 126.77577972 :: 00:22:3f:a5:7b:33 35.29422378 126.77641296 :: 10:62:e5:b1:8f:12 35.29444885 126.77671051 :: 12:09:a5:53:df:13 35.29491043 126.77599334 :: 28:6d:97:4f:be:d0 35.29439163 126.77655029 :: 28:6d:97:b9:89:96 35.29463195 126.77554321000001 :: 42:09:a5:53:df:13 35.294940940000004 126.7760086 set INFILE=results.txt echo Checking for GPS movement greater than %THRESH% degrees... echo. :: Loop through each line of results.txt for /f "tokens=1,2,3 delims= " %%A in (%INFILE%) do ( set BSSID=%%A set LAT=%%B set LON=%%C :: If we've seen this BSSID before, compare if defined lastLAT[!BSSID!] ( set /a diffLAT=1000000*( !LAT! - !lastLAT[!BSSID!]! ) set /a diffLON=1000000*( !LON! - !lastLON[!BSSID!]! ) :: Convert to absolute values if !diffLAT! lss 0 set /a diffLAT=-!diffLAT! if !diffLON! lss 0 set /a diffLON=-!diffLON! :: Compare against threshold (scaled by 1,000,000) set /a threshScaled=%THRESH%*1000000 if !diffLAT! gtr !threshScaled! ( echo BSSID !BSSID! moved in LAT by more than %THRESH% degrees ) if !diffLON! gtr !threshScaled! ( echo BSSID !BSSID! moved in LON by more than %THRESH% degrees ) ) :: Store current coordinates set lastLAT[!BSSID!]=!LAT! set lastLON[!BSSID!]=!LON! ) echo. echo Comparison complete. endlocal :: end of C:\app\os\python\apple_bssid_locator\bssidcompare.bat

bssidcompare.bat

bssidgenerate.bat

@echo off :: C:\app\os\python\apple_bssid_locator\bssidgenerate.bat :: Runs bssidgenerate.ps1 to generate random/sequential realistic BSSIDs set "SCRIPT=%~dp0bssidgenerate.ps1" echo Running PowerShell script: echo %SCRIPT% echo. powershell -NoLogo -NoProfile -ExecutionPolicy Bypass -File "%SCRIPT%"

bssidgenerate.bat

bssidgenerate.ps1

# C:\app\os\python\apple_bssid_locator\bssidgenerate.ps1 # Usemodel: bssidgenerate.bat (runs this powershell script) # v1p0 20251217 # Generates BSSIDs based on user input. # Prompts for: # 1. OUI (first half), e.g., F4:F2:6D (TP-Link) # 2. Number of BSSIDs to generate (e.g., 100) # 3. Mode: R = random, S = sequential # v1p1 20251217 # Saves results to bssidinput.txt # Forces an OUI in case enter is hit prematurely # v1p2 20251217 # Added menu of the ten most common USA OUI's # v1p3 20251217 # Added sequential-mode starting block # v1p4 20251217 # Added wraparound protection (at FF:FF:FF) # --- BEGIN OUI MENU BLOCK --- Write-Host "" Write-Host "Select an OUI from the menu or press Enter to type your own:" Write-Host " 1) TP-Link F4:F2:6D" Write-Host " 2) Netgear 44:94:FC" Write-Host " 3) Arris 60:31:97" Write-Host " 4) Ubiquiti 24:A4:3C" Write-Host " 5) Technicolor 10:13:31" Write-Host " 6) Cisco/Linksys 00:25:9C" Write-Host " 7) ASUS AC:9E:17" Write-Host " 8) Google/Nest F4:F5:D8" Write-Host " 9) Amazon/Eero 74:83:C2" Write-Host " 10) Hitron C8:3A:35" Write-Host "" $choice = Read-Host "Enter menu number (1-10) or press Enter to type your own" switch ($choice) { "1" { $oui = "F4:F2:6D" } "2" { $oui = "44:94:FC" } "3" { $oui = "60:31:97" } "4" { $oui = "24:A4:3C" } "5" { $oui = "10:13:31" } "6" { $oui = "00:25:9C" } "7" { $oui = "AC:9E:17" } "8" { $oui = "F4:F5:D8" } "9" { $oui = "74:83:C2" } "10" { $oui = "C8:3A:35" } default { $oui = $null } } # --- END OUI MENU BLOCK --- # Ask for the first half (OUI) if (-not $oui) { $oui = Read-Host "Enter the first half (e.g., F4:F2:6D)" } $oui = $oui.ToUpper().Replace("-",":").Trim() # Prevent empty OUI if (-not $oui) { Write-Host "You must enter an OUI such as F4:F2:6D." exit } # Ask how many BSSIDs to generate $count = Read-Host "How many BSSIDs do you want to generate?" $count = [int]$count # Ask for mode: random or sequential $mode = Read-Host "Random or Sequential? (R/S)" $mode = $mode.ToUpper() Write-Host "" Write-Host "Generating BSSIDs..." Write-Host "" # Output file $outfile = "bssidinput.txt" Clear-Content -Path $outfile -ErrorAction SilentlyContinue # Function to format a number 0¡V255 as two hex digits function To-Hex([int]$n) { return "{0:X2}" -f $n } # Sequential mode if ($mode -eq "S") { # Ask for starting second-half (e.g., AB:CD:EF) $startHex = Read-Host "Enter starting second-half (e.g., AB:CD:EF) or press Enter for 00:00:00" if (-not $startHex) { $startHex = "00:00:00" } # Normalize $startHex = $startHex.ToUpper().Replace("-",":").Trim() # Convert to integer $parts = $startHex.Split(":") if ($parts.Count -ne 3) { Write-Host "Invalid starting value. Must be three bytes like AB:CD:EF." exit } $startVal = ([Convert]::ToInt32($parts[0],16) -shl 16) `

  • ([Convert]::ToInt32($parts[1],16) -shl 8) `
  • [Convert]::ToInt32($parts[2],16) $maxVal = 0xFFFFFF for ($i = 0; $i -lt $count; $i++) { $val = $startVal + $i if ($val -gt $maxVal) { Write-Host "Reached maximum value FF:FF:FF. Stopping." break } $b1 = ($val -shr 16) -band 0xFF $b2 = ($val -shr 8) -band 0xFF $b3 = $val -band 0xFF $bssid = "${oui}:{0}:{1}:{2}" -f (To-Hex $b1), (To-Hex $b2), (To-Hex $b3) Write-Host $bssid Add-Content -Path $outfile -Value $bssid } } # Random mode elseif ($mode -eq "R") { $rand = New-Object System.Random for ($i = 0; $i -lt $count; $i++) { $b1 = $rand.Next(0,256) $b2 = $rand.Next(0,256) $b3 = $rand.Next(0,256) $bssid = "${oui}:{0}:{1}:{2}" -f (To-Hex $b1), (To-Hex $b2), (To-Hex $b3) Write-Host $bssid Add-Content -Path $outfile -Value $bssid } } else { Write-Host "Invalid choice. Please enter R or S." } Write-Host "" Write-Host "Output file saved to: $(Resolve-Path $outfile)" # end of C:\app\os\python\apple_bssid_locator\bssidgenerate.ps1
bssidgenerate.ps1

bssidplot.py

import folium # This is C:\app\os\python\bssidplot.py # USAGE: python bssidplot.py # v1p0 20251214 # Plots en masse GPS:BSSID pairs from results.txt using FOSS Folium # v1p1 20251215 # Generates bssid_map.html from results.txt using Folium # v1p2 20251215 # Brings up the bssid_map.html plot using the default web browser import folium # Read results.txt points = [] with open("results.txt") as f: for line in f: parts = line.strip().split("\t") if len(parts) == 3: mac, lat, lon = parts points.append((mac, float(lat), float(lon))) if not points: print("No points found in results.txt") exit() # Center map on the first point start_lat, start_lon = points[0][1], points[0][2] m = folium.Map(location=[start_lat, start_lon], zoom_start=15) # Add markers for mac, lat, lon in points: folium.Marker( location=[lat, lon], popup=f"{mac}\n({lat}, {lon})", icon=folium.Icon(color="blue", icon="wifi", prefix="fa") ).add_to(m) # Save map m.save("bssid_map.html") print("Map saved to bssid_map.html") # Open map in Microsoft Edge import subprocess import os map_path = os.path.abspath("bssid_map.html") import subprocess subprocess.Popen(['cmd', '/c', 'start', '', map_path]) # end of C:\app\os\python\bssidplot.py

bssidplot.py

Hi Carlos,

<exasperated-sarcasm> because of the ignorance exhibited by some posters...

Let's ignore, for the moment, how *desperate* Franklin Slootweg is to troll Newsgroups: alt.comp.os.windows-10,comp.mobile.android,alt.comp.microsoft.windows Subject: This is another request to ask Frank Slootweg to stop trolling this ng Message-ID: <117kuh5$kt1$ snipped-for-privacy@nnrp.usenet.blueworldhosting.com>

Clearly, Franklin hasn't read the papers, nor the cites, nor even any of the posts in this thread, since everything he said is dead wrong...

Franklin didn't read a single word that was said in this entire thread. Not one word.

So, of course, Franklin Slootweg has no idea whatsoever of the topic here.

But you are correct that the *new* information, is super secret & so well diabolically hidden from those like Franklin that they'll never find it.

You must congratulate us on how diabolically clever we were in hiding it. Franklin Slootweg, being a troll, will _never_ find out what's new, Carlos.

But you can, can't you. HINT: *Simply L@@K at the SUBJECT of this thread for the hidden answer...*

Isn't it amazing how well we hid from Frank what's new in the SUBJECT line!

</exasperated-sarcasm> due to the ungodly ignorance being exhibited here...

Hi Jon Ribbens,

You are intelligent, and I've been a bit brutal to those who have been idiotically trolling this newsgroup without even reading the subject line, so I want to first thank you for actually reading the paper & testing it.

*Surveilling the Masses with Wi-Fi-Based Positioning Systems*
formatting link

You are at the stage where you ran the apple_bssid_locator.py script and you found that your own BSSID/GPS pair was clearly in the results.

I had run the same code as you just did, where I was horrified a dozen of mine were in the results since _all_ of them had _nomap on the SSID!

Luckily, I live near to plenty of Silicon Valley executives, where my next-door neighbor happens to an executive in the Apple Maps group.

When I brought the topic up to him, he at first considered it a likely bug, and then his engineers tried to snow me by saying the issue wasn't reproducible, but I kept hammering him on an honest answer.

In the end, he said I could only talk to the lawyers, who, in the end, said they'd "fix" the documentation, which, let's be clear, is the topic.

Apple did fix the documentation. Apple has no intention of honoring what EVERY other company already honors!

Subsequently, I had discussions with Daniel Veditz and Brian Krebs, and they agreed with the tests, so at this point, there isn't much we can do.

At this point, you seem to be the only one who displays any indication that they understood the topic of this paper & what it means for privacy (given you ran the python code and it clearly told you where you lived).

I appreciate that you followed up by running the apple_bssid_locator.py script, where I went another step further to modify that script so that it not only reports a single output, but the next nearest 400 BSSIDs.

With the code below and the code I posted in response to Keith Roberts who claimed, apparently, that you hadn't run the code you did run, you can literally track any router BSSID location anywhere in the world.

Forever...

Here is the improved apple_bssid_locator.py for you to test, where I simply ask of you to run it, and for you to explain to the newsgroups what it does, as people seem to be saying I can't explain things well.

Maybe you can do better than I at explaining what this code does?

#!/usr/bin/env -S uv run --script # -*- coding: utf-8 -*- # C:\app\os\python\apple_bssid_locator\apple_bssid_locator.py # Queries Apple WPS database for GPS:BSSID location pairs # Implementation based on

formatting link
# # Usage: apple_bssid_locator.py 11:22:33:AA:BB:CC # Usage: apple_bssid_locator.py 11:22:33:AA:BB:CC --all # Usage: apple_bssid_locator.py 11:22:33:AA:BB:CC --map # # Changelog: # v1p0 20251205 - Initial version from apple_bssid_locator.py # v1p1 20251214 - Added logging to results.txt # v1p2 20251215 - Timestamped results.txt to avoid overwrites # v1p3 20251219 - Limited output to 6 decimal places # v1p4 20251219 - Added raw integer output alongside converted decimals # v1p5 20251222 - Fixed raw to decimal conversion (divide by 100 Million) import argparse import requests import webbrowser import AppleWLoc_pb2 def parse_arguments(): parser = argparse.ArgumentParser() parser.add_argument("bssid", type=str, help="display the location of the bssid") parser.add_argument("-m", "--map", help="shows the location on google maps", action='store_true') parser.add_argument("-a", "--all", help="shows all results returned, not just the requested one", action='store_true') args = parser.parse_args() return args def format_bssid(bssid): return ':'.join(e.rjust(2, '0') for e in bssid.split(':')) def query_bssid(bssid, output_file="results.txt"): apple_wloc = AppleWLoc_pb2.AppleWLoc() wifi_device = apple_wloc.wifi_devices.add() wifi_device.bssid = bssid apple_wloc.unknown_value1 = 0 apple_wloc.return_single_result = 0 # request ALL results serialized_apple_wloc = apple_wloc.SerializeToString() length_serialized_apple_wloc = len(serialized_apple_wloc) headers = {'User-Agent':'locationd/1753.17 CFNetwork/889.9 Darwin/17.2.0'} data = b"\x00\x01\x00\x05"+b"en_US"+b"\x00\x13"+b"com.apple.locationd"+b"\x00\x0a"+b"8.1.12B411"+b"\x00\x00\x00\x01\x00\x00\x00" + bytes((length_serialized_apple_wloc,)) + serialized_apple_wloc r = requests.post('
formatting link
', headers=headers, data=data) apple_wloc = AppleWLoc_pb2.AppleWLoc() apple_wloc.ParseFromString(r.content[10:]) # Build dictionary of results results = {} with open(output_file, "w") as f: for wifi_device in apple_wloc.wifi_devices: if wifi_device.HasField('location'): raw_lat = wifi_device.location.latitude raw_lon = wifi_device.location.longitude lat = raw_lat * 1e-8 lon = raw_lon * 1e-8 mac = format_bssid(wifi_device.bssid) results[mac] = (lat, lon, raw_lat, raw_lon) # Write both raw integers and converted decimals (8 decimal places) f.write(f"{mac}\t{raw_lat}\t{raw_lon}\t{lat:.8f}\t{lon:.8f}\n") print(f"Saved {len(results)} entries to {output_file}") return results def main(): args = parse_arguments() print("Searching for location of bssid: %s" % args.bssid) results = query_bssid(args.bssid) # Determine which BSSIDs to process bssids_to_process = results.keys() if args.all else [args.bssid.lower()] found = False for bssid in bssids_to_process: if bssid in results: lat, lon, raw_lat, raw_lon = results[bssid] if lat == -180.0 and lon == -180.0: continue # Skip entries that were not found if found: print() print(f"BSSID: {bssid}") print(f"Raw latitude integer: {raw_lat}") print(f"Raw longitude integer: {raw_lon}") print(f"Latitude (degrees): {lat:.8f}") print(f"Longitude (degrees): {lon:.8f}") if args.map: url = f"
formatting link
{lat:.8f},{lon:.8f}" webbrowser.open(url) found = True if not found: print("The bssid was not found.") if __name__ == '__main__': main() # end of C:\app\os\python\apple_bssid_locator\apple_bssid_locator.py

With the code supplied above, anyone can easily track the movements of every access point BSSID in the world (that is in Apple's WPS database). *Surveilling the Masses with Wi-Fi-Based Positioning Systems*

formatting link

Note the word "mass" indicates we can not only track a single BSSID's location, but we can track every single BSSID in the world's location.

It's trivial to do, but only with Apple's WPS database. It cannot be done with any other WPS database that I am aware of.

To his credit, Jon Ribbens proved the veracity of these statements by tracking his own BSSID location using the Python code in that article.

==================================================================== From: Jon Ribbens <jon+ snipped-for-privacy@unequivocal.eu Newsgroups: comp.lang.python,misc.phone.mobile.iphone,comp.mobile.android,alt.internet.wireless,alt.comp.os.windows-10 Subject: Re: Apple changed their documentation at my request but it proves they don't care about privacy Date: Sun, 6 Sep 2026 00:06:34 -0000 (UTC) Message-ID: <slrn119pbka.8k8.jon+ snipped-for-privacy@raven.unequivocal.eu>

Yes, I know that too now, after reading the paper. I downloaded the code at

formatting link
(which was originally uploaded in 2015) and ran it locally with my own AP MAC address and confirmed it showed my home location very accurately.

It does concern me that there is an attack model here which is that a mildly technically-inclined stalker can very easily get the BSSID of their victim and, as you say, find out where they've gone if they move house to get away from them. ====================================================================

For others to benefit from, in my records is this initial version of apple_bssid_locator.py where when I ran it, I was horrified to find that every one of my dozens of hidden access point BSSIDs were there!

I suggest each user on this thread who wishes to better understand the nature of the problem set (as described by the paper), run this code.

#!/usr/bin/env -S uv run --script # -*- coding: utf-8 -*- # C:\app\os\python\apple_bssid_locator\apple_bssid_locator.py # Queries Apple WPS database for GPS:BSSID location pairs # Implementation based on

formatting link
# # Usage: apple_bssid_locator.py 11:22:33:AA:BB:CC # Usage: apple_bssid_locator.py 11:22:33:AA:BB:CC --all # Usage: apple_bssid_locator.py 11:22:33:AA:BB:CC --map # # Changelog: # v1p0 20251205 - Initial version from apple_bssid_locator.py # v1p1 20251214 - Added logging to results.txt # v1p2 20251215 - Timestamped results.txt to avoid overwrites # v1p3 20251219 - Limited output to 6 decimal places # v1p4 20251219 - Added raw integer output alongside converted decimals # v1p5 20251222 - Fixed raw to decimal conversion (divide by 100 Million) import argparse import requests import webbrowser import AppleWLoc_pb2 def parse_arguments(): parser = argparse.ArgumentParser() parser.add_argument("bssid", type=str, help="display the location of the bssid") parser.add_argument("-m", "--map", help="shows the location on google maps", action='store_true') parser.add_argument("-a", "--all", help="shows all results returned, not just the requested one", action='store_true') args = parser.parse_args() return args def format_bssid(bssid): return ':'.join(e.rjust(2, '0') for e in bssid.split(':')) def query_bssid(bssid, output_file="results.txt"): apple_wloc = AppleWLoc_pb2.AppleWLoc() wifi_device = apple_wloc.wifi_devices.add() wifi_device.bssid = bssid apple_wloc.unknown_value1 = 0 apple_wloc.return_single_result = 0 # request ALL results serialized_apple_wloc = apple_wloc.SerializeToString() length_serialized_apple_wloc = len(serialized_apple_wloc) headers = {'User-Agent':'locationd/1753.17 CFNetwork/889.9 Darwin/17.2.0'} data = b"\x00\x01\x00\x05"+b"en_US"+b"\x00\x13"+b"com.apple.locationd"+b"\x00\x0a"+b"8.1.12B411"+b"\x00\x00\x00\x01\x00\x00\x00"

  • bytes((length_serialized_apple_wloc,)) + serialized_apple_wloc r = requests.post('
    formatting link
    ', headers=headers, data=data) apple_wloc = AppleWLoc_pb2.AppleWLoc() apple_wloc.ParseFromString(r.content[10:]) # Build dictionary of results results = {} with open(output_file, "w") as f: for wifi_device in apple_wloc.wifi_devices: if wifi_device.HasField('location'): raw_lat = wifi_device.location.latitude raw_lon = wifi_device.location.longitude lat = raw_lat * 1e-8 lon = raw_lon * 1e-8 mac = format_bssid(wifi_device.bssid) results[mac] = (lat, lon, raw_lat, raw_lon) # Write both raw integers and converted decimals (8 decimal places) f.write(f"{mac}\t{raw_lat}\t{raw_lon}\t{lat:.8f}\t{lon:.8f}\n") print(f"Saved {len(results)} entries to {output_file}") return results def main(): args = parse_arguments() print("Searching for location of bssid: %s" % args.bssid) results = query_bssid(args.bssid) # Determine which BSSIDs to process bssids_to_process = results.keys() if args.all else [args.bssid.lower()] found = False for bssid in bssids_to_process: if bssid in results: lat, lon, raw_lat, raw_lon = results[bssid] if lat == -180.0 and lon == -180.0: continue # Skip entries that were not found if found: print() print(f"BSSID: {bssid}") print(f"Raw latitude integer: {raw_lat}") print(f"Raw longitude integer: {raw_lon}") print(f"Latitude (degrees): {lat:.8f}") print(f"Longitude (degrees): {lon:.8f}") if args.map: url = f"
    formatting link
    {lat:.8f},{lon:.8f}" webbrowser.open(url) found = True if not found: print("The bssid was not found.") if __name__ == '__main__': main() # end of C:\app\os\python\apple_bssid_locator\apple_bssid_locator.py
apple_bssid_locator.py

Usenet welcomes technical discussion of topics that are relevant to the newsgroups in which they are posted.

I don't know or care where Jon Ribbens obtained any Python code. Perhaps you provided it to him directly. It wasn't posted here.

I don't know how you misunderstood what I wrote so badly.

I haven't asked for Python code. I've observed that you haven't posted any Python code, and therefore your posts should not be in comp.lang.python. If you want to talk about Python here in comp.lang.python, that's great. If you don't, I wish you'd go away.

Are you claiming that there's Python code in that paper? There is none in the PDF. (I didn't read it in any depth because it's not something I care about.)

I see you've posted some Python source code later in this thread, along with Windows batch and PowerShell. Since you haven't said anything about the code (how it's written, what features it uses, how it could be improved), I suggest that it's (a) far too late to excuse you behavior so far, and (b) still inappropriate for comp.lang.python.

Feel free to have the last word and pretend you've won.

Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com void Void(void) { Void(); } /* The recursive call of the void */

He achieved nothing. It's all weasel words. - nothing but a vague attempt(regardless of the size of multiple missives) to sound impressive and/or meaningful without any concrete proof.

...w¡ñ§±¤ñ

Some folks in the midwest would say he has a paper ass, I guess.

Join the Discussion

Have something to add? Share your thoughts — no account required.

Didn't find your answer?

Ask the community — no account required