
Инструмент оценки безопасности Bluetooth для беспроводных наушников, затронутых цепочкой уязвимостей Airoha SDK (CVE-2025-20700/20701/20702), не требующий донгла и прав root.
Версия 1.0.0
Инструмент оценки безопасности Bluetooth для беспроводных наушников, подверженных цепочке уязвимостей SDK Airoha (CVE-2025-20700 / CVE-2025-20701 / CVE-2025-20702). Сканирует ближайшие устройства, идентифицирует известные уязвимые чипсеты на базе Airoha и проверяет неаутентифицированный доступ через GATT и доступность протокола RACE — полностью через стек Bluetooth ОС (BlueZ) с помощью bleak. Внешний Bluetooth-адаптер не требуется, root-доступ не нужен. Результаты выводятся на простом языке вместе с техническими деталями, чтобы вы могли действовать на их основе без глубоких знаний Bluetooth.
Этот инструмент предназначен для оценки устройств, которые вам принадлежат или на тестирование которых вы получили явное разрешение. Зондирование GATT и RACE — это активные операции: они подключаются к целевому устройству и отправляют ему команды. Не запускайте --gatt, --race, --firmware, --bd-address, --assess, --baseline, --check-drift или --memory-read против устройства, которое не является вашим или на которое вы не получили разрешение на тестирование. --scan является пассивным и только прослушивает рекламные пакеты, которые уже публично транслируются, поэтому его безопасно запускать против любых устройств в зоне действия.
--memory-read идет на шаг дальше, чем другие активные зонды: он извлекает одну реальную, доступную только для чтения страницу (256 байт) фактического содержимого флэш-памяти устройства по фиксированному адресу, как окончательное подтверждение CVE-2025-20702, когда зонд --race, проверяющий только доступность, не получает ответа. Он доступен только для чтения (чтение флэш-памяти не несет риска износа или повреждения, в отличие от команд записи/стирания/FOTA, которые этот инструмент никогда не отправляет), является опциональным и требует собственного отдельного подтверждения помимо стандартного запроса на владение, в котором точно описано, что он делает, прежде чем что-либо запускать.
Зондирование произвольного nearby-устройства — это не только вопрос политики; оно может иметь реальные побочные эффекты. --gatt пытается выполнить чтение или подписку на уведомления для каждой найденной характеристики, и некоторые потребительские устройства предоставляют сервисы типа provisioning (например, сервис Google Fast Pair), которые реагируют на это запуском реального процесса сопряжения на целевом устройстве, независимо от того, что явно запрашивает этот инструмент. Характеристика, требующая шифрования, может вызвать то же самое даже на вашем собственном устройстве, поскольку BlueZ может незаметно направить этот запрос аутентификации любому агенту, зарегистрированному вашим рабочим столом (например, запросу на сопряжение от KDE) — поэтому каждая активная команда также регистрирует свой собственный временный агент BlueZ, который автоматически отклоняет любой такой запрос на время зонда, так что никакой запрос на сопряжение вообще не может появиться. Каждая активная команда также по-прежнему запрашивает подтверждение, что целевой адрес принадлежит вам, прежде чем что-либо делать в радиоэфире; передайте --yes, чтобы пропустить запрос для скриптового использования, после того как вы уже подтвердили, что это ваше устройство:
buds_audit.py --assess --target AA:BB:CC:DD:EE:FF --yes
--watch является пассивным, как --scan — он только слушает уже транслируемые рекламные пакеты и никогда не подключается ни к чему, поэтому не запрашивает подтверждение.
python3 -m venv venv
venv/bin/pip install -r requirements.txt
Требуется Python 3.10+ (разрабатывалось под 3.14) и Linux-система с BlueZ и включенным Bluetooth-адаптером.
Только Linux, и не обязательно каждая Linux-система:
bleak есть бэкенд для Windows, но этот инструмент полагается не только на bleak — обнаружение Bluetooth Classic (core/scanner.py) и проверка состояния сопряжения (core/gatt.py) напрямую вызывают bluetoothctl, инструмент командной строки, существующий только в BlueZ, которого нет в Windows. Эти пути кода просто завершатся ошибкой "command not found".bluetoothctl в PATH, а не просто любое ядро Linux. В большинстве дистрибутивов для настольных ПК это есть; минимальный образ или образ сервера без установленного пакета bluez не будет иметь этого из коробки. Проверено без root на BlueZ 5.86 — другие версии должны работать так же, поскольку bleak нацелен на стандартный D-Bus API BlueZ, но это независимо не перепроверялось.usbipd-win, который перенаправляет только USB-адаптеры. Встроенный Bluetooth большинства ноутбуков подключен по не-USB шине (SDIO/PCIe, вместе с Wi-Fi), которую usbipd-win обычно не может перенаправить — поэтому это полностью зависит от конкретного оборудования.Вам не нужна собственная Linux-машина — вам просто нужен Linux с реальным доступом к Bluetooth-радио. Два практических способа это получить:
В любом случае правило одинаково: сам инструмент не меняется — ему просто нужен Linux с Bluetooth-адаптером, до которого BlueZ действительно может добраться.
Многие TWS-наушники прекращают рекламироваться (и разрывают любое активное соединение) после периода бездействия для экономии энергии, а некоторые полностью выключаются сами. Если сканирование не может найти устройство, которое оно нашло минуту назад, или зонд не срабатывает на полпути, это обычно означает, что наушники перешли в режим ожидания, а не ошибка — достаньте их из чехла или снова нажмите кнопку сопряжения и повторите попытку.
Это также влияет на стабильность адреса: подтвержденное тестовое устройство этого проекта (Sony WF-1000XM3) сохраняло один и тот же BLE-адрес во всех протестированных циклах включения/выключения, что ожидаемо для наушников, предназначенных для переподключения через приложение-компаньон — они обычно используют фиксированный/публичный BLE-адрес, а не сменный (в отличие от телефонов, которые меняют частные адреса и по этой причине не являются подходящей целью для данного инструмента). Однако это не гарантируется для каждой модели наушников — некоторые производители используют разрешаемые частные адреса даже в режиме предварительного сопряжения/переподключения, которые для неподключенного сканера, как этот инструмент, будут выглядеть как другой адрес после каждого цикла питания.
GATT-зонд (--gatt, а также этап GATT в --assess) может потребовать нескольких переподключений, если устройство имеет характеристики, требующие сопряжения — каждый раз BlueZ пытается выполнить (а собственный агент этого инструмента отклоняет) реальное согласование сопряжения перед переподключением для продолжения сканирования, и перед каждой попыткой выводится однострочный статус, чтобы медленное сканирование не выглядело зависшим. Когда такая подписка отклоняется, BlueZ запоминает намерение и повторно выдает его при каждом последующем подключении к этому устройству; чтобы это не нарушало последующие переподключения, зонд очищает кэшированную запись BlueZ об устройстве (эквивалентно bluetoothctl remove) перед каждым переподключением, так что каждая попытка начинается с чистого состояния. С этим исправлением повторные последовательные сканирования на подтвержденном тестовом устройстве каждый раз возвращают один и тот же полный результат. Более раннее наблюдение — ухудшение полноты в течение сессии интенсивного тестирования и восстановление после отдыха — с тех пор не повторялось, и считается, что это было накоплением сохраненного состояния, а не усталостью устройства.
buds_audit.py
Запуск без каких-либо флагов открывает нумерованное меню вместо того, чтобы требовать заранее знать BLE-адрес или какой флаг что делает:
1) Полный анализ (сканирование, полная проверка CVE и сохранение базовой линии)
2) Проверка текущего состояния по сохраненной базовой линии
3) Сканирование на поддельные/имитирующие устройства
4) Выход
Вариант 1 сканирует ближайшие известные уязвимые устройства и предлагает выбрать их по номеру (вместо ввода MAC-адреса), запускает полную проверку CVE (так же, как --assess, включая запрос BD-адреса) и сохраняет базовую линию (так же, как --baseline), чтобы будущие запуски могли обнаружить изменения. Он также задает тот же вопрос о чтении памяти, на который отвечает --assess --memory-read через свой запрос подтверждения — ответ "да" включает ту же реальную, только для чтения, страницу флэш-памяти RACE, как описано выше; ответ "нет" просто запускает аудит без нее, это не отменяет весь анализ. Вариант 2 выводит список устройств, для которых уже сохранена базовая линия, и повторно проверяет выбранное на предмет отклонений (так же, как --check-drift). Вариант 3 — это --watch. Каждый вариант по-прежнему проходит через то же подтверждение владения, что и интерфейс на основе флагов, прежде чем трогать радиоэфир — мастер — это более дружелюбный интерфейс поверх тех же самых базовых проверок, а не отдельный, менее осторожный путь.
Интерфейс на основе флагов ниже по-прежнему доступен для скриптового использования или для тех, кто уже знает адрес, на который хочет нацелиться.
Все команды запускаются через venv/bin/python buds_audit.py.
buds_audit.py --help
Работает при простом python3 buds_audit.py --help даже без venv и без установленных зависимостей — он не импортирует bleak до тех пор, пока не будет запущена команда, которой действительно нужно радио.
buds_audit.py --scan
buds_audit.py --scan --flags-only # показывать только устройства, соответствующие каталогу известных уязвимых
buds_audit.py --scan --target AA:BB:CC:DD:EE:FF
Пассивно сканирует ближайшие устройства BLE и Bluetooth Classic, идентифицирует чипсеты Airoha по данным производителя и префиксу адреса, а также сверяет с data/affected_devices.json.
Каждый из них требует --target ADDR и является активной операцией против одного устройства:
buds_audit.py --gatt --target AA:BB:CC:DD:EE:FF # CVE-2025-20700: неаутентифицированный доступ GATT
buds_audit.py --race --target AA:BB:CC:DD:EE:FF # CVE-2025-20702: доступность канала RACE
buds_audit.py --firmware --target AA:BB:CC:DD:EE:FF # CVE-2025-20701: пассивная проверка прошивки/обхода сопряжения
buds_audit.py --bd-address --target AA:BB:CC:DD:EE:FF # Классический BD-адрес через RACE, информационный
Все четыре корректно пропускаются (без ошибки), если устройство уже сопряжено — вывод "неаутентифицированный доступ" не имеет смысла для связанного устройства.
--gatt теперь показывает фактическое значение, возвращаемое каждым успешным чтением или уведомлением без сопряжения (в шестнадцатеричном коде), а не только то, что чтение удалось — значение уже извлекалось, так что это не представляет дополнительного риска, просто больше не отбрасывается.
--race проверяет только доступность (безвредный запрос информации SDK, без доступа к памяти) — служба RACE может присутствовать и принимать запись корректно, но все равно не отвечать, что является действительно неопределенным результатом, а не свидетельством того, что что-то исправлено. Для окончательного ответа см. --memory-read ниже.
--bd-address является информационным, а сам по себе не является находкой уязвимости: он запрашивает реальный классический Bluetooth-адрес (BR/EDR) устройства через тот же неаутентифицированный канал RACE, с тем же профилем риска, что и запрос версии сборки в --firmware (команда метаданных с нулевой полезной нагрузкой). Полезно, если вы хотите самостоятельно провести активное тестирование CVE-2025-20701 с помощью классического радио/адаптера, поскольку этот инструмент не имеет собственного классического транспорта — см. раздел Требования к оборудованию ниже.
buds_audit.py --memory-read --target AA:BB:CC:DD:EE:FF
Выполняет одно реальное, только для чтения, чтение страницы флэш-памяти RACE (256 байт, с фиксированного адреса) для окончательного подтверждения CVE-2025-20702 — полезно, когда --race обнаруживает, что служба RACE присутствует, но не отвечает на безвредный запрос. Это опционально и намеренно отделено от --race: успех здесь извлекает реальное содержимое прошивки устройства, а не просто сигнал "да/нет" о доступности канала. Он никогда не выполняет запись, стирание, извлечение ключей связки или чтение ОЗУ/регистров (только флэш-память, чтение которой не имеет побочных эффектов) — см. ROADMAP.md, Фаза 8 и раздел "Вне области видимости" для полного обоснования. Он требует собственного отдельного подтверждения, описывающего, что именно он делает, помимо стандартного запроса на владение.
buds_audit.py --assess --target AA:BB:CC:DD:EE:FF
buds_audit.py --assess --target AA:BB:CC:DD:EE:FF --json result.json
buds_audit.py --assess --target AA:BB:CC:DD:EE:FF --memory-read
Запускает вышеуказанные зонды GATT, RACE, прошивки и BD-адреса против одной цели и выносит единый вердикт: PASS, PARTIAL, VULNERABLE или SUSPECTED_COMPROMISE. Вердикт и каждая отдельная находка выводятся с интерпретацией на простом языке рядом с техническими деталями, чтобы результат был читаем без глубоких знаний Bluetooth — этот инструмент предназначен для всех, кто проверяет свои собственные устройства, а не только для специалистов по безопасности. --json дополнительно записывает полный результат (информация об устройстве, вердикт и его объяснение на простом языке, флаги с доказательствами и их пояснением на простом языке, а также рекомендации по исправлению) в файл.
Добавление --memory-read включает подтверждение чтения памяти в тот же аудит и вердикт, с собственным отдельным запросом подтверждения в первую очередь. Запрос BD-адреса выполняется автоматически как часть --assess (отдельный флаг не нужен, дополнительный запрос подтверждения не требуется), поскольку он имеет ту же форму низкорискового запроса метаданных, что и проверка прошивки.
--assess намеренно работает только с одной целью, так же как и индивидуальные зонды — нет режима "оценить каждое устройство в зоне действия", поскольку это означало бы активное зондирование устройств, которые могут вам не принадлежать.
buds_audit.py --baseline --target AA:BB:CC:DD:EE:FF
buds_audit.py --check-drift --target AA:BB:CC:DD:EE:FF
--baseline захватывает доверенный снимок устройства при первой оценке — идентичность (имя и данные производителя), таблицу GATT, сборку прошивки RACE и локальное состояние связывания (только логические значения сопряжен/доверен/связан, никогда не ключевой материал) — и сохраняет его в data/device_baselines.json. Он никогда не захватывается автоматически; вы должны явно запросить его, и повторный запуск перезаписывает существующую базовую линию.
--check-drift повторно захватывает тот же снимок и сравнивает его с сохраненной базовой линией, вынося вердикт на основе обнаруженных отклонений: IDENTITY_DRIFT, GATT_TABLE_DRIFT, FIRMWARE_DOWNGRADE или BOND_STATE_DRIFT. Это отвечает на вопрос "изменилось ли что-то с тех пор, как я в последний раз доверял этому устройству", а не "уязвимо ли это устройство" — это эвристический сигнал компрометации, а не криминалистическое доказательство. Устройство с любым флагом отклонения получает вердикт SUSPECTED_COMPROMISE, который отменяет все остальное.
buds_audit.py --watch
Непрерывно сканирует в окнах фиксированной длины (Ctrl+C для остановки) и сопоставляет каждое рекламное объявление по имени и данным производителя. Если два разных адреса передают одну и ту же идентичность с перекрывающимися окнами наблюдения — то есть оба были в эфире с этой идентичностью одновременно — выводится флаг POSSIBLE_IMPERSONATION. Одно физическое устройство, меняющее свой BLE-адрес со временем (наблюдаемое последовательно, а не одновременно), не помечается; помечается только настоящий второй передатчик. Соответствует последнему шагу модели угроз: подмена наушников для телефона жертвы.
data/affected_devices.json — это курируемый каталог, а не исчерпывающий список. В настоящее время подтверждено:
| Бренд | Модель | Airoha SoC | CVE | Исправленная прошивка |
|---|---|---|---|---|
| Sony | WF-1000XM3 | AB1562 | CVE-2025-20700, CVE-2025-20701, CVE-2025-20702 | Не выпущено |
Согласно раскрытию ERNW, другие бренды, использующие SoC серий Airoha AB1562/AB1565/AB1568 (включая Bose, Jabra, JBL, Marshall и модели Beats до исправления), также считаются уязвимыми, но пока не добавлены в каталог, поскольку их точные префиксы адресов и детали чипсета не были подтверждены на реальном оборудовании в этом проекте. Устройство, отсутствующее в каталоге, все равно может быть активно протестировано с помощью --gatt/--race/--firmware/--assess — каталог влияет только на пассивное совпадение при --scan и взвешивание вердикта, а не на то, что проверяют сами зонды.
Этот инструмент оценивает CVE-2025-20701 (отсутствие принудительного сопряжения Bluetooth Classic) только пассивно, через проверку версии сборки прошивки RACE. Активное тестирование возможности выполнения скрытого рукопожатия сопряжения требует прямого доступа к HCI через Bumble и выделенный USB Bluetooth-адаптер, совместимый с Bumble — это не достижимо через BlueZ/bleak, поэтому данный инструмент этого не пытается. См. race-toolkit от ERNW для интерактивной эталонной реализации на основе адаптера, охватывающей все три CVE.
Цепочка уязвимостей SDK Airoha (CVE-2025-20700 / CVE-2025-20701 / CVE-2025-20702) была обнаружена и раскрыта Деннисом Хайнце и Фридером Штайнмецем из ERNW. Их race-toolkit является эталонной реализацией, рядом с которой этот проект заполняет пробел без использования адаптера, а точные UUID GATT протокола RACE и структура пакетов, используемые здесь, были прочитаны непосредственно из его исходного кода, а не угаданы — см. core/race.py для подробностей. race-toolkit не имеет лицензии (нет файла LICENSE, проверено непосредственно по репозиторию) — ничего из его исходного кода не используется здесь, кроме базовых фактов протокола (UUID, структура, коды команд), которые описывают собственный протокол Airoha и не являются оригинальным выражением его авторов для лицензирования.
MIT — см. LICENSE.
venv/bin/ruff check . --fix && venv/bin/ruff format .
venv/bin/python -m pytest tests/