
Kein Dongle, kein Root Bluetooth-Sicherheitsbewertungstool für drahtlose Ohrhörer, die von der Airoha SDK-Schwachstellenkette betroffen sind (CVE-2025-20700/20701/20702)
Version 1.0.0
Bluetooth-Sicherheitsbewertungstool für drahtlose Ohrhörer, die von der Airoha-SDK-Schwachstellenkette (CVE-2025-20700 / CVE-2025-20701 / CVE-2025-20702) betroffen sind. Es scannt nach Geräten in der Nähe, erstellt Fingerabdrücke von bekannten betroffenen Airoha-basierten Chipsätzen und prüft auf nicht authentifizierten GATT-Zugriff und RACE-Protokoll-Erreichbarkeit – vollständig über den OS-Bluetooth-Stack (BlueZ) mittels bleak. Es wird kein externer Bluetooth-Dongle benötigt und Root ist nicht erforderlich. Die Ergebnisse werden in einfacher Sprache zusammen mit den technischen Details gemeldet, sodass Sie ohne tiefgehende Bluetooth-Kenntnisse handeln können.
Dieses Tool dient zur Bewertung von Geräten, die Ihnen gehören oder für die Sie die ausdrückliche Erlaubnis zum Testen haben. GATT- und RACE-Prüfungen sind aktive Vorgänge: Sie stellen eine Verbindung zum Zielgerät her und senden Befehle. Führen Sie --gatt, --race, --firmware, --bd-address, --assess, --baseline, --check-drift oder --memory-read nicht gegen ein Gerät aus, das nicht Ihnen gehört oder für das Sie keine Testgenehmigung erhalten haben. --scan ist passiv und lauscht nur auf bereits öffentlich ausgestrahlte Ankündigungen, daher ist es sicher, es gegen alles in Reichweite auszuführen.
--memory-read geht noch einen Schritt weiter als die anderen aktiven Prüfungen: Es ruft eine reale, schreibgeschützte Seite (256 Byte) des tatsächlichen Flash-Inhalts des Geräts an einer festen Adresse ab, als endgültige Bestätigung von CVE-2025-20702, wenn die nur auf Reichweite prüfende --race-Prüfung keine Antwort erhält. Es ist schreibgeschützt (Flash-Lesevorgänge bergen kein Abnutzungs- oder Zerstörungsrisiko, im Gegensatz zu Schreib-/Lösch-/FOTA-Befehlen, die dieses Tool nie sendet), optional und erfordert eine eigene separate Bestätigung über die Standard-Besitzabfrage hinaus, die genau beschreibt, was es tut, bevor es etwas ausführt.
Das Prüfen eines beliebigen Geräts in der Nähe ist nicht nur eine Frage der Richtlinie – es kann echte Nebenwirkungen haben. --gatt versucht einen Lese- oder Benachrichtigungs-Abonnement bei jedem gefundenen Charakteristikum, und einige Unterhaltungselektronikgeräte stellen Bereitstellungsdienste bereit (z. B. Google Fast Pair Service), die darauf reagieren, indem sie einen echten Kopplungs-Handshake auf dem Zielgerät auslösen, unabhängig von dem, was dieses Tool explizit anfordert. Ein Charakteristikum, das Verschlüsselung erfordert, kann selbst gegen Ihr eigenes Gerät dasselbe auslösen, da BlueZ diese Authentifizierungsanfrage stillschweigend an den Agenten weiterleiten kann, den Ihr Desktop registriert hat (z. B. KDE-Kopplungsaufforderung) – daher registriert jeder aktive Befehl auch seinen eigenen temporären BlueZ-Agenten, der jede solche Anfrage für die Dauer der Prüfung automatisch ablehnt, sodass überhaupt keine Kopplungsaufforderung erscheinen kann. Jeder aktive Befehl fordert außerdem weiterhin eine Bestätigung an, dass die Zieladresse Ihre ist, bevor etwas über Funk gesendet wird; übergeben Sie --yes, um die Aufforderung für Skriptnutzung zu überspringen, sobald Sie bestätigt haben, dass es Ihr Gerät ist:
buds_audit.py --assess --target AA:BB:CC:DD:EE:FF --yes
--watch ist passiv, wie --scan – es lauscht nur auf bereits ausgestrahlte Ankündigungen und verbindet sich nie mit irgendetwas, daher fordert es keine Bestätigung an.
python3 -m venv venv
venv/bin/pip install -r requirements.txt
Erfordert Python 3.10+ (entwickelt mit 3.14) und ein Linux-System mit BlueZ und einem eingeschalteten Bluetooth-Adapter.
Nur Linux, und nicht automatisch jedes Linux-System:
bleak selbst hat ein Windows-Backend, aber dieses Tool verlässt sich nicht allein auf bleak – die Bluetooth-Classic-Erkennung (core/scanner.py) und die Überprüfung des Kopplungsstatus (core/gatt.py) nutzen beide direkt bluetoothctl, ein reines BlueZ-CLI-Tool, das es unter Windows nicht gibt. Diese Codepfade würden einfach mit "Befehl nicht gefunden" fehlschlagen.bluetoothctl im PATH, nicht nur einen beliebigen Linux-Kernel. Die meisten Desktop-Distributionen liefern dies mit; ein minimales oder Server-Image ohne das installierte bluez-Paket hat es nicht standardmäßig. Getestet und root-frei mit BlueZ 5.86 – andere Versionen sollten genauso funktionieren, da bleak auf die standardmäßige D-Bus-API von BlueZ abzielt, aber dies wurde nicht unabhängig erneut überprüft.usbipd-win, das nur USB-angeschlossene Adapter weiterleitet. Das eingebaute Bluetooth der meisten Laptops ist über einen nicht-USB-Bus (SDIO/PCIe, zusammen mit WLAN) angeschlossen, den usbipd-win in der Regel nicht weiterleiten kann – daher hängt dies vollständig von der spezifischen Hardware ab.Sie benötigen keinen eigenen Linux-Rechner – Sie brauchen nur Linux mit echtem Zugang zu einem Bluetooth-Funk. Zwei praktische Möglichkeiten, dies zu erreichen:
In beiden Fällen gilt die gleiche Regel: Das Tool selbst ist unverändert – es benötigt lediglich Linux mit einem Bluetooth-Adapter, den BlueZ tatsächlich erreichen kann.
Viele TWS-Ohrhörer stoppen das Senden von Ankündigungen (und trennen jede aktive Verbindung) nach einer gewissen Inaktivitätszeit, um Strom zu sparen, und einige schalten sich vollständig aus. Wenn ein Scan ein Gerät, das vor einer Minute gefunden wurde, nicht finden kann oder eine Prüfung teilweise fehlschlägt, liegt das in der Regel daran, dass die Ohrhörer in den Leerlauf gegangen sind, nicht an einem Fehler – nehmen Sie sie aus der Hülle oder drücken Sie erneut die Kopplungstaste und wiederholen Sie den Vorgang.
Dies betrifft auch die Adressstabilität: Die bestätigte Testeinheit dieses Projekts (ein Sony WF-1000XM3) behielt während aller getesteten Stromzyklen dieselbe BLE-Adresse bei, was für Ohrhörer zu erwarten ist, die für die Wiederverbindung mit Companion-Apps ausgelegt sind – sie verwenden typischerweise eine feste/öffentliche BLE-Adresse anstelle einer rotierenden (im Gegensatz zu Telefonen, die private Adressen rotieren und aus diesem Grund kein geeignetes Ziel für dieses Tool sind). Dies ist jedoch nicht für jedes Ohrhörermodell garantiert – einige Hersteller verwenden auch im Vor-Kopplungs-/Wiederverbindungsmodus auflösbare private Adressen, die nach jedem Stromzyklus als andere Adresse für einen ungekoppelten Scanner wie dieses Tool erscheinen würden.
Die GATT-Prüfung (--gatt und die GATT-Stufe von --assess) kann mehrere Wiederverbindungen erfordern, wenn das Gerät Charakteristika hat, die eine Kopplung erfordern – jede führt dazu, dass BlueZ versucht (und der eigene Agent dieses Tools ablehnt) eine echte Kopplungsaushandlung durchzuführen, bevor die Verbindung zur Fortsetzung des Durchlaufs wiederhergestellt wird, und gibt vor jedem Versuch eine einzeilige Statusmeldung aus, damit ein langsamer Durchlauf nicht wie ein Hängenbleiben aussieht. Wenn ein solches Abonnement abgelehnt wird, behält BlueZ die Absicht bei und gibt sie bei jeder späteren Verbindung zu diesem Gerät erneut aus; um zu verhindern, dass dies nachfolgende Wiederverbindungen stört, löscht die Prüfung den zwischengespeicherten Eintrag von BlueZ für das Gerät (entspricht bluetoothctl remove) vor jeder Wiederverbindung, sodass jeder Versuch mit einem sauberen Zustand beginnt. Mit dieser Maßnahme liefern wiederholte aufeinanderfolgende Durchläufe gegen das bestätigte Testgerät jedes Mal das gleiche vollständige Ergebnis. Eine frühere Beobachtung – dass die Vollständigkeit während einer Sitzung intensiver Tests nachzulassen schien und sich nach einer Pause erholte – ist seitdem nicht wieder aufgetreten und wird als dieselbe Ansammlung von beibehaltenem Zustand und nicht als Geräteermüdung angesehen.
buds_audit.py
Die Ausführung ohne jegliche Flags startet ein nummeriertes Menü, anstatt dass Sie bereits eine BLE-Adresse kennen oder wissen müssen, welches Flag was tut:
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
Option 1 scannt nach bekannten betroffenen Geräten in der Nähe und listet sie zur Auswahl per Nummer auf (anstatt eine MAC-Adresse einzugeben), führt das vollständige CVE-Audit durch (gleich wie --assess, einschließlich der BD-Adressabfrage) und speichert eine Basislinie (gleich wie --baseline), sodass zukünftige Ausführungen Änderungen erkennen können. Es stellt auch die gleiche Speicherlese-Frage, die --assess --memory-read über seinen Bestätigungsdialog beantwortet – ein Ja dort beinhaltet denselben echten, schreibgeschützten RACE-Flashseiten-Lesevorgang wie oben beschrieben; ein Nein führt einfach das Audit ohne ihn durch, es bricht nicht die gesamte Analyse ab. Option 2 listet Geräte auf, für die Sie bereits eine Basislinie erstellt haben, und überprüft das ausgewählte erneut auf Abweichungen (gleich wie --check-drift). Option 3 ist --watch. Jede Option durchläuft immer noch dieselbe Besitzbestätigung wie die flag-basierte Oberfläche, bevor das Funkgerät berührt wird – der Assistent ist eine benutzerfreundlichere Oberfläche über genau denselben zugrundeliegenden Prüfungen, kein separater, weniger sorgfältiger Pfad.
Die unten aufgeführte flag-basierte Oberfläche steht weiterhin für die Skriptnutzung oder für jeden zur Verfügung, der bereits die Adresse kennt, die er anvisieren möchte.
Alle Befehle werden über venv/bin/python buds_audit.py ausgeführt.
buds_audit.py --help
Funktioniert auch mit einem einfachen python3 buds_audit.py --help ohne venv und ohne installierte Abhängigkeiten – es importiert bleak erst, wenn ein Befehl ausgeführt wird, der tatsächlich das Funkgerät benötigt.
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
Scannt passiv nach nahen BLE- und Bluetooth-Classic-Geräten, erstellt Fingerabdrücke von Airoha-Chipsätzen anhand von Herstellerdaten und Adresspräfixen und gleicht sie mit data/affected_devices.json ab.
Jede dieser Prüfungen erfordert --target ADDR und ist ein aktiver Vorgang gegen dieses eine Gerät:
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
Alle vier überspringen sauber (kein Fehler), wenn das Gerät bereits gekoppelt ist – eine "nicht authentifizierter Zugriff"-Feststellung ist gegenüber einem gebundenen Gerät bedeutungslos.
--gatt zeigt jetzt den tatsächlichen Wert, der von jedem erfolgreichen ungekoppelten Lesevorgang oder Benachrichtigung zurückgegeben wurde (hex-kodiert), nicht nur, dass der Lesevorgang erfolgreich war – der Wert wurde bereits abgerufen, daher ist dies kein zusätzliches Risiko, sondern wird nur nicht mehr verworfen.
--race testet nur die Erreichbarkeit (eine harmlose SDK-Informationsabfrage, kein Speicherzugriff) – ein RACE-Dienst kann vorhanden sein und den Schreibvorgang sauber akzeptieren, aber dennoch nicht antworten, was ein wirklich nicht schlüssiges Ergebnis ist, kein Beweis für etwas Gefixtes. Für eine endgültige Antwort siehe --memory-read unten.
--bd-address ist informativ, keine Schwachstellenfeststellung für sich allein: Es fragt die echte Bluetooth-Classic (BR/EDR)-Adresse des Geräts über denselben nicht authentifizierten RACE-Kanal ab, gleiches Risikoprofil wie die Buildversion-Abfrage von --firmware (ein Metadatenbefehl ohne Nutzlast). Nützlich, wenn Sie CVE-2025-20701 selbst mit einem Classic-fähigen Funkgerät/Dongle aktiv testen möchten, da dieses Tool selbst keinen Classic-Transport hat – siehe Abschnitt Hardware-Anforderung unten.
buds_audit.py --memory-read --target AA:BB:CC:DD:EE:FF
Versucht einen echten, schreibgeschützten RACE-Flashseiten-Lesevorgang (256 Byte, von einer festen Adresse) für eine endgültige Bestätigung von CVE-2025-20702 – nützlich, wenn --race den RACE-Dienst vorhanden, aber nicht ansprechbar auf seine harmlose Abfrage hin findet. Dies ist absichtlich optional und separat von --race: Ein Erfolg hier ruft echten Geräte-Firmware-Inhalt ab, nicht nur ein Ja/Nein-Signal, ob der Kanal erreichbar ist. Es schreibt nie, löscht nie, extrahiert keine Verbindungsschlüssel und liest nie RAM/Register (nur Flash, der keine Lesene beneffekte hat) – siehe ROADMAP.md Phase 8 und Abschnitt "Außerhalb des Rahmens" für die vollständige Begründung. Es erfordert eine eigene separate Bestätigung, die genau beschreibt, was es tut, über die standardmäßige Besitzabfrage hinaus.
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
Führt die obigen GATT-, RACE-, Firmware- und BD-Adressprüfungen gegen ein Ziel aus und erzeugt ein einzelnes Urteil: PASS, PARTIAL, VULNERABLE oder SUSPECTED_COMPROMISE. Das Urteil und jede einzelne Feststellung werden mit einer Interpretation in einfacher Sprache zusammen mit den technischen Details ausgegeben, sodass das Ergebnis ohne tiefgehende Bluetooth-Kenntnisse lesbar ist – dieses Tool ist für jeden gedacht, der seine eigenen Geräte überprüft, nicht nur für Sicherheitsspezialisten. --json schreibt zusätzlich das vollständige Ergebnis (Geräteinformationen, Urteil und dessen Erklärung in einfacher Sprache, Flags mit Beweisen und deren Erläuterung in einfacher Sprache sowie Behebungsnotizen) in eine Datei. Das Hinzufügen von --memory-read bindet die Speicherlese-Bestätigung in dasselbe Audit und Urteil ein, mit einer eigenen separaten Bestätigungsaufforderung zuerst. Die BD-Adressabfrage wird automatisch als Teil von --assess ausgeführt (kein separates Flag erforderlich, keine zusätzliche Bestätigungsaufforderung), da es sich um dieselbe risikoarme Metadatenabfrage handelt wie die Firmware-Prüfung.
--assess ist bewusst nur auf ein einzelnes Ziel ausgelegt, genau wie die einzelnen Prüfungen – es gibt keinen Modus "Jedes Gerät in Reichweite bewerten", da dies bedeuten würde, aktiv Geräte zu prüfen, die möglicherweise nicht Ihre sind.
buds_audit.py --baseline --target AA:BB:CC:DD:EE:FF
buds_audit.py --check-drift --target AA:BB:CC:DD:EE:FF
--baseline erfasst einen vertrauenswürdigen Schnappschuss für ein Gerät beim ersten Bewerten – Identität (Name und Herstellerdaten), GATT-Tabelle, RACE-Firmware-Build und lokaler Kopplungsstatus (nur boolesche Werte für gekoppelt/vertraut/gebunden, niemals Schlüsselmaterial) – und speichert ihn in data/device_baselines.json. Es wird niemals automatisch erfasst; Sie müssen explizit danach fragen, und eine erneute Ausführung überschreibt die vorhandene Basislinie.
--check-drift erfasst denselben Schnappschuss erneut und vergleicht ihn mit der gespeicherten Basislinie, wobei ein Urteil basierend auf der gefundenen Abweichung erstellt wird: IDENTITY_DRIFT, GATT_TABLE_DRIFT, FIRMWARE_DOWNGRADE oder BOND_STATE_DRIFT. Dies beantwortet die Frage "Hat sich etwas geändert, seit ich diesem Gerät das letzte Mal vertraut habe?" und nicht "Ist dieses Gerät anfällig?" – es ist ein heuristisches Kompromittierungssignal, kein forensischer Beweis. Ein Gerät mit einer Abweichungsmarkierung erhält ein SUSPECTED_COMPROMISE-Urteil, das alles andere außer Kraft setzt.
buds_audit.py --watch
Scannt kontinuierlich in festen Zeitfenstern (Strg+C zum Stoppen) und korreliert jede gesehene Ankündigung nach Name und Herstellerdaten. Wenn zwei verschiedene Adressen dieselbe Identität mit überlappenden Beobachtungsfenstern ausstrahlen – d.h. beide waren gleichzeitig mit dieser Identität auf Sendung – wird POSSIBLE_IMPERSONATION markiert. Ein einzelnes physisches Gerät, das seine BLE-Adresse im Laufe der Zeit rotiert (sequentiell gesehen, nicht gleichzeitig), wird nicht markiert; nur ein echter zweiter Sender wird markiert. Entspricht dem letzten Schritt des Bedrohungsmodells: Identitätsdiebstahl der Ohrhörer gegenüber dem Telefon des Opfers.
data/affected_devices.json ist ein kuratierter Katalog, keine erschöpfende Liste. Derzeit bestätigt:
| Brand | Model | Airoha SoC | CVEs | Patched firmware |
|---|---|---|---|---|
| Sony | WF-1000XM3 | AB1562 | CVE-2025-20700, CVE-2025-20701, CVE-2025-20702 | None released |
Laut ERNW-Offenlegung sind auch andere Marken, die Airoha AB1562/AB1565/AB1568-Serien SoCs verwenden (einschließlich Bose, Jabra, JBL, Marshall und Vor-Patch-Beats-Modelle), ebenfalls betroffen gemeldet, befinden sich aber noch nicht im Katalog, da ihre genauen Adresspräfixe und Chipsatzdetails in diesem Projekt noch nicht an echter Hardware bestätigt wurden. Ein Gerät außerhalb des Katalogs kann dennoch mit --gatt/--race/--firmware/--assess aktiv geprüft werden – der Katalog beeinflusst nur die passive --scan-Übereinstimmung und die Urteilsgewichtung, nicht das, was die Prüfungen selbst testen.
Dieses Tool bewertet CVE-2025-20701 (fehlende Durchsetzung der Bluetooth-Classic-Kopplung) nur passiv über die RACE-Firmware-Buildversion-Prüfung. Das aktive Testen, ob ein stiller Kopplungs-Handshake abgeschlossen werden kann, erfordert rohen HCI-Zugriff über Bumble und einen dedizierten Bumble-kompatiblen USB-Bluetooth-Dongle – nicht erreichbar über BlueZ/bleak, weshalb dieses Tool es nicht versucht. Siehe ERNWs race-toolkit für eine interaktive, dongle-basierte Referenzimplementierung, die alle drei CVEs abdeckt.
Die Airoha-SDK-Schwachstellenkette (CVE-2025-20700 / CVE-2025-20701 / CVE-2025-20702) wurde von Dennis Heinze und Frieder Steinmetz bei ERNW entdeckt und offengelegt. Ihr race-toolkit ist die Referenzimplementierung, neben der dieses Projekt eine Lücke ohne Dongle schließt, und die genauen RACE-Protokoll-GATT-UUIDs und das hier verwendete Paket-Framing wurden direkt aus dessen Quelle gelesen, nicht erraten – siehe core/race.py für Einzelheiten. race-toolkit ist unlizenziert (keine LICENSE-Datei, direkt im Repository überprüft) – nichts aus seiner Quelle wird hier über die zugrundeliegenden Protokollfakten (UUIDs, Struct-Layout, Befehls-Codes) hinaus wiederverwendet, die Airohas eigenes Protokoll beschreiben und nicht der ursprüngliche Ausdruck seiner Autoren sind, der lizenziert werden müsste.
MIT – siehe LICENSE.
venv/bin/ruff check . --fix && venv/bin/ruff format .
venv/bin/python -m pytest tests/