10,501 Radios in Seven Days: What a $69 Badge Heard at Summer Camp

Seven days of passive listening at BSides, Black Hat and DEF CON: 673 open networks, one beacon flooder, and what it takes to count devices honestly.

I have wanted to build one of these for a long time. A little screen on my bag that tells me what the air around me is actually made of, not just that there are networks nearby, but what they are. The thing that finally made it possible was the Pimoroni Tufty 2350 showing up at Adafruit: an RP2350B, a 2.8″ colour screen, WiFi and Bluetooth, a 1000 mAh battery in the box, and MicroPython already flashed on it. Sixty-nine dollars, and nothing to install before you can write a line of code.

The billboard view, mid-week. Thirty-three radios logged as devices, 259 in range right now, and 254 rotating addresses thrown away. That last number is what most of this post is about.

The other thing that made it doable was pairing with Claude for a week. I have built enough hardware side projects to know how they usually end, which is in a drawer, two thirds finished, blocked on the boring part. This one did not, and the reason is that the boring parts got done in the gaps between talks.

So: seven days, one badge, passive listening only. Here is what it found and, more usefully, everything I believed on Monday that turned out to be wrong by Sunday.

TL;DR

Seven days of Summer Camp produced 4,653 access points and 5,848 Bluetooth devices, all of it passive. No association, no probe requests, no packet capture. Just beacons and advertisements, which every device in the room broadcasts to anyone who bothers to listen.

The two findings I would put on a slide: 673 open networks, and one beacon flooder pushing 77 fake networks across all 13 channels, impersonating ISP defaults from six countries. And the correction that took the longest: my first device count was 8.8% inflated, because a badge that counts addresses is not yet counting devices.

Three caveats up front. This is a listening device, not an attack tool. It never associates and never captures traffic, and counts of rotating addresses are upper bounds by design.

Bluetooth scanning was active until Thursday. Active scanning sends a scan request, which means the badge was transmitting for the first three days. It ran passive from Thursday on, for reasons in the scanning section below. WiFi was passive throughout.

And it only hears 2.4 GHz. The Tufty’s radio module is single-band, so every access point number in this post excludes 5 GHz and 6 GHz entirely. Across 15,446 access point records all week, not one sat above channel 13. The real access point count in those buildings is meaningfully higher than 4,653.

Key Statistics at a Glance

MetricValue
Access points4,653
Bluetooth devices5,848
Seen on more than one day2,552
Open networks673 (14.5%)
WEP access points88
Fake networks from one emitter77
Spoofed network names39
Espressif (ESP32) devices544
Days carried7
Radio2.4 GHz only (no 5 GHz seen, ever)
Times the badge hung and recovered itself92 (Thu-Sun only)
Lines of MicroPython (the app)2,480

Somebody Was Flooding Fake Networks

The badge classified 88 access points as WEP, an encryption scheme broken since 2001 and shipped by essentially nothing built this decade. Worth being precise about what that means: the badge reads a driver auth-mode index rather than parsing the actual security element, so “WEP” here most likely means the privacy bit was set with nothing valid behind it. That is a fingerprint of a beacon generator, not of 2001-era hardware. What makes it a finding rather than a curiosity is that 77 of them had locally administered MAC addresses, meaning the hardware address was invented in software rather than burned in at the factory.

WEP plus an invented MAC is suspicious, but not conclusive on its own: plenty of legitimate multi-BSSID, mesh and phone-hotspot gear sets the locally administered bit too. What closes the case is the SSID list.

The network names close the case. Thirty-nine of them. Here are the ones that give it away:

CategoryNames
US ISPsxfinitywifioptimumwifiTWCWiFiMySpectrumWiFi
UKSKYVirgin MediaBTWiFi-with-FON
GermanyTelekom_FONVodafone HotspotFRITZ!Box 7490
FranceorangeSFR WiFi MobileFreeWifi
Netherlands / BelgiumKPN FonZiggoTELENETHOMESPOT
The free-WiFi trapGoogle StarbucksMcDonald's Free WiFiAirport Free WiFiHotel WiFi
Universitieseduroam
Router defaultsNETGEARTP-LinklinksysASUSsetup
The restGuestHomeHOME-internetwirelessWLANAndroidAPiPhoneSamsungDIRECT-HP-PrintNetgear-5GStarbucks WiFi

My first instinct was to call this a karma rig, and my own data says it is not one.

Karma is a response technique: it listens for a phone’s probe request and answers with the exact network name that phone asked for. Three of these names are bare wordlist prefixes, DIRECT-HP-Print and HOME-, and no client has ever probed for those literal strings. That is not truncation on my end either; the same capture holds DIRECT-0F-HP PageWide Pro 577 and HP-Print-D7-Officejet 6600 at full length.

