
Senza dongle, senza root, strumento di valutazione della sicurezza Bluetooth per auricolari wireless affetti dalla catena di vulnerabilità dell'SDK Airoha (CVE-2025-20700/20701/20702)
Versione 1.0.0
Strumento di valutazione della sicurezza Bluetooth per auricolari wireless affetti dalla
catena di vulnerabilità dell'SDK Airoha (CVE-2025-20700 / CVE-2025-20701 /
CVE-2025-20702). Esegue la scansione dei dispositivi vicini, identifica i chipset
Airoha noti come vulnerabili e verifica l'accesso GATT non autenticato e la
raggiungibilità del protocollo RACE - interamente tramite lo stack Bluetooth del
sistema operativo (BlueZ) attraverso bleak. Non è richiesto alcun dongle Bluetooth
esterno né privilegi di root. I risultati vengono riportati in linguaggio semplice
insieme ai dettagli tecnici, in modo da poter agire senza conoscenze approfondite
del Bluetooth.
Questo strumento è destinato alla valutazione di dispositivi di tua proprietà o per i quali
hai esplicita autorizzazione. Le sonde GATT e RACE sono operazioni attive: si connettono
e inviano comandi al dispositivo target. Non eseguire --gatt, --race, --firmware,
--bd-address, --assess, --baseline, --check-drift o --memory-read
contro un dispositivo che non sia tuo o per il quale non hai ricevuto autorizzazione
per il test. --scan è passivo e si limita ad ascoltare gli annunci già
trasmessi pubblicamente, quindi è sicuro da eseguire su qualsiasi dispositivo nel raggio
d'azione.
--memory-read va oltre le altre sonde attive: recupera una pagina reale, di sola
lettura (256 byte) del contenuto flash effettivo del dispositivo, a un indirizzo fisso,
come conferma definitiva di CVE-2025-20702 quando la sonda --race (solo
raggiungibilità) non ottiene risposta. È di sola lettura (le letture flash non comportano
rischi di usura o danneggiamento, a differenza dei comandi di scrittura/cancellazione/FOTA,
che questo strumento non invia mai), è facoltativa e richiede una propria conferma
separata oltre al prompt standard di proprietà, che descrive esattamente cosa fa prima
di eseguire qualsiasi operazione.
Sondare un dispositivo arbitrario nelle vicinanze non è solo una questione di policy -
può avere effetti collaterali reali. --gatt tenta una lettura o una sottoscrizione di
notifica su ogni caratteristica che trova, e alcuni dispositivi consumer espongono servizi
di tipo provisioning (ad esempio il servizio Fast Pair di Google) che reagiscono avviando
una vera negoziazione di associazione sul dispositivo target, indipendentemente da ciò che
questo strumento richiede esplicitamente. Una caratteristica che richiede crittografia può
innescare la stessa cosa anche contro il tuo stesso dispositivo, poiché BlueZ può instradare
silenziosamente tale richiesta di autenticazione all'agente registrato dal tuo desktop
(ad esempio il prompt di associazione di KDE) - pertanto ogni comando attivo registra anche
un proprio agente BlueZ temporaneo che rifiuta automaticamente qualsiasi richiesta del genere
per la durata della sonda, in modo che non possa apparire alcun prompt di associazione. Ogni
comando attivo richiede comunque una conferma che l'indirizzo target sia tuo prima di fare
qualsiasi cosa via radio; passa --yes per saltare il prompt nell'uso scriptato, una volta
che hai già confermato che il dispositivo è tuo:
buds_audit.py --assess --target AA:BB:CC:DD:EE:FF --yes
--watch è passivo, come --scan - si limita ad ascoltare gli annunci già
trasmessi e non si connette mai a nulla, quindi non richiede conferma.
python3 -m venv venv
venv/bin/pip install -r requirements.txt
Richiede Python 3.10+ (sviluppato con 3.14) e un sistema Linux con BlueZ e un adattatore Bluetooth acceso.
Solo Linux, e non automaticamente tutti i sistemi Linux:
bleak ha un backend Windows, ma
questo strumento non si basa solo su bleak - la scoperta Bluetooth Classic
(core/scanner.py) e i controlli dello stato di associazione (core/gatt.py)
utilizzano direttamente bluetoothctl, un'interfaccia a riga di comando
specifica di BlueZ che non esiste su Windows. Quei percorsi di codice
fallirebbero semplicemente con "comando non trovato."bluetoothctl nel PATH, non solo un kernel Linux
qualsiasi. La maggior parte delle distribuzioni desktop lo include; un'immagine
server o minimale senza il pacchetto bluez installato non lo avrà
preinstallato. Verificato senza root su BlueZ 5.86 - altre versioni
dovrebbero funzionare allo stesso modo poiché bleak si rivolge all'API
D-Bus standard di BlueZ, ma non è stato verificato indipendentemente.usbipd-win, che inoltra solo adattatori
collegati via USB. La maggior parte dei Bluetooth integrati nei laptop è
collegata tramite bus non USB (SDIO/PCIe, insieme al Wi-Fi), che usbipd-win
generalmente non può inoltrare - quindi tutto dipende dall'hardware specifico.Non hai bisogno di una macchina Linux tua - ti serve solo Linux con accesso reale a un chip Bluetooth. Due modi pratici per ottenerlo:
In entrambi i casi, la regola è la stessa: lo strumento in sé è invariato - ha solo bisogno di Linux con un adattatore Bluetooth che BlueZ possa realmente raggiungere.
Molti auricolari TWS smettono di annunciarsi (e chiudono qualsiasi connessione attiva) dopo un periodo di inattività per risparmiare energia, e alcuni si spengono completamente da soli. Se una scansione non trova un dispositivo che aveva trovato un minuto prima, o una sonda fallisce a metà, di solito è perché gli auricolari sono andati in idle, non è un bug - tirali fuori dalla custodia o premi di nuovo il pulsante di associazione e riprova.
Questo influisce anche sulla stabilità dell'indirizzo: l'unità di test confermata di questo progetto (una Sony WF-1000XM3) ha mantenuto lo stesso indirizzo BLE per ogni ciclo di alimentazione testato, cosa prevista per auricolari progettati per la riconnessione con app companion - in genere usano un indirizzo BLE fisso/pubblico anziché uno rotante (a differenza dei telefoni, che ruotano gli indirizzi privati e per questo motivo non sono un target adatto per questo strumento). Tuttavia non è garantito per tutti i modelli di auricolari - alcuni vendor usano indirizzi privati risolvibili anche in modalità pre-associazione/riconnessione, che apparirebbero come un indirizzo diverso dopo ogni ciclo di alimentazione per uno scanner non associato come questo strumento.
La sonda GATT (--gatt, e la fase GATT di --assess) può richiedere
diverse riconnessioni se il dispositivo ha caratteristiche che richiedono
l'associazione - ciascuna fa sì che BlueZ tenti (e l'agente di questo
strumento rifiuti) una vera negoziazione di associazione prima di
riconnettersi per riprendere la scansione, e stampa una riga di stato prima
di ogni tentativo in modo che una scansione lenta non sembri bloccata. Quando
una tale sottoscrizione viene rifiutata, BlueZ mantiene l'intenzione e la
riemette a ogni connessione successiva a quel dispositivo; per evitare che
ciò comprometta le successive riconnessioni, la sonda cancella il record
cached di BlueZ del dispositivo (equivalente a bluetoothctl remove) prima
di ogni riconnessione, in modo che ogni tentativo parta da uno stato pulito.
Con questo sistema, scansioni consecutive ripetute contro il dispositivo di
test confermato restituiscono ogni volta lo stesso risultato completo.
Un'osservazione precedente - la completezza sembrava degradare durante una
sessione di test intensivi e recuperare dopo una pausa - non si è più
ripetuta da allora e si ritiene fosse la stessa accumulazione di stato
trattenuto piuttosto che affaticamento del dispositivo.
buds_audit.py
Eseguendolo senza alcun flag si avvia un menu numerato invece di richiedere che tu conosca già un indirizzo BLE o quale flag faccia cosa:
1) Analisi completa (scansiona, esegui il controllo completo delle CVE e salva una baseline)
2) Controlla lo stato corrente rispetto a una baseline salvata
3) Scansiona alla ricerca di dispositivi spoofati/impersonati
4) Esci
L'opzione 1 esegue la scansione dei dispositivi vicini noti come vulnerabili e
li elenca per sceglierli per numero (invece di digitare un indirizzo MAC), esegue
il controllo completo delle CVE (come --assess, inclusa la query dell'indirizzo
BD) e salva una baseline (come --baseline) in modo che le esecuzioni future
possano rilevare cambiamenti. Chiede anche la stessa domanda sulla lettura della
memoria a cui --assess --memory-read risponde tramite il suo prompt di conferma -
rispondere sì include la stessa lettura flash RACE reale e di sola lettura descritta
sopra; rispondere no esegue solo il controllo senza di essa, non annulla l'intera
analisi. L'opzione 2 elenca i dispositivi di cui hai già creato una baseline e
ricontrolla quello che scegli per eventuali derive (come --check-drift).
L'opzione 3 è --watch. Ogni opzione richiede comunque la stessa conferma di
proprietà dell'interfaccia basata su flag prima di toccare la radio - la procedura
guidata è un front-end più amichevole per gli stessi identici controlli sottostanti,
non un percorso separato e meno attento.
L'interfaccia basata su flag qui sotto è ancora presente per l'uso scriptato o per chi conosce già l'indirizzo che vuole targetizzare.
Tutti i comandi vengono eseguiti tramite venv/bin/python buds_audit.py.
buds_audit.py --help
Funziona anche con un semplice python3 buds_audit.py --help senza venv e senza
dipendenze installate - non importa bleak finché non viene eseguito un comando che
richiede effettivamente la radio.
buds_audit.py --scan
buds_audit.py --scan --flags-only # mostra solo i dispositivi corrispondenti al catalogo dei noti vulnerabili
buds_audit.py --scan --target AA:BB:CC:DD:EE:FF
Esegue una scansione passiva dei dispositivi BLE e Bluetooth Classic nelle vicinanze,
identifica i chipset Airoha dai dati del produttore e dal prefisso dell'indirizzo, e
li confronta con data/affected_devices.json.
Ciascuna richiede --target ADDR ed è un'operazione attiva contro quel singolo
dispositivo:
buds_audit.py --gatt --target AA:BB:CC:DD:EE:FF # CVE-2025-20700: accesso GATT non autenticato
buds_audit.py --race --target AA:BB:CC:DD:EE:FF # CVE-2025-20702: raggiungibilità canale RACE
buds_audit.py --firmware --target AA:BB:CC:DD:EE:FF # CVE-2025-20701: controllo passivo del firmware/bypass dell'associazione
buds_audit.py --bd-address --target AA:BB:CC:DD:EE:FF # Indirizzo BD Classic via RACE, informativo
Tutti e quattro saltano pulitamente (nessun errore) se il dispositivo è già associato - una scoperta di "accesso non autenticato" è priva di significato contro un dispositivo accoppiato.
--gatt ora mostra il valore effettivo restituito da ogni lettura o notifica
non associata riuscita (codificato in esadecimale), non solo che la lettura è
riuscita - il valore veniva già recuperato, quindi non c'è alcun rischio
aggiuntivo, solo che non veniva più scartato.
--race testa solo la raggiungibilità (una query benigna di informazioni SDK,
nessun accesso alla memoria) - un servizio RACE può essere presente e accettare
la scrittura pulitamente ma comunque non rispondere, il che è un risultato
genuinamente inconcludente, non la prova che qualcosa sia stato risolto. Per una
risposta definitiva, vedi --memory-read sotto.
--bd-address è informativo, non una scoperta di vulnerabilità di per sé:
interroga l'indirizzo Bluetooth Classic (BR/EDR) reale del dispositivo attraverso
lo stesso canale RACE non autenticato, stesso profilo di rischio della query
buildversion di --firmware (un comando di metadati a carico zero). Utile se
vuoi perseguire tu stesso test attivi di CVE-2025-20701 con una radio/dongle
Classic, poiché questo strumento non ha un proprio trasporto Classic - vedi la
sezione Requisiti hardware sotto.
buds_audit.py --memory-read --target AA:BB:CC:DD:EE:FF
Tenta una lettura flash RACE reale e di sola lettura (256 byte, da un indirizzo
fisso) per una conferma definitiva di CVE-2025-20702 - utile quando
--race trova il servizio RACE presente ma non reattivo alla sua query benigna.
Questa è opt-in e separata da --race apposta: un successo qui recupera
contenuto reale del firmware del dispositivo, non solo un segnale sì/no sulla
raggiungibilità del canale. Non scrive mai, non cancella, non estrae chiavi di
associazione, né legge RAM/registri (solo flash, che non ha effetti collaterali
di lettura) - vedi ROADMAP.md, Fase 8 e sezione Fuori ambito per la motivazione
completa. Richiede una propria conferma separata, che descrive esattamente cosa
fa, oltre al prompt standard di proprietà.
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
Esegue le sonde GATT, RACE, firmware e BD-address sopra contro un singolo
target e produce un verdetto unico: PASS, PARTIAL, VULNERABLE o
SUSPECTED_COMPROMISE. Il verdetto e ogni singola scoperta vengono stampati
con un'interpretazione in linguaggio semplice accanto al dettaglio tecnico,
in modo che il risultato sia leggibile senza conoscenze approfondite del
Bluetooth - questo strumento è pensato per chiunque controlli i propri
dispositivi, non solo specialisti della sicurezza. --json scrive inoltre il
risultato completo (informazioni sul dispositivo, verdetto e relativa
spiegazione in linguaggio semplice, flag con prove e relativa glossa in
linguaggio semplice, e note di remediation) in un file.
Aggiungendo --memory-read si integra la conferma di lettura della memoria
nello stesso controllo e verdetto, con il suo proprio prompt di conferma
separato per primo. La query dell'indirizzo BD viene eseguita automaticamente
come parte di --assess (nessun flag separato necessario, nessun prompt di
conferma aggiuntivo) poiché ha la stessa forma di query di metadati a basso
rischio del controllo firmware.
--assess è deliberatamente solo per singolo target, come le sonde
individuali - non esiste una modalità "valuta tutti i dispositivi nel raggio",
poiché ciò significherebbe sondare attivamente dispositivi che potrebbero non
essere tuoi.
buds_audit.py --baseline --target AA:BB:CC:DD:EE:FF
buds_audit.py --check-drift --target AA:BB:CC:DD:EE:FF
--baseline cattura un'istantanea attendibile per un dispositivo la prima
volta che lo valuti - identità (nome e dati del produttore), tabella GATT,
build firmware RACE e stato di associazione locale (solo booleani
paired/trusted/bonded, mai materiale chiave) - e la memorizza in
data/device_baselines.json. Non viene mai catturata automaticamente; devi
richiederlo esplicitamente, e rieseguendolo si sovrascrive la baseline
esistente.
--check-drift ricattura la stessa istantanea e la confronta con la
baseline memorizzata, producendo un verdetto in base a qualsiasi deriva
trovata: IDENTITY_DRIFT, GATT_TABLE_DRIFT, FIRMWARE_DOWNGRADE o
BOND_STATE_DRIFT. Questo risponde a "è cambiato qualcosa da quando mi
fidavo di questo dispositivo", non "questo dispositivo è vulnerabile" - è un
segnale euristico di compromissione, non una prova forense. Un dispositivo
con qualsiasi flag di deriva ottiene un verdetto
SUSPECTED_COMPROMISE, che prevale su tutto il resto.
buds_audit.py --watch
Esegue una scansione continua in finestre di lunghezza fissa (Ctrl+C per
fermare) e correla ogni annuncio visto per nome e dati del produttore. Se
due indirizzi diversi trasmettono la stessa identità con finestre di
osservazione sovrapposte - cioè entrambi erano in onda con quell'identità
contemporaneamente - segnala POSSIBLE_IMPERSONATION. Un singolo
dispositivo fisico che ruota il proprio indirizzo BLE nel tempo (visto in
sequenza, non contemporaneamente) non viene segnalato; solo un vero secondo
trasmettitore viene rilevato. Si mappa all'ultimo passo del modello di
minaccia: impersonare gli auricolari verso il telefono della vittima.
data/affected_devices.json è un catalogo curato, non un elenco esaustivo.
Attualmente confermati:
| Marca | Modello | SoC Airoha | CVE | Firmware corretto |
|---|---|---|---|---|
| Sony | WF-1000XM3 | AB1562 | CVE-2025-20700, CVE-2025-20701, CVE-2025-20702 | Nessuno rilasciato |
Secondo la divulgazione di ERNW, anche altri marchi che utilizzano SoC della
serie Airoha AB1562/AB1565/AB1568 (tra cui Bose, Jabra, JBL, Marshall e
modelli Beats precedenti alla patch) sono segnalati come vulnerabili, ma non
sono ancora presenti nel catalogo poiché i loro prefissi di indirizzo e i
dettagli del chipset non sono stati confermati su hardware reale in questo
progetto. Un dispositivo non presente nel catalogo può comunque essere sondato
attivamente con --gatt/--race/--firmware/--assess - il catalogo
influisce solo sulla corrispondenza passiva di --scan e sulla
ponderazione del verdetto, non su ciò che le sonde stesse testano.
Questo strumento valuta CVE-2025-20701 (mancata applicazione dell'associazione Bluetooth Classic) solo passivamente, tramite il controllo della versione build firmware RACE. Testare attivamente se una negoziazione di associazione silenziosa può essere completata richiede l'accesso HCI grezzo tramite Bumble e un dongle Bluetooth USB dedicato compatibile con Bumble - non realizzabile tramite BlueZ/bleak, motivo per cui questo strumento non tenta di farlo. Vedi race-toolkit di ERNW per un'implementazione di riferimento interattiva basata su dongle che copre tutte e tre le CVE.
La catena di vulnerabilità dell'SDK Airoha (CVE-2025-20700 / CVE-2025-20701 /
CVE-2025-20702) è stata scoperta e divulgata da Dennis Heinze e Frieder
Steinmetz di ERNW. Il loro race-toolkit
è l'implementazione di riferimento accanto alla quale questo progetto colma
una lacuna senza dongle, e gli UUID GATT esatti del protocollo RACE e la
struttura dei pacchetti utilizzati qui sono stati letti direttamente dal suo
codice sorgente anziché indovinati - vedi core/race.py per i dettagli.
race-toolkit non è concesso in licenza (nessun file LICENSE, verificato
direttamente sul repository) - nulla del suo codice sorgente viene riutilizzato
qui al di là dei fatti del protocollo sottostante (UUID, struttura dei
pacchetti, codici comando), che descrivono il protocollo stesso di Airoha e
non sono un'espressione originale dei suoi autori da licenziare in primo
luogo.
MIT - vedi LICENSE.
venv/bin/ruff check . --fix && venv/bin/ruff format .
venv/bin/python -m pytest tests/