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 to 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 to 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 to 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.