Two more tells point the same way. Every one of these is advertised with encryption set, but the real xfinitywifi and eduroam are open networks, so nothing would auto-join these anyway. And they are spread near-evenly across all thirteen channels, including 12 and 13, which are not legal in the US and where American phones largely will not associate. Everything else in the week clusters hard on channels 1, 6 and 11.

So this is a beacon flooder reading a name list off disk, not a rig fishing for associations. Most likely somebody’s ESP32 or an mdk4 demo, left running.

It ran on Monday and essentially never again. 76 of the 77 were first heard on BSides day one; exactly one more turned up on Friday. Across the entire DEF CON weekend the badge saw two WEP access points total: one called Hac-Man Contest Network on real TP-Link hardware with a burned-in address, and one more invented-MAC rogue broadcasting no name at all, which was the seventy-seventh and last of the flooded set.

I want to be careful here: this is almost certainly a demo, a village exercise, or someone’s research, and beacon flooding does not compromise anything by itself. It fills the air with names. But it is a good illustration of how little it costs to make a room look like it contains forty networks that do not exist.


What the Air Was Secured With

Wednesday was the densest WiFi day, which is Black Hat’s business hall doing what business halls do, and Friday was the densest for Bluetooth, which is DEF CON’s first full day. 2,552 access points and devices were seen on more than one day, which is 1,117 access points and 1,435 Bluetooth devices.

It is worth saying what that figure can and cannot mean. A device that rotates its address is structurally incapable of appearing on two different days in this dataset, so the multi-day count is not a measure of returning attendees. It is a census of hardware with no address privacy, and it looks like it: 90% of those returning Bluetooth devices have burned-in public addresses, led by casino hardware and the venue’s beacon estate.

673 open networks, 14.5% of everything seen. Most of that is the resort’s own guest WiFi, which is doing exactly what resort guest WiFi does. MGMResorts-WiFi alone accounted for 366 access points.

Worth repeating here: this is the 2.4 GHz picture only. Plenty of modern enterprise gear puts its best-configured, WPA3-capable radios on 5 GHz and leaves 2.4 GHz for the guest and legacy traffic, so the mix below is probably the least flattering slice of these networks rather than a fair sample of them.

WPA2 still dominates at 79.9%.

I had a paragraph here about WPA3 adoption being stuck at 3.7%. I cut it, because the badge cannot actually see WPA3, and the proof is sitting in my own capture: all 38 access points broadcasting the SSID DefCon-WPA3 came back classified as WPA2. The badge reads an auth-mode index from the radio driver, not the network’s actual security element, and a WPA3-only network lands in the WPA2 bucket by construction. Not one access point all week resolved as WPA3 on its own, which tells you about my instrument rather than about the buildings.

So the honest version is narrower: 3.7% advertised something the driver reported as WPA2/WPA3 transition mode, and I cannot give you a real WPA3 adoption number from this hardware.


What a Hacker Conference Is Actually Made Of

544 Espressif devices. That is the ESP32, and at DEF CON it is not IoT, it is badges. Within that 544, 134 advertise as Espressif Battery and another 66 simply call themselves Me, which is a picture of the independent badge scene that no attendance number captures.

The rest of the list is a Vegas conference centre being a Vegas conference centre. 685 devices on Panasonic’s Matsushita OUI, 158 of which identify themselves as IGT card readers, plus 71 more on Panasonic’s industrial block: almost certainly the casino floor advertising all week. I did not verify what any individual unit is, and I did not go looking. 564 Apple iBeacons, and not one of them is an Apple device. They are Texas Instruments, Silicon Labs and Murata modules on burned-in addresses: fixed wayfinding and retail infrastructure speaking Apple’s protocol, not a single phone among them.

And 93 Nintendo devices, 22 of them Pokemon GO Plus dongles, because it is DEF CON and people bring their Switch.


Counting Addresses Is Not Counting Devices

This is the part I actually want to write about, because the numbers above are only trustworthy because of it.

The badge shipped Monday counting addresses. It finished Sunday counting devices. Those are not the same thing, and across the week the gap was 8.8%.

That percentage is the effect on a week dominated by access points and casino hardware with burned-in addresses. In a dense, rotation-heavy room the same rules cut far harder, as the two examples below show.

Modern phones, watches, and earbuds re-randomise their Bluetooth address every fifteen minutes or so, specifically so they cannot be followed around. A naive scanner sees each rotation as a brand-new device. Four separate corrections, every one of them forced by real capture data rather than by reading a spec:

