Chapter: 4 (Week 5) Duration: 3.5 hr Substrate: nRF52840 USB dongle + Python (bleak) + Wireshark; ANTSDR E200 optional Points: 8
Overview
Enumerate Bluetooth Low Energy devices, capture an authenticated BLE pairing exchange, and decode the GATT attribute hierarchy using the Wireshark BLE dissector and the bleak Python library.
Physical path: nRF52840 dongle for passive BLE capture; bleak for active GATT enumeration.
Virtual path: lab4-ble-pairing.pcapng (pre-captured BLE pairing exchange).
Part 1: BLE Advertising Survey (30 min)
Physical path
Flash the nRF52840 dongle with the Wireshark BLE extcap firmware (see SETUP.md). In Wireshark, open the nRF52840 capture interface (advertising channel 37+38+39).
Run hcitool lescan in parallel:
sudo hcitool lescan --duplicates 2>/dev/null | head -30
Observe and record for 5 minutes:
- Advertising PDU types seen (ADV_IND, ADV_NONCONN_IND, SCAN_RSP, ADV_DIRECT_IND)
- Unique AdvA (advertising addresses) — distinguish public (OUI-based) vs. random (bit 6+7 of MSB byte)
- Advertising data fields: device name, service UUIDs, manufacturer-specific data
Virtual path
Open lab4-ble-pairing.pcapng in Wireshark. Apply display filter btle. Answer the same questions from the capture.
Part 2: GATT Enumeration with bleak (45 min)
Identify a BLE device you have authority to enumerate (your phone with BLE developer mode, a smart bulb, a fitness tracker, or the instructor-provided lab BLE target). Get its BLE address from the scan in Part 1.
import asyncio
from bleak import BleakScanner, BleakClient
# First: scan for BLE devices
async def scan_ble():
print("Scanning for BLE devices (5 seconds)...")
devices = await BleakScanner.discover(timeout=5.0)
for d in sorted(devices, key=lambda x: x.rssi, reverse=True):
print(f" {d.address:20s} RSSI={d.rssi:4d} '{d.name}'")
return devices
# Then: enumerate a specific device
async def enumerate_device(address):
print(f"\nConnecting to {address}...")
async with BleakClient(address, timeout=15.0) as client:
print(f"Connected: {client.is_connected}")
print(f"MTU: {client.mtu_size}")
services = await client.get_services()
for svc in services:
print(f"\n SERVICE: {svc.uuid} ({svc.description})")
for char in svc.characteristics:
print(f" CHAR: {char.uuid} props={char.properties}")
if 'read' in char.properties:
try:
val = await client.read_gatt_char(char.uuid)
print(f" value (hex): {val.hex()}")
# Try UTF-8 decode
try: print(f" value (text): {val.decode('utf-8')}")
except: pass
except Exception as e:
print(f" read error: {e}")
for desc in char.descriptors:
print(f" DESC: {desc.uuid} ({desc.description})")
asyncio.run(scan_ble())
# asyncio.run(enumerate_device("AA:BB:CC:DD:EE:FF")) # replace with target address
Record the complete GATT service/characteristic inventory for your target device.
Part 3: BLE Pairing Capture (60 min)
Perform a BLE pairing (Just Works or Passkey Entry) between two devices you control (phone + laptop, or phone + instructor lab BLE peripheral). Capture the exchange with the nRF52840 sniffer in Wireshark.
The pairing exchange involves these PDU sequences:
1. ADV_IND (advertising) from peripheral
2. CONNECT_IND from central → connection established
3. SMP (Security Manager Protocol) PDUs:
- Security Request (slave → master)
- Pairing Request (master → slave)
- Pairing Response (slave → master)
- Pairing Public Key exchange (if LESC)
- Pairing DHKey Check (if LESC)
- Pairing Random (Passkey)
- Pairing Confirm
- Encryption Start (LL_ENC_REQ / LL_ENC_RSP)
In Wireshark, display filter: btle.data_header.llid (to isolate data-channel PDUs) and btatt (ATT layer, post-pairing GATT traffic).
Questions to answer from the capture:
- What pairing method was used? (Look at the SMP Pairing Request
AuthReqflags: Bonding (bits 0-1), MITM (bit 2), SC (bit 3), Keypress (bit 4)) - What IO Capabilities did each device advertise? (SMP Pairing Request/Response IO Capability field: 0x00=DisplayOnly, 0x01=DisplayYesNo, 0x02=KeyboardOnly, 0x03=NoInputNoOutput, 0x04=KeyboardDisplay)
- Was Secure Connections (LESC / LE Secure Connections) used? (AuthReq SC bit)
- Is the ATT traffic after pairing encrypted? (Look at LL_ENC_RSP; subsequent data PDUs should show encrypted payload — Wireshark shows "Encrypted Packet" if you have not provided the LTK)
Virtual path
Use lab4-ble-pairing.pcapng. Answer the same four questions from the pre-captured exchange.
Part 4: Wireshark BLE Dissection (30 min)
From your capture (or the virtual-path file), identify and annotate:
- One ADV_IND frame: expand the advertising data; identify each AD Type field
- One SMP Pairing Request: expand all fields; note the AuthReq bits
- One ATT Read Request + ATT Read Response: what characteristic handle was read? What was the value?
Screenshot each in Wireshark with the frame tree fully expanded.
Deliverables
- BLE scan output (from bleak or hcitool lescan): list of devices with addresses and names
- GATT enumeration: complete service/characteristic inventory for your chosen device
- Wireshark screenshots: (1) ADV_IND with advertising data expanded, (2) SMP Pairing Request, (3) ATT Read Request+Response
- Written answers to the four pairing-capture questions
- 1-paragraph security assessment: what could an attacker do with the ability to capture this pairing exchange? What pairing method would resist passive eavesdropping?
Grading (8 points)
| Item | Points |
|---|---|
| BLE scan output with ≥3 unique devices | 1 |
| GATT enumeration: complete service/char inventory | 2 |
| Wireshark screenshots (all three) | 2 |
| Four pairing-capture questions answered correctly | 2 |
| Security assessment paragraph | 1 |