Знаешь, что меня бесит больше всего в современной индустрии информационной безопасности? Даже не то, что уязвимости не закрывают годами, и не то, что производители чипов кладут болт на обратную связь от исследователей. Бесит, как это всё подаётся. Открываешь утром ленту - а там сплошные заголовки: "Эксперты обнаружили критическую уязвимость в Bluetooth-чипах, угрожающую миллиардам устройств! Срочно установите обновление!". Переходишь по ссылке - и видишь пересказанный бюллетень безопасности, где из технических деталей только номер CVE и абстрактное "переполнение буфера может привести к выполнению кода". Ноль конкретики. Ноль инструментов. Ноль понимания, как это работает на самом деле. Просто очередная волна хайпа, чтобы антивирусные компании продали лишнюю подписку, а корпоративные блоги отчитались перед начальством о "продвижении экспертизы".
А на Хабре? Там та же песня, только с другим акцентом. "Умный замок открывается по Bluetooth: безопасно ли это?" - и пять картинок с рекламных буклетов, пара общих фраз про шифрование и вывод "нужно быть осторожным". Спасибо, кэп. Ты правда думаешь, что после такого текста кто-то станет безопаснее? Или это просто способ набрать просмотры и сделать вид, что тема раскрыта?
Или взять эти бесконечные отчёты "Лаборатории Касперского" и им подобных. Красивые графики, диаграммы, проценты роста атак. Ноль технических деталей. Потому что детали - это их "интеллектуальная собственность", понимаешь? Они продают отчёты корпорациям, а не делятся знаниями с сообществом. И в результате мы получаем мир, где уязвимости живут годами, потому что информация о них либо засекречена, либо размазана тонким слоем по маркетинговым буклетам.
Я не хочу так. И ты, раз уж читаешь эти строки, скорее всего, тоже устал от этой поверхностной жвачки. Ты здесь не для того, чтобы узнать, что "Bluetooth может быть опасен". Ты здесь потому, что хочешь понять: как именно, почему и что с этим можно сделать. Ты хочешь не просто потребить информацию, а вооружиться инструментами. Я тебя услышал.
Если ты не знаешь, что такое HCI, LMP, L2CAP или ARM Thumb - это не проблема, мы по ходу разберём. Но я не буду тратить время на объяснение того, что такое операционная система или зачем нужен Bluetooth-адаптер. Предполагается, что ты уже прошёл базовый курс молодого бойца и теперь хочешь углубиться в траншеи.
Тема сегодняшнего разговора - Bluetooth. Но не тот Bluetooth, про который пишут в инструкциях к колонкам или в характеристиках фитнес-браслетов. Мы полезем в самое сердце - в прошивки чипов Broadcom и Cypress. Почему именно они? Да потому что эти два монстра (теперь уже, по сути, одна корпорация, поглотившая беспроводное подразделение Broadcom) сидят буквально везде.
Достань свой ноутбук. MacBook? Внутри Broadcom. Любой Windows-ноутбук от приличного бренда - Dell, HP, Lenovo? С вероятностью 90% там комбо-чип Broadcom или Cypress на Wi-Fi и Bluetooth. iPhone? Да, даже в последних моделях с их собственными модемами, Bluetooth-часть всё равно от Broadcom. Raspberry Pi - любая модель со встроенным Bluetooth (начиная с Raspberry Pi 3) использует чип Cypress CYW43455. Автомобильные информационно-развлекательные системы? Tesla, BMW, Mercedes - сплошь Broadcom. Медицинские импланты, промышленные контроллеры, смарт-телевизоры, приставки, игровые консоли - если устройство умеет в Bluetooth и не откровенный ширпотреб с чипом от китайского ноунейма (вроде JL или Realtek), скорее всего, там Broadcom или Cypress.
Это стандарт де-факто. И это стандарт с огромным количеством тёмных углов. Почему? Потому что эти чипы - не просто радиомодули. Это полноценные компьютеры на кристалле (SoC) с собственным ARM-ядром, собственной памятью, собственной операционной системой реального времени (RTOS). Они загружают прошивку с основного процессора при старте. Они имеют прямой доступ к памяти хоста (DMA) через шину (SDIO, USB, UART). Они обрабатывают сложнейшие стеки протоколов, которые писались инженерами двадцать лет назад и с тех пор только обрастали новыми функциями, но редко подвергались рефакторингу.
Ирония в том, что при всём этом, прошивки этих чипов - это практически "чёрный ящик". Производители устройств (Apple, Dell, Lenovo) просто кладут в свою прошивку BIOS бинарный файл .hcd или .bin, полученный от Broadcom. Они его не анализируют, не проверяют на безопасность, не обновляют, если Broadcom выпускает патч, но не шумит об этом. А Broadcom, в свою очередь, не особо заинтересован в публичном раскрытии деталей своей архитектуры. В результате мы имеем миллиарды устройств, внутри которых крутится код, написанный с ошибками десятилетней давности, и никто об этом не знает, пока не приходит какой-нибудь Энди Дэвис или команда Google Project Zero и не показывает, что всё это время наши ноутбуки можно было взломать просто пройдя мимо с Bluetooth-донглом.
Мы разберём несколько реальных эксплойтов:
- BlueBorne (CVE-2017-0785) - как переполнение буфера в SDP-сервере Broadcom позволяло захватить чип, а через него - и весь телефон, даже без сопряжения.
- Баги в LMP - как неправильная обработка пакетов управления линком может вызвать отказ в обслуживании на уровне прошивки, после которого устройство не перезагружается без снятия питания.
- Проблемы AFH (Adaptive Frequency Hopping) - как функция, призванная улучшить стабильность работы в шумном эфире, становится вектором атаки из-за выхода за границы массива при парсинге карты каналов.
- Уязвимости фрагментации L2CAP - как отправка перекрывающихся фрагментов пакета может повредить кучу в прошивке и привести к выполнению кода.
- CVE-2023-45866 и подобные находки - атаки на протокол паринга, где чипы Broadcom просто не проверяют подписи, позволяя злоумышленнику притвориться клавиатурой и подключиться к устройству без подтверждения.
Мы поговорим про:
- InternalBlue - фреймворк на Python, который использует вендор-специфичные HCI-команды Broadcom для чтения и записи памяти чипа, хукинга функций и активации отладочных логов. Это, пожалуй, лучшее, что случилось с реверсингом Bluetooth-прошивок за последние годы. Я покажу, как его установить, как подключиться к чипу, как дампить память и как ставить брейкпоинты прямо в рантайме.
- Ghidra и IDA - как загружать дампы прошивки, находить функции по сигнатурам, анализировать графы вызовов и искать уязвимые места вроде unsafe memcpy.
- Ubertooth One - железный сниффер, способный синхронизироваться с пикосетью и перехватывать Bluetooth-трафик "из воздуха". Без него анализ протоколов на низком уровне - как игра в шахматы вслепую.
- QEMU и Unicorn - как эмулировать прошивку Broadcom на хосте и фаззить её обработчики, не имея реального железа под рукой (или чтобы не спалить его).
- Самописные скрипты на Python и C - для фаззинга HCI-команд, для автоматизации поиска багов, для модификации прошивки на лету.
Мы пройдём по следующим этапам:
- Анатомия чипа и прошивки. Что внутри BCM43xx? Как устроена память, как работает загрузчик, как хост загружает прошивку. Форматы .hcd и .bin. Почему вендоры не обновляют прошивки годами и как это влияет на безопасность.
- HCI - интерфейс между хостом и чипом. Стандартные команды и события. Вендор-специфичные команды (VSC). Как использовать VSC для отладки и модификации поведения чипа.
- Реверсинг прошивки. Загрузка дампа в Ghidra. Поиск main, обработчиков событий, таблиц векторов прерываний. Идентификация функций по паттернам. Работа с ARM и Thumb-инструкциями.
- Анализ уязвимостей на примерах. Детальный разбор BlueBorne: что такое SDP, где в прошивке живёт его обработчик, как происходило переполнение. Разбор LMP-багов: структура пакетов, уязвимые функции. Разбор AFH-атак: как устроена карта каналов и где в коде происходит её парсинг.
- Инструментарий в действии. InternalBlue: подключение, чтение памяти, запись памяти, хукинг, поиск паттернов. Ubertooth: захват трафика, синхронизация, анализ L2CAP-фрагментации. Ghidra: скрипты для парсинга прошивок Broadcom.
- Создание собственных эксплойтов. Как найти баг с помощью фаззинга. Как написать патч прошивки на лету для включения отладочного вывода. Как использовать найденную уязвимость для выполнения кода на чипе. Как из захваченного чипа пробиться на хост через DMA.
- Современные уязвимости и тренды. Что находят сейчас. Почему баги в GATT и атрибутных протоколах становятся новым фронтом. Как работает атака на Bluetooth-клавиатуры без подтверждения.
- Защита и обнаружение. Как понять, что твой чип атакуют. Можно ли вообще защититься от атак на прошивку, если ты обычный пользователь. Что делают производители (и чего не делают) для предотвращения этих атак.
Глава 1: Анатомия чипа и прошивки. Смотрим под микроскопом до атомов
Знаешь, в чём прикол? Большинство так называемых "исследователей безопасности" останавливаются на уровне HCI-команд. Мол, есть интерфейс между хостом и чипом, мы можем посылать команды, видеть события - и этого достаточно, чтобы делать какие-то выводы. Это примерно как изучать анатомию человека, только ощупывая его через скафандр. Да, ты можешь понять, где рука, где нога, но что там внутри - загадка.
Мы пойдём дальше. Мы снимем скафандр. Вскроем грудную клетку. И посмотрим, как бьётся это маленькое ARM-сердце.
1.1 Что вообще внутри этого маленького чипа?
Чипы Broadcom серии BCM43xx (и их клоны Cypress CYW43xxx) - это не просто радиомодули. Это System-on-Chip (SoC) средней сложности. Типичный представитель - BCM4345, который стоит в Raspberry Pi 3B+ и куче ноутбуков. Давай откроем его даташит (который мы, конечно, раздобыли в тёмном переулке интернета) и посмотрим на блок-схему.Внутри:
- CPU: 32-битное ARM ядро. В старых чипах (BCM20702) это был ARM7TDMI, в новых - Cortex-M3 или M4. Тактовая частота - от 48 до 160 МГц в зависимости от модели. Этого достаточно, чтобы крутить стек Bluetooth и Wi-Fi одновременно, не напрягаясь. На этом ядре работает прошивка - тот самый код, который мы будем реверсить.
- Память:
- ROM (Read-Only Memory): Небольшой объём (десятки килобайт), зашитый при производстве. Там лежит самый-самый низкоуровневый загрузчик (bootrom) и, возможно, минимальный набор драйверов для инициализации железа. Это единственное, что нельзя перепрошить (технически можно, но это требует лазерной коррекции, так что считаем неизменным).
- SRAM (или просто RAM): Основная память для кода и данных. Объём - от 512 КБ до нескольких мегабайт в зависимости от чипа. В неё загружается прошивка с хоста при старте. Туда же пишутся стек и куча во время работы.
- TCM (Tightly Coupled Memory): В некоторых моделях есть быстрая память, привязанная напрямую к ядру для критичных по времени функций (например, обработка ACK-пакетов).
- Радиочастотный тракт (RF): Аналоговая часть, которая принимает и передаёт сигнал на 2.4 ГГц. Для нас это чёрный ящик, но косвенно влияет на безопасность через калибровочные данные (NVRAM).
- Контроллеры:
- LMAC (Lower MAC): Реализован аппаратно или на очень маленьком микроконтроллере. Отвечает за вещи, требующие жёстких таймингов: отправка ACK, отслеживание слотов, подсчёт CRC. С программной точки зрения мы с ним взаимодействуем через регистры.
- UMAC (Upper MAC): Это уже программная часть, исполняемая на основном ARM-ядре. Там живут протоколы: LMP, L2CAP, SDP, ATT, GATT. Вот где настоящий простор для багов.
- Интерфейсы с хостом:
- UART: Самый простой и медленный. Используется в старых Bluetooth-адаптерах и для отладки.
- USB: В USB-свистках и некоторых встроенных решениях.
- SDIO: Самый популярный интерфейс для комбо-чипов (Wi-Fi + Bluetooth) в ноутбуках и одноплатниках. SDIO даёт высокую скорость и, что критично, поддерживает DMA (Direct Memory Access). Чип может читать и писать в память хоста без участия его CPU.
- Периферия:
- Таймеры, прерывания, GPIO, I2C, SPI - для взаимодействия с антенными переключателями, усилителями и прочим обвесом.
Код:
0x00000000 - 0x0000FFFF : ROM (64 KB) - загрузчик и векторы
0x10000000 - 0x1003FFFF : SRAM (256 KB) - код и данные прошивки
0x18000000 - 0x18003FFF : Shared memory с Wi-Fi (если есть)
0x20000000 - 0x20007FFF : TCM (32 KB) - быстрая память для критических функций
Это приблизительно, адреса могут плавать от модели к модели, но принцип общий.
1.2 Процесс загрузки: как чип получает душу
Теперь самое интересное - как эта железка оживает. У Bluetooth-чипов Broadcom есть одна архитектурная особенность, которая делает их одновременно гибкими и уязвимыми: прошивка загружается хостом.Процесс выглядит так:
- Подаётся питание. ARM-ядро внутри чипа просыпается и начинает исполнять код из ROM. Это bootrom.
- Bootrom инициализирует минимально необходимую периферию: тактовый генератор, интерфейс с хостом (UART/USB/SDIO), небольшую часть RAM.
- Чип переходит в режим ожидания и начинает "слушать" хост. Он ждёт специальной последовательности (например, определённого HCI-пакета), которая означает: "эй, я хочу загрузить в тебя прошивку".
- Хост (драйвер в ядре) отправляет эту команду, а затем начинает пересылать бинарный файл прошивки кусками. Каждый кусок содержит адрес в памяти чипа, по которому его нужно записать. Чип пишет данные в свою RAM.
- После передачи всех кусков хост отправляет команду "запуск". Чип делает прыжок на точку входа (обычно это адрес, где лежит функция main прошивки) и начинает выполнять код уже из RAM.
Это не баг, это фича дизайна. Так было сделано для упрощения производства: чипы могут быть одинаковыми для разных рынков, а прошивка заливается под конкретное применение. Но с точки зрения безопасности это огромная дыра.
Практический инструмент: Чтобы увидеть этот процесс в действии, можно включить логирование HCI-трафика в Linux. Для этого используем btmon (из пакета bluez) или hcidump. Запусти:
Bash:
sudo btmon &
sudo hciconfig hci0 down
sudo hciconfig hci0 up
В выводе btmon ты увидишь, как драйвер шлёт Vendor Specific Commands, которые записывают данные в память чипа. Это и есть загрузка прошивки. Если порыться в исходниках ядра Linux (драйвер brcmfmac для Wi-Fi и hci_bcm для Bluetooth), можно найти, откуда берётся файл прошивки и как он передаётся.
1.3 Форматы прошивок: .hcd, .bin, .trx - что внутри?
В зависимости от модели чипа и типа интерфейса, прошивки поставляются в разных форматах. Самые распространённые:- .hcd (Host Controller Driver) - исторический формат для Bluetooth-only чипов через UART или USB. Название обманчиво: это не драйвер, а именно прошивка. Файл .hcd - это просто дамп памяти с простым заголовком.
- .bin - чаще всего для комбо-чипов (Wi-Fi + Bluetooth) через SDIO. Может быть в формате TRX (старый формат прошивок Broadcom для роутеров) или просто бинарник без заголовка, который загружается по фиксированным адресам.
- .clm_blob - файл с калибровочными данными для работы с радиочастотным каналом (Country Locale Matrix). Там лежат таблицы разрешённых частот, мощности передатчика и т.п.
Первые несколько байт часто содержат магическое число. Для Broadcom это может быть 0x4243 (ASCII "BC") или 0x4243 0x4d48 ("BCMH" - Broadcom Corporation Mini-Host?). После магического числа идёт длина блока, адрес загрузки и данные.
Давай посмотрим на реальный пример (вывод hexdump -C BCM4345C0.hcd | head -n 10):
Код:
00000000 42 43 4d 48 00 00 00 01 00 00 00 00 00 00 00 00 |BCMH............|
00000010 00 10 00 00 80 01 00 00 00 00 00 00 00 00 00 00 |................|
00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000060 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000070 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000080 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000090 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
Видишь? Заголовок "BCMH", потом какие-то поля. В старых прошивках формат был проще: 4 байта magic, 4 байта длина, 4 байта адрес загрузки, потом данные. Здесь, судя по всему, более сложная структура, возможно с таблицей разделов.
Но есть и другой способ: некоторые прошивки .hcd - это просто конкатенация HCI-команд, которые отправляются чипу для загрузки. Каждая команда имеет свой код операции (opcode) и параметры. Это называется "HCD over HCI". В этом случае файл можно разобрать, зная структуру HCI-пакета. Типичный пакет HCI Command: 2 байта opcode, 1 байт длина параметров, потом параметры. Для загрузки прошивки используются вендор-специфичные opcode, например, 0xFC4E (Download Mini-driver) или 0xFC4F (Write RAM).
Инструмент для разбора: Напишем простой Python-скрипт, который будет читать .hcd файл и интерпретировать его как поток HCI-команд. Это поможет понять, что именно шлёт хост чипу.
Python:
import struct
import sys
def parse_hcd(filename):
with open(filename, 'rb') as f:
data = f.read()
pos = 0
while pos < len(data):
# HCI Command packet: 2 bytes opcode, 1 byte param len
if pos + 3 > len(data):
break
opcode, param_len = struct.unpack('<HB', data[pos:pos+3])
pos += 3
if pos + param_len > len(data):
print("Truncated packet")
break
params = data[pos:pos+param_len]
pos += param_len
print(f"Opcode: 0x{opcode:04X}, length: {param_len}, params: {params.hex()}")
# Интересные opcode для Broadcom: 0xFC4E, 0xFC4F, 0xFC4C и т.д.
if __name__ == "__main__":
parse_hcd(sys.argv[1])
1.4 NVRAM: калибровки и региональные настройки
Помимо кода, в чип загружаются калибровочные данные, которые хранятся в отдельном файле - часто это .nvram или .clm_blob. Для Raspberry Pi, например, в папке /lib/firmware/brcm/ лежит файл brcmfmac43455-sdio.txt. Это текстовый файл с параметрами:
Код:
NVRAMRev=$Rev: 521337 $
sromrev=11
vendid=0x14e4
devid=0x43ab
boardtype=0x8872
boardrev=0x1100
boardnum=22
macaddr=00:11:22:33:44:55
...
Здесь задаются идентификаторы, MAC-адрес, параметры антенн, усиление, частотные планировки для разных стран. Эти данные имеют критическое значение для работы радио. Если их испортить, чип может не включить передатчик или работать на недопустимых частотах. С точки зрения безопасности, модификация NVRAM может привести к нестабильной работе или даже к выходу за допустимые пределы мощности (что интересно для атак типа "глушение" или для обхода регуляторных ограничений).
1.5 Реверсинг прошивки: от бинарника к ассемблеру
Теперь, когда у нас есть дамп прошивки (из .hcd или .bin), мы хотим понять, что там внутри. Для этого нам понадобится дизассемблер. Я использую Ghidra (от АНБ, бесплатно и круто) или IDA Pro (если есть деньги). Ghidra отлично подходит для ARM.Первый шаг - определить, какой именно ARM используется. Cortex-M3 использует набор инструкций Thumb-2, но точка входа может быть в режиме ARM или Thumb. Обычно прошивки Broadcom для Bluetooth - чистый Thumb, потому что он более плотный и энергоэффективный.
Определяем базовый адрес. В Ghidra при загрузке бинарника нужно указать базовый адрес, по которому прошивка будет расположена в памяти. Откуда его взять? Из .hcd файла мы видели адреса загрузки. Например, если команды Write RAM писали данные по адресу 0x100000, значит, код будет исполняться оттуда. В некоторых прошивках есть таблица векторов прерываний в начале. Для Cortex-M векторная таблица содержит 32-битные адреса обработчиков, причём первый элемент (по адресу базы + 0) - это начальное значение stack pointer, второй (база + 4) - адрес сброса (Reset_Handler). Младший бит адреса должен быть 1 для Thumb-кода.
Если мы видим в начале файла что-то похожее на таблицу векторов - это хороший знак. Например, если первые 4 байта 0x20001000 - это может быть значение SP (обычно указывает на верхнюю границу RAM), а следующие 4 байта 0x10000101 - адрес сброса (база + 0x100, младший бит 1 - значит Thumb).
После загрузки в Ghidra с правильным базовым адресом мы можем проанализировать код. Ghidra автоматически найдёт функции, перекрёстные ссылки, строки. Строки - это кладезь информации. В прошивках Broadcom часто встречаются отладочные сообщения вроде:
Код:
[HCI] Command received: opcode %04x
[LMP] Sent packet on handle %d
[GATT] Attribute read from handle 0x%04x
Assertion failed: %s line %d
По этим строкам можно понять, какие функции за что отвечают, и найти потенциально опасные места (например, sprintf без проверки длины).
Инструмент: поиск строк. Используем команду strings на прошивке:
Bash:
strings -n 8 BCM4345C0.hcd | less
Пример вывода:
Код:
4345c0-roml/sym/bt/./os/../hci/hci_main.c
HCI: reset
HCI: set_event_mask
hci_send_command
lmp_sniff_subrating
bcm_lmp_acp_sniff_req
assert: %s line %d
malloc failed
Здесь мы видим путь к исходнику hci_main.c (утечка путей компиляции - мечта реверсера), названия функций и даже assert. Это золото.
1.6 Вендор-специфичные команды (VSC) - чёрный ход для исследователя
Broadcom, как и любой производитель, добавляет свои собственные HCI-команды для отладки, конфигурирования и обновления прошивки. Многие из них документированы только в NDA-шных документах, но некоторые были открыты через утечки или обратный инжиниринг.В InternalBlue есть целая база таких команд для разных чипов. Например:
- 0xFC4C - Read RAM
- 0xFC4D - Write RAM
- 0xFC4E - Download Mini-driver
- 0xFC4F - Launch RAM
- 0xFC4B - Change baudrate UART
Пример: подключимся к чипу через InternalBlue и прочитаем несколько байт по адресу, где лежит строка с версией.
Python:
from internalblue import InternalBlue
device = InternalBlue()
device.interface = 'hci0' # наш Bluetooth адаптер
device.connect()
# Читаем 64 байта по адресу 0x100200 (пример)
data = device.readMemory(0x100200, 64)
print(data.hex())
Если адрес угадан правильно, мы увидим знакомые строки из дампа.
1.7 Модификация прошивки на лету: первый шаг к эксплуатации
Теперь самое интересное. Если мы можем писать в RAM чипа, мы можем изменять его поведение прямо во время работы. Например, мы хотим включить отладочный вывод, который производитель отключил в релизной прошивке. Для этого нужно найти функцию логирования (часто это пустышка, которая ничего не делает) и переписать её, чтобы она выводила данные через какой-нибудь скрытый канал (например, записывала в зарезервированную область памяти, которую мы потом прочитаем через Read RAM).InternalBlue умеет ставить хуки - он заменяет первые инструкции функции на переход в наш код, который мы предварительно записали в свободную область RAM. Это позволяет отслеживать вызовы функций, смотреть аргументы и даже модифицировать их.
Пример хука на функцию обработки HCI-команд:
Код:
# Код нашего обработчика (в виде массива байт ARM Thumb)
hook_code = bytes([
0x70, 0xb5, # push {r4-r6, lr}
0x08, 0x4c, # ldr r4, [pc, #32] ; загружаем адрес логгера
0x20, 0x47, # blx r4 ; вызываем логгер
0x70, 0xbd, # pop {r4-r6, pc}
# ... тут адрес логгера и т.д.
])
device.writeMemory(hook_addr, hook_code)
device.hook_function(target_func, hook_addr)
Теперь при каждом вызове target_func будет выполняться наш код, который может, например, записывать аргументы в специальный буфер. Потом мы читаем этот буфер через Read RAM.
Это мощнейший инструмент для отладки и поиска уязвимостей. Мы можем залогировать все входящие HCI-пакеты, все LMP-PDU, все обращения к куче - и найти странное поведение, которое не видно на уровне хоста.
1.8 Пример: разбор прошивки BCM4345C0.hcd с помощью Ghidra
Давай проведём небольшой практический сеанс. Я возьму прошивку от Raspberry Pi 3B+ (файл BCM4345C0.hcd из репозитория firmware-nonfree).- Смотрим strings BCM4345C0.hcd | head -20. Видим:
Код:4345c0-roml/sym/bt/./os/../hci/hci_main.cHCI: reset HCI: set_event_mask HCI: read_buffer_size ...
Путь указывает на то, что прошивка собиралась в сборочной среде с иерархией sym/bt/.... Это может помочь восстановить структуру исходников. - Ищем упоминания версии: strings BCM4345C0.hcd | grep -i version. Вывод:
Код:Version: 1.154FW Version 1.154
Не густо, но хоть что-то. - Загружаем файл в Ghidra. Выбираем тип процессора ARM, little endian, вариант Thumb. Указываем базовый адрес. Откуда его взять? В данном случае мы не знаем точного адреса загрузки, но можем предположить, что он 0x0 или 0x100000. В Ghidra можно попробовать разные варианты и посмотреть, появятся ли осмысленные инструкции.
Лучше определить базовый адрес по таблице векторов. Ищем первые 4 байта. В моём файле первые 4 байта: 42 43 4d 48 (BCMH) - это не таблица векторов, это заголовок. Значит, надо пропустить заголовок. После заголовка обычно идут данные. В некоторых прошивках есть фиксированное смещение до векторов. Методом проб находим место, где начинаются векторы. Часто это паттерн: сначала 32-битные значения, указывающие на адреса в памяти. Например, значение 0x20002000 (указатель на стек) и следом 0x10000101 (адрес сброса + 1). Если мы видим такие числа, скорее всего, мы нашли начало векторов.
Допустим, нашли смещение 0x80 от начала файла. Тогда базовый адрес для Ghidra нужно выставить так, чтобы это смещение соответствовало адресу 0x0. То есть base = -0x80. Тогда вектор по адресу 0x4 (в Ghidra) будет соответствовать смещению 0x84 в файле. Если там лежит число с битом 1, значит, мы на верном пути.
- После анализа Ghidra покажет функции. Ищем функцию с именем HCI_Command_Handler или что-то похожее. По строкам находим функцию, которая выводит сообщение "HCI: command received". Анализируем её код. Смотрим, как она разбирает opcode, как вызывает другие функции. Ищем вызовы memcpy, sprintf - типичные источники переполнений.
- Обращаем внимание на функции работы с кучей. В прошивках Broadcom используется простая реализация malloc/free (часто на основе dlmalloc). Если найти функции выделения памяти, можно отследить, где копируются данные без проверки длины.
1.9 Почему производители не патчат прошивки?
Broadcom выпускает обновления прошивок для своих чипов. Они исправляют найденные уязвимости. Но проблема в цепочке поставки.Broadcom отдаёт бинарные прошивки своим клиентам - производителям ноутбуков, смартфонов, модулей. Эти клиенты должны включить обновлённые файлы в свои прошивки BIOS, в образы ОС, в драйверы. Но они этого часто не делают, потому что:
- Их цикл разработки уже закрыт, сертификация пройдена, перевыпуск образа дорог.
- Они не считают Bluetooth-уязвимости критичными (по сравнению с багами в ядре или веб-браузере).
- Они просто забывают или теряют файлы.
Глава 2: Исторические баги и методы охоты (CVE и не только)
Мы стоим на плечах гигантов. Без исследований Google Project Zero, без работы Энди Дэвиса (Andy Davis) и без утечек исходников из Broadcom (да, такое было) мы бы тут не собрались.2.1 Переход в разбор: CVE-2017-0785 (BlueBorne)
Это классика, с которой началась паника. Тогда оказалось, что можно удаленно выполнить код на телефоне Android через уязвимость в SDP сервисе (Service Discovery Protocol), даже если телефон не в паре.В чем была соль? Broadcom реализовал SDP сервер прямо в прошивке чипа. При получении определенного пакета происходило переполнение буфера. И код исполнялся на самом Bluetooth чипе, а не на хосте. Почему это страшно? Потому что чип имеет прямой доступ к оперативной памяти хоста через DMA (в случае SDIO) или shared memory. То есть, захватив чип, можно читать и писать в память ядра. Привет, тотальный компромисс.
Практический инструмент:
Для реверса таких вещей используется эмуляция. Берем прошивку BCM, грузим её в эмулятор (например, Unicorn Engine или QEMU в режиме ARM) и фаззим SDP хэндлеры. В 2017 году никто особо не фаззил Bluetooth-чипы. Сейчас это мейнстрим.
2.2 Не только BlueBorne: Уязвимости в обработке LMP и LLCP
После BlueBorne все бросились копать LMP (Link Management Protocol). Это такой низкоуровневый протокол, которым чипы общаются друг с другом до установки соединения.Одна из моих любимых уязвимостей - та, что позволяла вызывать отказ в обслуживании (DoS) на уровне прошивки. Отправляешь специально сформированный LMP PDU с неправильной длиной, и чип уходит в бесконечный цикл или падает. Банально? Да. Эффективно? Еще как. Некоторые промышленные контроллеры с BCM чипами после такой атаки не перезагружались без снятия питания.
2.3 Баги в настройках AFH (Adaptive Frequency Hopping)
Это вообще тема для хакерского фольклора. AFH нужен для того, чтобы Bluetooth избегал шумных частот (например, если рядом работает Wi-Fi). Чипы обмениваются картой каналов. Broadcom реализовали парсинг этой карты с ошибкой. Можно было прислать карту, которая вызовет выход за границы массива в прошивке. Ирония в том, что это функция, призванная улучшить стабильность работы, становилась вектором атаки. Ты пытаешься помочь чипу избежать помех, а вместо этого ломаешь его стек.Глава 3: Broadcom vs Cypress: Слияние и слияние кода
Тут важный момент для понимания современного ландшафта. Cypress (раньше был известен своими PSoC и чипами для IoT) купил беспроводной бизнес у Broadcom в 2016 году. Потом сам Cypress был куплен Infineon. Но старые Broadcom чипы (BCM) и новые Cypress (CYW) - это часто одно и то же ядро с разной маркировкой. CYW43455 - это брат-близнец BCM43455.Что это значит для нас? Кодовая база у них общая. Баги, найденные в Broadcom, с высокой вероятностью живут и в чипах Cypress, которые сейчас пихают во все от Tesla до детских игрушек. Вендоры не любят портить совместимость, поэтому старые костыли и ошибки мигрируют из поколения в поколение.
Глава 4: Инструментарий хакера: Работаем с железом
Мы не можем просто взять и воткнуть отладчик в чип Bluetooth на своем ноутбуке (хотя JTAG контакты иногда можно найти на плате, если снять экран). Но есть обходные пути. Самый прямой - работать через HCI.4.1 InternalBlue: Фреймворк, который изменил игру
Это, пожалуй, лучший подарок хакерскому сообществу от исследователей из SEEMOO (Secure Mobile Networking Lab). InternalBlue - это фреймворк на Python, который позволяет взаимодействовать с чипом Broadcom/Cypress на низком уровне через HCI. Он использует так называемые VSC (Vendor Specific Commands).Каждый вендор добавляет свои команды для отладки, чтения памяти, патчинга прошивки на лету. Broadcom не исключение. InternalBlue использует эти команды, чтобы:
- Читать память чипа: можно дампить куски RAM и смотреть, что там лежит.
- Писать в память чипа: а вот это уже хак. Можно прямо во время работы чипа переписать кусок кода.
- Перехватывать HCI трафик: смотреть, какие команды хост шлет чипу.
- Активировать отладочные логи: многие чипы Broadcom имеют встроенные отладочные выводы (printf), которые просто отключены в продакшн-прошивке. InternalBlue позволяет их включить.
Нужен Linux (или виртуалка с пробросом USB Bluetooth-адаптера). Подойдут многие адаптеры на чипах Broadcom, например, некоторые встройки в ноутбуках или USB-свистки на BCM20702.
Bash:
git clone https://github.com/seemoo-lab/internalblue.git
cd internalblue
pip3 install -r requirements.txt
sudo python3 setup.py install
После этого подключаешься к своему адаптеру и начинаешь копаться.
Солидарность с читателем: InternalBlue - это пример того, как исследователи не стали копить знания в стол, а выложили инструмент, с которым любой может повторить их результаты. Это и есть хакерская культура в действии.
4.2 Использование InternalBlue для поиска багов
Допустим, мы подозреваем, что функция обработки Extended Inquiry Response (EIR) в чипе имеет баг. Мы можем использовать InternalBlue, чтобы захукать (hook) функцию обработки EIR в RAM чипа. Для этого нужно найти её адрес в прошивке.- Шаг 1: Сдампить прошивку через InternalBlue (дамп RAM).
- Шаг 2: Загрузить дамп в Ghidra или IDA.
- Шаг 3: Найти функцию (часто по сигнатурам или по константам).
- Шаг 4: Через InternalBlue поставить брейкпоинт (переписав первый байт инструкции на 0xBE - бесконечный цикл или вызвав исключение, зависит от архитектуры).
- Шаг 5: Заставить устройство сканировать устройства и отправить EIR-пакет.
- Шаг 6: Если чип завис - попали. Дальше реверсим контекст.
4.3 Wireshark и Ubertooth
Для пассивного анализа нельзя забывать про снифферы. Но Bluetooth (особенно BLE) прыгает по частотам. Обычный адаптер не сможет поймать весь трафик. Для этого есть Ubertooth One - железка, которая может синхронизироваться с пикостью и слушать разговор. Это незаменимая вещь, когда нужно понять, что именно два устройства шлют друг другу, без необходимости сидеть на одном из них.Глава 5: Глубокая заморозка: Анализ уязвимости в наше время
Не думай, что всё закрыто. Недавние исследования (например, от NCC Group) показывают, что в стеках Bluetooth, включая Broadcom, до сих пор находят баги в обработке атрибутов GATT.Один из свежих векторов - фрагментация пакетов в L2CAP. L2CAP может фрагментировать большие пакеты. Реализация фрагментации в некоторых версиях прошивок Broadcom содержала race condition. Если отправить фрагменты в неправильном порядке или с перекрывающимися границами, можно вызвать повреждение кучи в прошивке. А повреждение кучи в маленьком RTOS с отсутствием защиты памяти (MPU часто настроена неправильно или отключена) ведет к выполнению кода.
Практика:
Чтобы протестировать это, используется фаззер, который генерирует искаженные L2CAP фрагменты. Фаззер работает в паре с InternalBlue, который мониторит состояние чипа (отвечает ли он на следующие команды, не упал ли).
Глава 6: Иммунитет и защита
Обычному пользователю не спастись. Обновляйся, если вендор выпускает обновления прошивок. На Linux ставь bluez последней версии (там часто есть патчи, которые обходят баги чипов на уровне драйвера, не давая чипу обрабатывать опасные пакеты).Для нас, исследователей и просто интересующихся, главная защита - это знание. Знание того, как работает твой враг (в данном случае - чип). Мы не можем полагаться на "security by obscurity". Прошивки Broadcom/Cypress лежат в открытом доступе (в репозиториях Linux), их никто не шифрует. Значит, разработчики либо надеются на сложность протокола, либо им плевать.
Если ты нашел баг, сообщи о нем ответственно. Но если тебе не отвечают полгода (а с Broadcom так часто бывает), то имеешь ли ты право опубликовать эксплойт? Вопрос сложный. По законам жанра, если производитель игнорирует проблему, он теряет право на тишину. Но решение всегда за тобой.
Глава 7: Пишем свой инструмент для патчинга прошивки на лету
Вдохновившись InternalBlue, можно пойти дальше. Что, если мы хотим не просто читать память, а постоянно подгружать свой код в чип? Например, мы нашли баг в функции шифрования AES в BLE и хотим его исправить (или, наоборот, эксплуатировать, подменяя ключи).Для этого нужно понять, как загружается прошивка. Процесс загрузки (Download Mini-driver) - это специальная фаза, когда хост может загрузить небольшой патч в RAM чипа до старта основной прошивки. В HCI есть команда для загрузки RAM (Write_RAM). Используя её, мы можем вшить свой код прямо в образ прошивки перед стартом.
Пример идеи:
Мы хотим, чтобы чип логировал все ключи шифрования, которые он генерирует.
- Находим функцию генерации ключа (например, hci_le_ltk_request_reply).
- С помощью дизассемблера смотрим её пролог.
- Пишем свой код на ARM Thumb, который сохраняет ключ в какую-нибудь свободную область RAM, а потом передает управление обратно.
- Через HCI команду пишем этот код в RAM чипа по адресу, который никогда не используется (или перекрываем неиспользуемую функцию).
- Патчим начало целевой функции инструкцией BL (branch with link) на наш код.
Будущее и эпилог
Пока я писал это, наверняка вышла еще пара уязвимостей. Помнишь про атаку Bluetooth Impersonation AttackS (BIAS)? Это уже было. Потом была атака на протокол паринга, где выяснилось, что некоторые чипы Broadcom не проверяли подпись в ответе на запрос паринга (CVE-2023-45866 - как пример, где атакующий может притвориться клавиатурой и подключиться к устройству без подтверждения).Суть всех этих багов одна: предположение о доверии. Чип доверяет пакетам, которые приходят по радио, даже если они выглядят подозрительно. Производители экономят на проверках ради производительности и энергоэффективности.
Если ты вынес из этого только одно - запомни вот что: Bluetooth-чип в твоём ноутбуке или телефоне - это не периферия, это полноценный агент влияния внутри твоей системы. У него есть свой мозг, своя память, свой код, и он имеет прямой доступ к критическим ресурсам хоста. И при этом код этот написан инженерами, которые думали в первую очередь о совместимости и производительности, а не о безопасности. Это legacy-код, который тащится из 2000-х годов, обрастая новыми функциями как снежный ком.
Мы не решили эту проблему. Мы даже не приблизились к её решению. Мы просто заглянули за кулисы и увидели, сколько там пыли, паутины и забытых скелетов в шкафу.
Что теперь делать?
Для обычного пользователя - ничего. Правда. Ты не защитишься от атаки на прошивку Bluetooth, если кто-то целенаправленно идёт за тобой с эксплойтом нулевого дня. Максимум - отключай Bluetooth, когда не пользуешься. Это реально работает. Не потому что это панацея, а потому что атакующему нужно окно, когда чип активен и слушает эфир. Выключил - и ты вне зоны досягаемости для радиоатак. Банально, но эффективно.
Для нас с тобой - для тех, кто читает такие материалы и хочет копать дальше - путь один: исследовать, учиться, делиться.
Бери InternalBlue, подключай к своему ноутбуку, дампи память, смотри, что там происходит. Покупай дешёвые USB-свистки на BCM, ломай их, чини, ломай снова. Изучай протоколы по спецификациям Bluetooth SIG - они открыты, скачай и читай. Смотри, как в прошивке реализованы те или иные фичи. Ищи несоответствия между спецификацией и реализацией. Там, где реализация расходится с докой - почти всегда баг.
Если найдёшь что-то интересное - не молчи. Пиши в рассылку безопасности Broadcom? Ха, удачи, они тебе даже не ответят. Но есть другие пути. Google Project Zero принимает отчёты, если баг действительно серьёзный. Можно публиковать в блогах, на конференциях, в открытых репозиториях. Можно участвовать в разработке InternalBlue и других opensource-инструментов.
И главное - не становись циником. Да, индустрия дерьмовая. Да, производителям плевать. Да, одни и те же баги переезжают из версии в версию десятилетиями. Но если мы с тобой будем просто сидеть и плеваться в потолок - ничего не изменится. А если будем ковырять, находить, публиковать, объяснять - может, через десять лет чипы станут чуточку безопаснее. Или хотя бы мы сами станем чуточку умнее.