1. Apple Find My beacons rotate, but look permanent. A 96-minute walk logged 356 distinct Find My addresses, 116 of them inside a single ten-minute window. That is roughly 55 real devices. By the address bits they look static and permanent; in practice they re-derive every fifteen minutes.

2. All of Apple Continuity does it, not just Find My. An overnight capture in one hotel room logged 99 addresses labelled “AirPods”. That is a handful of earbuds re-randomising, not 99 pairs of AirPods in adjacent rooms.

3. Apple is not special. Microsoft’s Swift Pair and Google’s Fast Pair rotate exactly the same way. A single BSides afternoon logged 153 addresses announcing Swift Pair inside fifteen minutes, which was one spammer with an ESP32, popping “add a device” dialogs at everyone walking past.

4. A label only counts if it tells devices apart. This is the general version of the other three and the one I wish I had started with. Sixty-six addresses all calling themselves T-Dongle Biscuit are one dongle, not sixty-six. The badge now counts how many random addresses have worn each name and stops believing a name once too many have.

Applied to one overnight hotel capture, those rules turned 389 “devices” into 6: a fitness band, a Tile, and four things with real burned-in addresses. Applied to an airport concourse they kept 473 of 1,200, because a concourse genuinely is full of permanent hardware.

The lesson I would hand to anyone building this: your scanner’s device count is a claim about identity, and identity is the hard part. Everything else is just parsing.


It Hung 92 Times and Kept Working

This is the part I only understood at the end of the week, and it is the engineering I am happiest with.

On Thursday I found the badge frozen at DEF CON: screen stuck, USB dead, and it had been that way for 3 hours and 40 minutes. Somewhere in the RP2350’s firmware, or the shared radio, or the interaction between the two, something wedges hard enough that no Python is running at all. I still do not know what. I tested the WiFi/Bluetooth handover across 400 cycles, the interrupt workload at conference rates, flash writes racing interrupts at 1,600 times the normal rate, and every queueing race I could construct. All clean. It will not reproduce on a desk, and the one variable a desk cannot supply is a room with ten thousand people in it.

So rather than keep chasing it, I made it survivable. An eight-second hardware watchdog reboots the badge whenever the main loop stops. The log lives on flash and de-duplicates on restart, so a reboot costs about fifteen seconds and nothing else.

Then the telemetry showed it was happening 92 times.

Roughly once every fifteen minutes, and I never noticed a single one, which is exactly what you want a watchdog to feel like. That count covers Thursday through Sunday, because the health log that records restarts did not exist before Thursday, which means the true week total is higher than 92 and I cannot tell you by how much. It also counts only restarts while the badge was on battery, so my own resets on the bench are excluded.

Before the watchdog existed, the first hang of the day simply ended the day, which is why it had looked like “a couple of lockups” rather than a steady drumbeat. Measured downtime across those four days: about 1.6%.

The fix did have a knock-on effect worth knowing about. Every restart wiped the in-memory counters that stop one spammer being logged as sixty devices. The rule held perfectly within each session, but across 36 sessions on Saturday a single label still accumulated 62. Moving those counts onto disk took one evening, and Sunday came back with every shared label capped at exactly 8.

One caveat on that, since it affects the numbers earlier in this post: the cap runs per capture session, and each day is its own session. Across seven days a rotating label can still accumulate to roughly eight per day, which is why Tile appears 46 times in the week totals rather than 8. Public burned-in addresses are exempt from the cap entirely and always were, which is why the casino hardware counts are untouched by it.


What Changed, Day by Day

Every one of these came from the badge contradicting something I believed. Not one came from reading a specification.

DayWhat the data saidWhat I changed
Sat 1stThe interrupt handler was writing to a dict the main loop was walkingQueue from the interrupt, insert from the main loop
Sun 2ndThe battery died at 2,793 mV, not the textbook 3,300Rewrote the runtime model; 6h 27m became 9h 46m
Sun 2ndMean SSID was 7.7 characters in a 32-byte field, so 45% of the log was zeroesLength-prefixed records, halving the log
Mon 3rd356 Find My addresses in 96 minutes, from roughly 55 devicesRotating addresses counted, never logged as identities
Mon 3rdSomebody spamming pairing popups locked up the screenCap admissions per frame, detect and report floods
Tue 4th153 “Microsoft Swift Pair” addresses inside fifteen minutesSwift Pair and Fast Pair rotate too, same as Apple
Tue 4thThe billboard said DEVICES over a number that included access pointsSay RADIOS, because that is what it counts
Wed 5th50 real devices read as 507/second and latched the alarm foreverMeasure address novelty, not advertisement volume
Thu 6thFrozen at DEF CON: screen dead, USB dead, 3h 40m lostHardware watchdog, so a hang costs fifteen seconds
Thu 6thThe scanner was transmitting while the README said it never didSwitched to passive scanning
Thu 6thDeploys were failing silently and I was testing the old builddeploy.sh now proves the install landed, twice
Fri 7thNo idea what the badge was doing when it frozeBreadcrumb the current phase to flash before every long call
Sat 8th34 of 36 restarts died “running normally”, not in a blocking callConfirmed a real firmware hang, not my watchdog misfiring
Sat 8thRestarts wiped the counters, so one label reached 62Rebuild the counts from disk at startup
Sun 9thEvery shared label capped at exactly 8Nothing. It finally worked.

