
Airoha SDKの脆弱性チェーン(CVE-2025-20700/20701/20702)の影響を受けるワイヤレスイヤーバッド向けの、ドングル不要・root不要のBluetoothセキュリティ評価ツール
Version 1.0.0
Airoha SDK の脆弱性チェーン(CVE-2025-20700 / CVE-2025-20701 /
CVE-2025-20702)の影響を受けるワイヤレスイヤーバッズ向けの Bluetooth セキュリティ評価ツールです。周辺デバイスのスキャン、既知の影響を受ける Airoha ベースのチップセットのフィンガープリンティング、未認証 GATT アクセスと RACE プロトコルの到達可能性のプロービングを、OS の Bluetooth スタック(BlueZ)経由で bleak を使ってすべて行います。外部 Bluetooth ドングルは不要で、root も必要ありません。結果は技術的な詳細とともに平易な言葉で報告されるため、Bluetooth の深い知識がなくても対応できます。
このツールは、自分が所有するデバイス、または明示的な許可を得てテストするデバイスを評価するためのものです。GATT と RACE のプロービングは能動的な操作であり、対象デバイスに接続してコマンドを送信します。自分のデバイスではない、またはテスト許可を得ていないデバイスに対して、--gatt、--race、--firmware、--bd-address、--assess、--baseline、--check-drift、--memory-read を実行しないでください。--scan は受動的で、公開されているアドバタイズメントを傍受するだけなので、範囲内のどのデバイスに対して実行しても安全です。
--memory-read は他の能動的プローブより一歩進んでいます。到達可能性のみを確認する --race プローブが応答を得られなかった場合に、CVE-2025-20702 の決定的な確認として、デバイスの実際のフラッシュ内容の読み取り専用ページ(256 バイト)を固定アドレスから 1 回取得します。これは読み取り専用であり(フラッシュ読み取りは、このツールが決して送信しない書き込み/消去/FOTA コマンドと異なり、摩耗やブリックのリスクを伴いません)、オプトインであり、何をするのかを正確に説明する標準の所有権確認プロンプトに加えて、別途個別の確認が必要です。
近くの任意のデバイスをプロービングすることは、単なるポリシーの問題ではなく、実際の副作用を引き起こす可能性があります。--gatt は見つけたすべてのキャラクタリスティックに対して読み取りまたは通知サブスクライブを試みます。一部の民生デバイスは、プロビジョニング形式のサービス(例: 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 に対して開発)と、BlueZ が動作し Bluetooth アダプタが有効な Linux システムが必要です。
Linux のみ、かつ自動的にすべての Linux システムで動作するわけではありません:
bleak 自体には Windows バックエンドがありますが、このツールは bleak だけに依存しているわけではありません。Bluetooth Classic の検出(core/scanner.py)とボンディング状態のチェック(core/gatt.py)はどちらも bluetoothctl を直接シェルアウトしますが、これは BlueZ 専用の CLI ツールであり Windows には存在しません。それらのコードパスは単に「command not found」で失敗します。bluetoothctl を含む BlueZ が必要です。単なる Linux カーネルだけでは不十分です。ほとんどのデスクトップディストリビューションには含まれていますが、bluez パッケージがインストールされていない最小構成やサーバーイメージには標準では含まれていません。BlueZ 5.86 で root 不要を確認済みです。bleak は BlueZ の標準 D-Bus API をターゲットにしているため、他のバージョンでも同様に動作するはずですが、独立には再検証されていません。usbipd-win を介して Windows から転送する必要があり、これは USB 接続のアダプタのみを転送します。ほとんどのノート PC の内蔵 Bluetooth は非 USB バス(SDIO/PCIe、Wi-Fi と並行)経由で接続されているため、usbipd-win は通常転送できません。そのため、これは特定のハードウェアに完全に依存します。自分で Linux マシンを用意する必要はありません。必要なのは、実際の Bluetooth 無線にアクセスできる Linux だけです。実用的な方法が 2 つあります:
どちらの場合もルールは同じです。ツール自体は変わりません。必要なのは、BlueZ が実際に到達できる Bluetooth アダプタを備えた Linux だけです。
多くの TWS イヤーバッズは、省電力のため一定時間操作がないとアドバタイズ(およびアクティブな接続)を停止し、完全に電源が切れるものもあります。1 分前に見つかったデバイスがスキャンで見つからない場合や、プローブが途中で失敗する場合、それは通常イヤーバッズがアイドル状態になっただけで、バグではありません。ケースから取り出すか、ペアリングボタンをもう一度押して再試行してください。
これはアドレスの安定性にも影響します。このプロジェクトの確認済みテストユニット(Sony WF-1000XM3)は、テストしたすべての電源サイクルで同じ BLE アドレスを維持しました。これは、コンパニオンアプリの再接続用に設計されたイヤーバッズでは期待どおりです。通常、回転するアドレスではなく固定/公開 BLE アドレスを使用します(スマートフォンはプライベートアドレスを回転させるため、このツールの対象としては適していません)。ただし、これはすべてのイヤーバッズモデルで保証されているわけではありません。一部のベンダーは、ペアリング前/再接続モードでも解決可能なプライベートアドレスを使用しており、その場合、このツールのようなペアリングしていないスキャナーには電源サイクルごとに異なるアドレスとして見えます。
GATT プローブ(--gatt、および --assess の GATT ステージ)は、デバイスがペアリングを必要とするキャラクタリスティックを持つ場合、複数回の再接続が必要になることがあります。そのたびに BlueZ が実際のペアリングネゴシエーションを試行し(このツール自身のエージェントが拒否)、その後再接続してスイープを再開します。各試行の前に 1 行のステータスが表示されるため、遅いスイープがハングしているように見えることはありません。そのようなサブスクリプションが拒否されると、BlueZ はその意図を保持し、そのデバイスへの後続の接続のたびに再発行します。これが後続の再接続を妨げないように、プローブは再接続のたびに BlueZ のデバイスキャッシュ記録をクリアし(bluetoothctl remove に相当)、すべての試行がクリーンな状態から開始されるようにします。これにより、確認済みテストデバイスに対する連続したバックツーバックのスイープは、毎回同じ完全な結果を返します。以前の観察——激しいテストのセッション中に完全性が低下し、休止後に回復するように見えたこと——はその後再発しておらず、デバイスの疲労ではなく、同じ保持状態の蓄積であったと考えられています。
buds_audit.py
フラグをまったく付けずに実行すると、BLE アドレスや各フラグの機能をあらかじめ知っている必要はなく、番号付きメニューが起動します:
1) Full analysis (scan, run the full CVE audit, and save a baseline)
2) Check current state against a saved baseline
3) Scan for spoofed/impersonating devices
4) Exit
オプション 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
venv も依存関係もない状態でも、素の python3 buds_audit.py --help で動作します。実際に無線を必要とするコマンドが実行されるまで bleak をインポートしないためです。
buds_audit.py --scan
buds_audit.py --scan --flags-only # only show devices matching the known-affected catalog
buds_audit.py --scan --target AA:BB:CC:DD:EE:FF
周辺の BLE および Bluetooth Classic デバイスを受動的にスキャンし、製造元データとアドレスプレフィックスから Airoha チップセットをフィンガープリントし、data/affected_devices.json と照合します。
これらの各コマンドは --target ADDR を必要とし、その 1 台のデバイスに対する能動的な操作です:
buds_audit.py --gatt --target AA:BB:CC:DD:EE:FF # CVE-2025-20700: unauthenticated GATT access
buds_audit.py --race --target AA:BB:CC:DD:EE:FF # CVE-2025-20702: RACE channel reachability
buds_audit.py --firmware --target AA:BB:CC:DD:EE:FF # CVE-2025-20701: passive firmware/pairing-bypass check
buds_audit.py --bd-address --target AA:BB:CC:DD:EE:FF # Classic BD address via RACE, informational
4 つすべて、デバイスがすでにペアリング済みの場合はエラーなしでスキップします。ボンディング済みデバイスに対する「未認証アクセス」の発見は無意味だからです。
--gatt は、ペアリングなしの読み取りまたは通知が成功するたびに、読み取りが成功したという事実だけでなく、実際に返された値(16 進エンコード)を表示するようになりました。値はすでに取得されていたため、追加のリスクはなく、破棄されなくなっただけです。
--race は到達可能性のみをテストします(無害な SDK 情報クエリであり、メモリアクセスはありません)。RACE サービスが存在し、書き込みを正常に受け入れても応答しないことがありますが、これは本当に結論が出ない結果であり、何かが修正された証拠ではありません。決定的な答えについては、以下の --memory-read を参照してください。
--bd-address は情報提供用であり、単独では脆弱性の発見ではありません。同じ未認証 RACE チャネルを介してデバイスの実際の Bluetooth Classic(BR/EDR)アドレスを照会します。リスクの形状は --firmware の buildversion クエリ(ゼロペイロードのメタデータコマンド)と同じです。このツールには独自の Classic トランスポートがないため、Classic 対応の無線/ドングルを使って自分で CVE-2025-20701 の能動的テストを追求したい場合に役立ちます。以下のハードウェア要件のセクションを参照してください。
buds_audit.py --memory-read --target AA:BB:CC:DD:EE:FF
CVE-2025-20702 の決定的な確認として、実際の読み取り専用 RACE フラッシュページ読み取り(固定アドレスから 256 バイト)を 1 回試行します。--race が RACE サービスを検出したものの、その無害なクエリに応答しない場合に役立ちます。これは意図的にオプトインであり、--race とは別です。ここでの成功は、チャネルに到達可能かどうかの yes/no 信号だけでなく、実際のデバイスファームウェアコンテンツを取得します。書き込み、消去、リンクキーの抽出、RAM/レジスタの読み取りは一切行いません(読み取りの副作用がないフラッシュのみ)。詳細な理由は ROADMAP.md の Phase 8 および Out of Scope セクションを参照してください。標準の所有権プロンプトに加えて、何を行うかを正確に説明する独自の個別確認が必要です。