
Sin dongle, sin root – herramienta de evaluación de seguridad Bluetooth para auriculares inalámbricos afectados por la cadena de vulnerabilidades del SDK de Airoha (CVE-2025-20700/20701/20702)
Versión 1.0.0
Herramienta de evaluación de seguridad Bluetooth para auriculares inalámbricos afectados por la cadena de vulnerabilidades del SDK Airoha (CVE-2025-20700 / CVE-2025-20701 / CVE-2025-20702). Escanea dispositivos cercanos, identifica chipsets Airoha conocidos y vulnerables, y sondea el acceso GATT no autenticado y la accesibilidad del protocolo RACE, todo a través de la pila Bluetooth del sistema operativo (BlueZ) mediante bleak. No se requiere un dongle Bluetooth externo ni permisos de root. Los resultados se informan en lenguaje sencillo junto con el detalle técnico, para que puedas actuar sobre ellos sin conocimientos profundos de Bluetooth.
Esta herramienta está diseñada para evaluar dispositivos que posees o para los cuales tienes autorización explícita para probar. Las sondas GATT y RACE son operaciones activas: se conectan al dispositivo objetivo y le envían comandos. No ejecutes --gatt, --race, --firmware, --bd-address, --assess, --baseline, --check-drift, ni --memory-read contra un dispositivo que no sea tuyo o para el cual no tengas permiso para probar. --scan es pasivo y solo escucha anuncios que ya se están transmitiendo públicamente, por lo que es seguro ejecutarlo contra cualquier dispositivo dentro del alcance.
--memory-read va un paso más allá que las otras sondas activas: recupera una página real de solo lectura (256 bytes) del contenido flash del dispositivo, en una dirección fija, como confirmación definitiva de CVE-2025-20702 cuando la sonda de accesibilidad --race no obtiene respuesta. Es de solo lectura (las lecturas flash no conllevan riesgo de desgaste ni de bloqueo, a diferencia de los comandos de escritura/borrado/FOTA, que esta herramienta nunca envía), es opcional y requiere su propia confirmación separada, más allá del aviso estándar de propiedad, que describe exactamente lo que hace antes de ejecutar cualquier cosa.
Sondear un dispositivo cercano arbitrario no es solo una cuestión de política; puede tener efectos secundarios reales. --gatt intenta una lectura o suscripción a notificaciones en cada característica que encuentra, y algunos dispositivos de consumo exponen servicios de aprovisionamiento (por ejemplo, el servicio Fast Pair de Google) que reaccionan iniciando un handshake de emparejamiento real en el dispositivo objetivo, independientemente de lo que esta herramienta solicite explícitamente. Una característica que requiera cifrado puede desencadenar lo mismo incluso contra tu propio dispositivo, ya que BlueZ puede enrutar silenciosamente esa solicitud de autenticación al agente que tenga registrado tu escritorio (por ejemplo, el aviso de emparejamiento de KDE); por lo tanto, cada comando activo también registra su propio agente BlueZ temporal que rechaza automáticamente cualquier solicitud de este tipo durante la duración de la sonda, de modo que no pueda aparecer ningún aviso de emparejamiento. Cada comando activo también solicita confirmación de que la dirección objetivo es tuya antes de realizar cualquier acción en la radio; pasa --yes para omitir el aviso en uso mediante scripts una vez que hayas confirmado que es tu dispositivo:
buds_audit.py --assess --target AA:BB:CC:DD:EE:FF --yes
--watch es pasivo, como --scan: solo escucha anuncios que ya se están transmitiendo y nunca se conecta a nada, por lo que no solicita confirmación.
python3 -m venv venv
venv/bin/pip install -r requirements.txt
Requiere Python 3.10+ (desarrollado contra 3.14) y un sistema Linux que ejecute BlueZ con un adaptador Bluetooth encendido.
Solo Linux, y no necesariamente todos los sistemas Linux:
bleak en sí mismo tiene un backend para Windows, pero esta herramienta no depende solo de bleak: el descubrimiento Bluetooth Classic (core/scanner.py) y las comprobaciones de estado de vinculación (core/gatt.py) ejecutan directamente bluetoothctl, una herramienta CLI exclusiva de BlueZ que no existe en Windows. Esas rutas de código simplemente fallarían con "comando no encontrado".bluetoothctl en el PATH, no solo cualquier kernel de Linux. La mayoría de las distribuciones de escritorio lo incluyen; una imagen mínima o de servidor sin el paquete bluez instalado no lo tendrá de serie. Verificado sin root en BlueZ 5.86; otras versiones deberían funcionar de la misma manera ya que bleak se dirige a la API D-Bus estándar de BlueZ, pero eso no se ha verificado de forma independiente.usbipd-win, que solo reenvía adaptadores conectados por USB. El Bluetooth integrado de la mayoría de las computadoras portátiles está conectado a través de un bus no USB (SDIO/PCIe, junto con Wi-Fi), que usbipd-win generalmente no puede reenviar, por lo que esto depende completamente del hardware específico.No necesitas una máquina Linux propia; solo necesitas Linux con acceso real a una radio Bluetooth. Dos formas prácticas de obtenerlo:
De cualquier manera, la regla es la misma: la herramienta en sí no cambia; solo necesita Linux con un adaptador Bluetooth que BlueZ pueda alcanzar.
Muchos auriculares TWS dejan de anunciarse (y cierran cualquier conexión activa) después de un período de inactividad para ahorrar energía, y algunos se apagan por completo por sí solos. Si un escaneo no puede encontrar un dispositivo que encontró hace un minuto, o si una sonda falla a mitad de camino, generalmente se debe a que los auriculares entraron en reposo, no a un error; sácalos del estuche o presiona el botón de emparejamiento nuevamente y vuelve a intentarlo.
Esto también afecta la estabilidad de la dirección: la unidad de prueba confirmada de este proyecto (un Sony WF-1000XM3) mantuvo la misma dirección BLE en todos los ciclos de encendido probados, lo cual es esperado para auriculares diseñados para reconexión con la aplicación complementaria; típicamente usan una dirección BLE fija/pública en lugar de una rotatoria (a diferencia de los teléfonos, que sí rotan direcciones privadas y no son un objetivo adecuado para esta herramienta por esa razón). Sin embargo, eso no está garantizado para todos los modelos de auriculares; algunos fabricantes usan direcciones privadas resolubles incluso en modo previo al emparejamiento/reconexión, lo que aparecería como una dirección diferente después de cada ciclo de encendido para un escáner no emparejado como esta herramienta.
La sonda GATT (--gatt y la etapa GATT de --assess) puede necesitar varias reconexiones si el dispositivo tiene características que requieren emparejamiento; cada una hace que BlueZ intente (y el agente propio de esta herramienta rechace) una negociación de emparejamiento real antes de reconectar para reanudar el barrido, e imprime una línea de estado antes de cada intento para que un barrido lento no parezca congelado. Cuando se rechaza dicha suscripción, BlueZ retiene la intención y la reemite en cada conexión posterior a ese dispositivo; para evitar que eso desvíe las reconexiones posteriores, la sonda borra el registro en caché de BlueZ del dispositivo (equivalente a bluetoothctl remove) antes de cada reconexión, de modo que cada intento comience desde un estado limpio. Con eso implementado, los barridos repetidos consecutivos contra el dispositivo de prueba confirmado devuelven el mismo resultado completo cada vez. Una observación anterior (la integridad parecía degradarse durante una sesión de pruebas intensivas y se recuperaba después de un descanso) no ha vuelto a ocurrir desde entonces, y se cree que fue la misma acumulación de estado retenido en lugar de fatiga del dispositivo.
buds_audit.py
Ejecutarlo sin ninguna bandera inicia un menú numerado en lugar de requerir que ya sepas una dirección BLE o qué bandera hace qué:
1) Análisis completo (escanea, ejecuta la auditoría completa de CVE y guarda una línea base)
2) Verificar el estado actual contra una línea base guardada
3) Escanear en busca de dispositivos suplantados/que se hacen pasar por otros
4) Salir
La opción 1 escanea dispositivos cercanos conocidos y afectados, los lista para que elijas por número (en lugar de escribir una dirección MAC), ejecuta la auditoría completa de CVE (igual que --assess, incluyendo la consulta de dirección BD) y guarda una línea base (igual que --baseline) para que futuras ejecuciones puedan detectar cambios. También hace la misma pregunta sobre lectura de memoria que --assess --memory-read responde mediante su aviso de confirmación; responder "sí" aquí incluye la misma lectura real de solo lectura de la página flash RACE descrita anteriormente; responder "no" solo ejecuta la auditoría sin ella, no cancela todo el análisis. La opción 2 lista los dispositivos para los que ya tienes una línea base y vuelve a verificar el que elijas para detectar desviaciones (igual que --check-drift). La opción 3 es --watch. Cada opción sigue pasando por la misma confirmación de propiedad que la interfaz basada en banderas antes de tocar la radio; el asistente es un front-end más amigable sobre exactamente las mismas comprobaciones subyacentes, no una ruta separada y menos cuidadosa.
La interfaz basada en banderas que se muestra a continuación sigue estando disponible para uso con scripts o para quienes ya conocen la dirección a la que quieren apuntar.
Todos los comandos se ejecutan mediante venv/bin/python buds_audit.py.
buds_audit.py --help
Funciona incluso con un simple python3 buds_audit.py --help sin venv ni dependencias instaladas; no importa bleak hasta que se ejecuta un comando que realmente necesita la radio.
buds_audit.py --scan
buds_audit.py --scan --flags-only # solo muestra dispositivos que coinciden con el catálogo de afectados conocidos
buds_audit.py --scan --target AA:BB:CC:DD:EE:FF
Escanea pasivamente dispositivos BLE y Bluetooth Classic cercanos, identifica chipsets Airoha a partir de datos del fabricante y prefijos de dirección, y los contrasta con data/affected_devices.json.
Cada una de estas requiere --target ADDR y es una operación activa contra ese único dispositivo:
buds_audit.py --gatt --target AA:BB:CC:DD:EE:FF # CVE-2025-20700: acceso GATT no autenticado
buds_audit.py --race --target AA:BB:CC:DD:EE:FF # CVE-2025-20702: accesibilidad del canal RACE
buds_audit.py --firmware --target AA:BB:CC:DD:EE:FF # CVE-2025-20701: comprobación pasiva de firmware/evasión de emparejamiento
buds_audit.py --bd-address --target AA:BB:CC:DD:EE:FF # Dirección BD Classic mediante RACE, informativo
Las cuatro se omiten limpiamente (sin error) si el dispositivo ya está emparejado; un hallazgo de "acceso no autenticado" no tiene sentido contra un dispositivo vinculado.
--gatt ahora muestra el valor real devuelto por cada lectura o notificación exitosa sin emparejar (codificado en hexadecimal), no solo que la lectura fue exitosa; el valor ya se estaba recuperando, por lo que esto no es un riesgo adicional, solo que ya no se descarta.
--race solo prueba la accesibilidad (una consulta benigna de información del SDK, sin acceso a memoria); un servicio RACE puede estar presente y aceptar la escritura correctamente pero aun así no responder, lo cual es un resultado genuinamente no concluyente, no evidencia de que algo esté solucionado. Para una respuesta definitiva, consulta --memory-read a continuación.
--bd-address es informativo, no un hallazgo de vulnerabilidad por sí solo: consulta la dirección Bluetooth Classic (BR/EDR) real del dispositivo a través del mismo canal RACE no autenticado, con el mismo perfil de riesgo que la consulta de versión de compilación de --firmware (un comando de metadatos sin carga útil). Útil si deseas realizar pruebas activas de CVE-2025-20701 por tu cuenta con una radio/dongle compatible con Classic, ya que esta herramienta no tiene transporte Classic propio; consulta la sección Requisito de hardware a continuación.
buds_audit.py --memory-read --target AA:BB:CC:DD:EE:FF
Intenta una lectura real de solo lectura de una página flash RACE (256 bytes, desde una dirección fija) para una confirmación definitiva de CVE-2025-20702, útil cuando --race encuentra el servicio RACE presente pero no responde a su consulta benigna. Esto es opt-in y está separado de --race a propósito: un éxito aquí recupera contenido real del firmware del dispositivo, no solo una señal de sí/no sobre si el canal es accesible. Nunca escribe, borra, extrae claves de enlace ni lee RAM/registros (solo flash, que no tiene efectos secundarios de lectura); consulta la Fase 8 y las secciones Fuera del alcance de ROADMAP.md para obtener el razonamiento completo. Requiere su propia confirmación separada, que describe exactamente lo que hace, más allá del aviso estándar de propiedad.
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
Ejecuta las sondas GATT, RACE, firmware y dirección BD anteriores contra un objetivo y produce un veredicto único: PASS, PARTIAL, VULNERABLE o SUSPECTED_COMPROMISE. El veredicto y cada hallazgo individual se imprimen con una interpretación en lenguaje sencillo junto al detalle técnico, de modo que el resultado sea legible sin conocimientos profundos de Bluetooth; esta herramienta está pensada para cualquiera que verifique sus propios dispositivos, no solo especialistas en seguridad. --json además escribe el resultado completo (información del dispositivo, veredicto y su explicación en lenguaje sencillo, indicadores con evidencia y su glosa en lenguaje sencillo, y notas de remediación) en un archivo. Agregar --memory-read integra la confirmación de lectura de memoria en la misma auditoría y veredicto, con su propio aviso de confirmación separado primero. La consulta de dirección BD se ejecuta automáticamente como parte de --assess (no se necesita una bandera separada, ni un aviso de confirmación adicional) ya que tiene la misma forma de consulta de metadatos de bajo riesgo que la comprobación de firmware.
--assess está deliberadamente diseñado para un solo objetivo, al igual que las sondas individuales; no hay un modo "evaluar cada dispositivo en el alcance", ya que eso implicaría sondear activamente dispositivos que pueden no ser tuyos.
buds_audit.py --baseline --target AA:BB:CC:DD:EE:FF
buds_audit.py --check-drift --target AA:BB:CC:DD:EE:FF
--baseline captura una instantánea de confianza para un dispositivo la primera vez que lo evalúas: identidad (nombre y datos del fabricante), tabla GATT, compilación del firmware RACE y estado de vinculación local (solo booleanos emparejado/confiable/vinculado, nunca material de clave), y la almacena en data/device_baselines.json. Nunca se captura automáticamente; debes solicitarlo explícitamente, y ejecutarlo nuevamente sobrescribe la línea base existente.
--check-drift vuelve a capturar la misma instantánea y la compara con la línea base almacenada, produciendo un veredicto según la desviación encontrada: IDENTITY_DRIFT, GATT_TABLE_DRIFT, FIRMWARE_DOWNGRADE o BOND_STATE_DRIFT. Esto responde "¿ha cambiado algo desde la última vez que confié en este dispositivo?", no "¿es vulnerable este dispositivo?"; es una señal heurística de compromiso, no una prueba forense. Un dispositivo con cualquier indicador de desviación recibe un veredicto SUSPECTED_COMPROMISE, que anula todo lo demás.
buds_audit.py --watch
Escanea continuamente en ventanas de longitud fija (Ctrl+C para detener) y correlaciona cada anuncio visto por nombre y datos del fabricante. Si dos direcciones diferentes transmiten la misma identidad con ventanas de observación superpuestas, lo que significa que ambas estaban en el aire con esa identidad al mismo tiempo, marca POSSIBLE_IMPERSONATION. Un solo dispositivo físico que rota su dirección BLE con el tiempo (visto secuencialmente, no simultáneamente) no se marca; solo se marca un segundo transmisor genuino. Se asigna a la etapa final del modelo de amenaza: suplantar los auriculares ante el teléfono de la víctima.
data/affected_devices.json es un catálogo curado, no una lista exhaustiva. Actualmente confirmados:
| Marca | Modelo | SoC Airoha | CVEs | Firmware parcheado |
|---|---|---|---|---|
| Sony | WF-1000XM3 | AB1562 | CVE-2025-20700, CVE-2025-20701, CVE-2025-20702 | Ninguno publicado |
Según la divulgación de ERNW, otras marcas que usan SoCs de la serie Airoha AB1562/AB1565/AB1568 (incluyendo Bose, Jabra, JBL, Marshall y modelos Beats anteriores al parche) también se reportan como afectadas, pero aún no están en el catálogo ya que sus prefijos de dirección exactos y detalles del chipset no se han confirmado con hardware real en este proyecto. Un dispositivo fuera del catálogo aún puede ser sondado activamente con --gatt/--race/--firmware/--assess; el catálogo solo afecta la coincidencia pasiva de --scan y la ponderación del veredicto, no lo que las sondas mismas prueban.
Esta herramienta evalúa CVE-2025-20701 (falta de aplicación del emparejamiento Bluetooth Classic) solo de forma pasiva, mediante la comprobación de la versión de compilación del firmware RACE. Probar activamente si se puede completar un handshake de emparejamiento silencioso requiere acceso HCI sin procesar a través de Bumble y un dongle Bluetooth USB dedicado compatible con Bumble; esto no se puede lograr mediante BlueZ/bleak, por lo que esta herramienta no lo intenta. Consulta el race-toolkit de ERNW para una implementación de referencia interactiva basada en dongle que cubre los tres CVEs.
La cadena de vulnerabilidades del SDK Airoha (CVE-2025-20700 / CVE-2025-20701 / CVE-2025-20702) fue descubierta y divulgada por Dennis Heinze y Frieder Steinmetz en ERNW. Su race-toolkit es la implementación de referencia junto a la cual este proyecto llena un vacío sin dongle, y los UUIDs exactos del protocolo RACE GATT y el encapsulado de paquetes utilizados aquí se leyeron directamente de su fuente en lugar de adivinarse; consulta core/race.py para más detalles. race-toolkit no tiene licencia (sin archivo LICENSE, verificado directamente contra el repositorio); no se reutiliza nada de su fuente más allá de los hechos subyacentes del protocolo (UUIDs, diseño de estructuras, códigos de comando), que describen el protocolo propio de Airoha y no son la expresión original de sus autores para licenciar en primer lugar.
MIT - consulta LICENSE.
venv/bin/ruff check . --fix && venv/bin/ruff format .
venv/bin/python -m pytest tests/