The pattern is the thing I would take away. Every single correction came from looking at what the badge actually kept, and every single one contradicted a reasonable assumption. The spec says static-random addresses are stable. They are not, for anything Apple, Microsoft or Google ships. The spec says a device name identifies a device. It does not, when sixty-six addresses share one.


Passive Is a Setting, Not a Category

Bluetooth scanning has two modes, and the difference matters more than it looks from the outside. Passive listens. Active sends a scan request to every device it hears, asking it to say a bit more about itself.

Active is the mode you reach for when you want good device names, and it is where I started. It is also, strictly speaking, transmitting, while the README I had written described the tool as passive. The code and the documentation had quietly drifted apart, and I only noticed while looking at the radio settings for a completely unrelated reason.

Switching to passive turned out to cost nothing. Measured back to back in the same room, passive drew 34.5 advertisements per second against active’s 57.6, and found 36 devices against 34. Fewer packets on the air, and no loss of identification: 63% of Sunday’s devices carried a name beyond their manufacturer, against 56% on the last full day of active scanning. Read the device delta as no meaningful difference rather than passive winning, since it is one test in one room.

Worth checking on anything you carry: “passive scanner” describes a setting, not a category of device, and the library default is not always the one you would have chosen.


Privacy, Since This Is the Part That Matters

This badge records identifiers belonging to other people’s devices, in a city full of people who did not consent to being in my dataset.

So, the rules I held myself to. MAC addresses are personal data under the GDPR and plenty of other regimes, and Summer Camp is an international crowd. The full capture with addresses never left my laptop; everything in this post is an aggregate. Rotating addresses exist precisely because people opted out of being followed, and the counting work above is not a way around that, it is the opposite: the badge deliberately refuses to re-identify anything that has asked not to be. And the log gets erased when the analysis is done.

Passive reception is broadly lawful. What you do with the results is the part with rules attached, and “I could” is not the same as “I should.”


What I Would Change for Next Year

A second radio, or a different one. The single biggest limitation of this build is that the Tufty hears 2.4 GHz and nothing else. Raspberry Pi’s RM2 module is Wi-Fi 4, single-band, and it shows: across 15,446 access point records I never once logged a channel above 13. Everything in this post about access points describes the least interesting half of the spectrum, because the modern, well-configured, WPA3-capable radios in those buildings are mostly sitting on 5 GHz where the badge is deaf.

That also means my WPA3 number deserves an asterisk. 3.7% is real for 2.4 GHz, and I would not be surprised if the true figure across both bands is several times that.

Fixing it means either a badge with a dual-band radio, or bolting an ESP32-C5 or similar onto this one and treating the Tufty as the screen and the brains. That is next year’s project.

Find the hang. Ninety-two recoveries a week is survivable, not solved. The watchdog turned an invisible catastrophic failure into an invisible trivial one, which is a real improvement, but I would rather know what wedges the chip. That needs either a logic analyser on the radio bus or a way to reproduce conference-scale RF density on a bench.

Ship the badge fully built. Everything in this post was tuned during the week it was being used, which is a terrible way to collect a clean dataset and a wonderful way to find bugs. Next year I want the counting rules settled before day one, so the whole week is measured the same way.


Build One

Everything is open source and the whole thing runs on a $69 off-the-shelf badge with no toolchain:

It identifies what it sees using 39,877 IEEE OUI prefixes and 3,988 Bluetooth SIG company IDs compiled onto the device, so there is no network lookup and no phoning home. It runs about ten hours on the internal battery.

If you build one, I would genuinely like to see what your city looks like. Find me on LinkedInXBluesky, or infosec.exchange.

And if you were the one running the beacon flooder on Monday: nice wordlist.

Site Footer