The 'promiscious' branch is about the WiFi promiscuous detector, not BLE. Reworked the root README to lead with: - @NitekryDPaul credit front-and-center (30-OUI list + addr1 technique) - What the WiFi firmware does and why promiscuous mode is the right tool - Detection pipeline, OUI list, SPIFFS envelope format - Flask dashboard integration with the wifi_oui_addr1/addr2 methods - Hardware, build, config cheatsheet - SMB 1-2 underground boot melody The BLE firmware is now a short pointer to main at the bottom.
Flock-You: Promiscuous WiFi Edition (promiscious branch)
Passive 2.4 GHz promiscuous-mode detector for Flock Safety surveillance infrastructure. Runs standalone or feeds the Flask dashboard over USB for live GPS-tagged wardriving.
Credit
All WiFi promiscuous detection research — the 30-OUI target list, the promiscuous-mode strategy, and the addr1-receiver detection technique — is the work of ØяĐöØцяöЪöяцฐ / @NitekryDPaul. The firmware in promiscuis-flock-you/ is a mod of his original firmware with added SPIFFS persistence and Flask-dashboard integration. Full research writeup: datasets/NitekryDPaul_wifi_ouis.md.
What this branch does
Turns a Seeed XIAO ESP32-S3 into a passive WiFi receiver that watches 2.4 GHz management and data frames for Flock Safety MAC OUIs. No AP, no transmit — the radio stays dedicated to sniffing while the device hops channels 1 / 6 / 11 at 350 ms dwell.
Every detection is:
- beeped (piezo on GPIO3) and flashed (onboard LED on GPIO21)
- written to on-device SPIFFS in an atomic CRC-envelope format, surviving power loss
- emitted as one JSON line over USB CDC in the schema
api/flockyou.pyexpects, so the Flask dashboard auto-ingests it with GPS temporal matching
The device works standalone (no USB host needed) and plugged in (live dashboard) without any mode switch.
Why promiscuous mode, and why addr1
Most WiFi sniffers only check the transmitter address (addr2). Flock infrastructure spends most of its duty cycle asleep — it wakes briefly in bursts, uploads, then sleeps again. During the silence it may never transmit a single frame in your capture window.
But it may still appear on the air as the destination (addr1) of probe responses or data frames from nearby APs.
Checking addr1 in addition to addr2 picks those silent stations up. It requires two guards to avoid false positives:
addr1is broadcast (ff:ff:ff:ff:ff:ff) in beacons and broadcasts — multicast filter- Modern devices use randomised (locally-administered) MACs that can't be fingerprinted by OUI — randomised-MAC filter on byte 0 bit 1
Both are applied before the OUI match. This whole approach, including the 30-OUI list, is @NitekryDPaul's research.
Detection pipeline
[2.4GHz air]
│
▼
wifiSniffer() ← IRAM promiscuous callback (WiFi task)
│ fast match only, no Serial / no malloc
▼
alertQueue[32] ← lock-free ring buffer (ISR-safe mux)
│
▼
drainAlertQueue() ← loop() context, per-iteration drain
│
├─► fyAddDetection() ← always, every hit
│ │
│ ▼
│ fyDet[200] ← unique-by-MAC on-device table
│ │
│ ▼
│ autosaveTick() ← every 60s when dirty
│ │
│ ▼
│ fySaveSession() ← atomic CRC-envelope write to SPIFFS
│
├─► shouldSuppressDuplicate() ← 5s per-MAC serial-emit rate limit
│
└─► emitDetectionJSON() ← USB CDC line for Flask
buzzerBeep() + ledFlash()
The split between callback and loop is deliberate: the WiFi task has hard real-time constraints and cannot call Serial.print or malloc safely. The callback writes only to the lock-free ring buffer; loop() does all the heavy work.
OUI target list (@NitekryDPaul research)
All lowercase, colon-separated. 30 Flock Safety infrastructure prefixes:
70:c9:4e 3c:91:80 d8:f3:bc 80:30:49 b8:35:32
14:5a:fc 74:4c:a1 08:3a:88 9c:2f:9d c0:35:32
94:08:53 e4:aa:ea f4:6a:dd f8:a2:d6 24:b2:b9
00:f4:8d d0:39:57 e8:d0:fc e0:4f:43 b8:1e:a4
70:08:94 58:8e:81 ec:1b:bd 3c:71:bf 58:00:e3
90:35:ea 5c:93:a2 64:6e:69 48:27:ea a4:cf:12
Pre-compiled into a byte table in setup() so the matcher stays entirely in IRAM with no flash-resident lookups during callback execution.
Full dataset and methodology: datasets/NitekryDPaul_wifi_ouis.md.
SPIFFS wire format
On-flash layout, atomic and crash-safe:
Line 1: {"v":1,"count":N,"bytes":B,"crc":"0xXXXXXXXX"}
Line 2: [{"mac":"...","method":"...","rssi":...,...},...]
Save procedure:
- Compute CRC32 + byte count over the serialised payload
- Write envelope header + payload to
/session.tmp - Re-read and re-validate
/session.tmp(CRC check) - Remove
/session.json - Atomic rename
/session.tmp→/session.json(copy+delete fallback)
Boot recovery:
- If
/session.jsonvalidates, promote it to/prev_session.json - Otherwise try
/session.tmp(interrupted save) - Delete both working files, start with an empty live table
/prev_session.jsonstays around for inspection
CRC32 uses the standard 0xEDB88320 polynomial so the same file can be verified on a host with any off-the-shelf CRC tool.
Flask dashboard integration
The firmware emits one JSON line per detection in the same schema the BLE detector uses, so api/flockyou.py picks it up with zero changes:
{"event":"detection","detection_method":"wifi_oui_addr2","protocol":"wifi_2_4ghz","mac_address":"aa:bb:cc:dd:ee:ff","oui":"aa:bb:cc","device_name":"","rssi":-62,"channel":6,"frequency":2437,"ssid":""}
detection_method values:
wifi_oui_addr2— transmitter-side OUI matchwifi_oui_addr1— receiver-side OUI match (the @NitekryDPaul technique)wifi_oui_addr3— BSSID OUI match (mgmt frames only; disabled by default)wifi_ssid— SSID keyword match (disabled by default)
GPS wardriving
GPS is handled Flask-side, since the ESP32 radio is dedicated to sniffing and there's no on-device AP. Two options:
- USB NMEA puck plugged into the host running Flask — Flask reads NMEA and timestamps a GPS timeline
- Flask dashboard open in a phone browser — browser Geolocation API posts updates to Flask
Flask does a temporal match between detection timestamp and GPS timeline, then exports JSON / CSV / KML for Google Earth.
Running Flask
cd api
pip install -r requirements.txt
python flockyou.py
Open http://localhost:5000, pick your serial port from the UI, detections start showing up live.
Hardware
Board: Seeed Studio XIAO ESP32-S3
| Pin | Function |
|---|---|
| GPIO 3 | Piezo buzzer |
| GPIO 21 | Onboard user LED (active low) |
| GPIO 43 | Serial1 TX mirror (115200 baud) |
Boot sound: first 6 notes of Super Mario Bros. World 1-2 (underground).
Build and flash
Requires PlatformIO.
cd promiscuis-flock-you
pio run # build
pio run -t upload # flash
pio device monitor # serial output
The subdirectory has its own platformio.ini and partitions.csv (1.9 MB SPIFFS partition, 6 MB app). No extra libraries needed beyond the Arduino-ESP32 core that ships with the espressif32 platform.
Config cheatsheet (top of promiscuis-flock-you/main.cpp)
| Define | Default | Notes |
|---|---|---|
CHANNEL_MODE |
CHANNEL_MODE_CUSTOM |
CUSTOM (1/6/11), FULL_HOP (1-11), or SINGLE |
CHANNEL_DWELL_MS |
350 | Time on each channel before hop |
RSSI_MIN |
-95 | Drop frames weaker than this |
ALERT_COOLDOWN_MS |
5000 | Per-MAC serial-emit rate limit |
CHECK_ADDR1 |
1 | The @NitekryDPaul receiver-side technique |
CHECK_ADDR3 |
0 | BSSID fallback (mgmt frames only) |
ENABLE_SSID_MATCH |
0 | Substring match against target_ssid_keywords[] |
PROCESS_MGMT_FRAMES |
1 | Beacons, probe req/resp, etc. |
PROCESS_DATA_FRAMES |
1 | Data frames (where addr1 catch shines) |
MAX_DETECTIONS |
200 | On-device table cap |
AUTOSAVE_INTERVAL_MS |
60000 | SPIFFS save cadence |
LED_PIN |
21 | Onboard user LED |
BUZZER_PIN |
3 | Piezo |
Standalone vs connected
Without USB: device boots, plays the SMB 1-2 intro, starts scanning, stores every unique detection to SPIFFS, flashes the onboard LED on each hit. Plug in later — the prior session is sitting in /prev_session.json.
With USB + Flask running: same thing, plus every detection streams live to the dashboard as a JSON line. Flask adds GPS (if configured) and deduplicates across MAC, building the wardriving map as you move.
Both modes work simultaneously — the SPIFFS write path doesn't care if a host is listening.
BLE companion firmware (on main branch)
The original BLE-only firmware still lives in src/main.cpp. It detects Flock and Raven gear via BLE advertisements (OUI prefix, device name, manufacturer ID 0x09C8, Raven service UUIDs), runs its own WiFi AP with a phone-facing dashboard at 192.168.4.1, and emits the same Flask JSON schema. Run both firmwares on separate boards for overlapping BLE + WiFi coverage feeding one dashboard. See the main branch README for BLE-specific details.
Acknowledgments
- ØяĐöØцяöЪöяцฐ (@NitekryDPaul) — WiFi promiscuous detection research: the 30-OUI Flock Safety target list and the addr1-receiver detection technique that form the entirety of the
promiscuis-flock-youfirmware on this branch. The firmware here is a mod of his original work. - Will Greenberg (@wgreenberg) — BLE manufacturer company ID detection (
0x09C8XUNTONG) sourced from his flock-you fork (used by the BLE companion onmain) - DeFlock (FoggedLens/deflock) — crowdsourced ALPR location data and detection methodologies. Datasets included in
datasets/ - GainSec — Raven BLE service UUID dataset (
raven_configurations.json) used by the BLE companion
OUI-SPY Firmware Ecosystem
Flock-You is part of the OUI-SPY firmware family:
| Firmware | Description | Board |
|---|---|---|
| OUI-SPY Unified | Multi-mode BLE + WiFi detector | ESP32-S3 / ESP32-C5 |
| OUI-SPY Detector | Targeted BLE scanner with OUI filtering | ESP32-S3 |
| OUI-SPY Foxhunter | RSSI-based proximity tracker | ESP32-S3 |
| Flock You | Flock Safety / Raven surveillance detection (this project) | ESP32-S3 |
| Sky-Spy | Drone Remote ID detection | ESP32-S3 / ESP32-C5 |
| Remote-ID-Spoofer | WiFi Remote ID spoofer & simulator with swarm mode | ESP32-S3 |
| OUI-SPY UniPwn | Unitree robot exploitation system | ESP32-S3 |
Author
colonelpanichacks
Oui-Spy devices available at colonelpanic.tech
Disclaimer
Passive reception of publicly-broadcast 802.11 frames for security research, privacy auditing, and education. The device does not transmit and does not authenticate to any network. Detecting the presence of surveillance hardware in public spaces is legal in most jurisdictions; always comply with local laws regarding wireless reception.