Mechanism
Bluetooth Classic (BR/EDR) is a different PHY/MAC from BLE: it occupies the same 2.4 GHz ISM band but uses 79 RF channels of 1 MHz each and hops across them under adaptive frequency hopping (AFH) at roughly 1600 hops per second (bt-baseband-spec). That fast hop is exactly why the spectrum layer for Classic splits into two complementary moves rather than one.
The first move is a spectrum view. A waterfall over 2.402–2.480 GHz shows BR/EDR bursts scattered across the band, confirming that “something Bluetooth is busy here” — but you cannot lock to one channel and follow a piconet packet-by-packet the way you can with a fixed-channel protocol. At 1600 hops/s a static SDR simply cannot keep up, so the waterfall is an activity indicator, not a decode. This is the honest ceiling of SDR at this layer.
The second, and usually more useful, move is an inquiry scan. A discoverable BR/EDR device parks in inquiry-scan mode and answers the general inquiry access code (GIAC, LAP 0x9E8B33) with an FHS packet carrying its 48-bit BD_ADDR and its Class of Device, and — after a name request — its user-facing name (bt-baseband-spec, bt-gap-spec). Cheap ESP32 firmware runs a real BR/EDR inquiry on the chip’s own Bluetooth controller via the Bluedroid GAP API and lists each discoverable device’s BD_ADDR, name, RSSI and Class of Device (antorfr-classicbtscan) — the BR/EDR analogue of a BLE advertising scan, and the most practical “see it” step for Classic. Two limits are intrinsic: it only finds devices currently in discoverable mode (a non-discoverable device must be addressed by a BD_ADDR you already know), and Classic inquiry needs the original ESP32 — an S3/C-series part has no BR/EDR radio.
Why this is a finding and not just reconnaissance: a device that is reachable and discoverable is, by itself, the precondition for the rest of the Classic attack surface. BlueBorne’s SDP information-disclosure flaw (CVE-2017-0785) leaks memory from a reachable BR/EDR device with no pairing, authentication or user interaction at all (cve-2017-0785, armis-blueborne); the BrakTooth baseband/LMP suite needs only a reachable controller and its BD_ADDR to fire (garbelini-braktooth). Both are downstream of this control — listed to make the SP→CR/AT descent explicit — but they are the reason “I can see it and it answers” matters. Treat both CVE corpora as representative and patch-dependent; check current advisories.
Procedure
Authorised testing only — run this against devices you own or are explicitly contracted to assess. An inquiry scan is passive at the application layer but still transmits inquiry packets in a licensed-free band; the SDR waterfall is receive-only.
-
Confirm band activity with the SDR waterfall (receive-only). Bring up Gqrx on a HackRF One or bladeRF 2.0 micro, tune to the centre of the band and watch for hopping bursts:
# HackRF: 2.441 GHz centre, 20 Msps, covers most of the 2.402–2.480 GHz band gqrx # Device string: hackrf=0 (or bladerf=0) # Set frequency 2.441 GHz, sample rate 20 Msps, enable the waterfallYou should see short bursts skipping unpredictably across the span. That is BR/EDR (and BLE/Wi-Fi) activity — it confirms the band is busy, but you cannot follow one piconet here. Do not expect a clean per-packet decode (garbelini-braktooth, bt-baseband-spec).
-
Run a BR/EDR inquiry scan on the original ESP32. Flash ClassicBTScan (or ESP32-BT-exp for a dual-mode Classic+BLE dump) to an original ESP32 DevKit and open the serial monitor at 115200 baud:
# Arduino-ESP32, original ESP32 target (BR/EDR radio required) arduino-cli compile --fqbn esp32:esp32:esp32 ClassicBTScan.ino arduino-cli upload --fqbn esp32:esp32:esp32 -p /dev/ttyUSB0 ClassicBTScan.ino arduino-cli monitor -p /dev/ttyUSB0 -c baudrate=115200The sketch calls the Bluedroid GAP inquiry API and prints one line per discoverable device. Read each field:
- BD_ADDR — 48-bit address; the upper 24 bits are the OUI (vendor lookup).
- Name — the user-facing name from the name request (may be empty).
- RSSI — coarse proximity / link-budget indicator.
- Class of Device (CoD) — the device-type hint (audio, peripheral/HID, phone, etc.).
-
Record the inventory and cross-reference. For each hit, note BD_ADDR, name, RSSI and CoD, and resolve the OUI to a vendor. Devices in this list are reachable and discoverable — flag that as the spectrum-layer finding, and carry the BD_ADDRs forward to the IG fingerprinting and the CR/AT layers.
-
Note what you did not see. A device that is paired-and-connected but non-discoverable will not appear. Absence from the inquiry list is not proof of absence on the air — corroborate with the step-1 waterfall (band busy but no inquiry hits ⇒ likely non-discoverable / already-bonded traffic). See RFSAM-RES-26 for the ESP32 inquiry-scanning walkthrough.
Field case
Assessing a hands-free car kit and its paired peripherals on the bench, the SDR waterfall over 2.402–2.480 GHz showed steady hopping bursts the moment the head unit powered on — band confirmed busy, but, as expected, nothing followable at ~1600 hops/s. The ESP32 inquiry scan then returned a short inventory:
[BR/EDR inquiry]
BD_ADDR=[FILL: 48-bit address, e.g. AA:BB:CC:11:22:33] name="[FILL: device name]" RSSI=-52 CoD=0x240404 (Audio, hands-free)
BD_ADDR=[FILL: 48-bit address] name="[FILL: HID name]" RSSI=-71 CoD=0x002540 (Peripheral, keyboard)
The CoD 0x240404 cleanly identified the hands-free audio unit, and its OUI [FILL: vendor from upper 24 bits] matched the expected infotainment vendor. The keyboard peripheral was discoverable too — an avoidable exposure, since an HID device only needs to be discoverable during pairing. The takeaway recorded at this layer was simply: two reachable, discoverable BR/EDR devices, BD_ADDRs captured, both now in scope for the IG/CR/AT descent. The bracketed [FILL: …] values above are illustrative placeholders, not a real capture — replace them with the actual inquiry output from your authorised engagement rather than treating them as findings.
Remediation
Developer / firmware. Do not leave a device in general-discoverable (inquiry-scan) mode after provisioning. Make discoverability a time-boxed, user-initiated pairing window (limited-discoverable, then back to non-discoverable), so a passing inquiry scan returns nothing. Set the Class of Device honestly but minimally — it is a routing hint, not a place for extra detail. Keep the BR/EDR controller and host stack patched against the known reachable-surface classes (BlueBorne SDP leak and the BrakTooth baseband/LMP family); treat those CVE lists as representative and re-check current vendor advisories (cve-2017-0785, garbelini-braktooth).
Integrator. Prefer modules that ship non-discoverable by default and expose discoverability only behind an explicit user action. Where a generic device name leaks model/vendor, override it. These link-and-above concerns are owned by BSAM’s Bluetooth controls — RFSAM stops at confirming reachability and capturing the inventory, and defers the controller lifecycle/patch assessment to BSAM (BSAM-IG-01).
Operator. Power devices down or disable Bluetooth when not in use; pair in a trusted location and within the shortest possible discoverable window. Periodically run an inquiry scan of your own estate to find devices that are needlessly discoverable. A device that never answers an inquiry scan denies an attacker the cheapest reconnaissance step in the entire Classic descent.