
Vulnerabilidades de inyección de teclado en presentadores inalámbricos
Tenía ganas de hacer un poco de ingeniería inversa de RF, así que pedí algunos clickers de presentación y me divertí un poco.
Esto es un fork de nrf-research-firmware (que escribí hace unos años en Bastille). He añadido soporte para algunos transceptores/protocolos nuevos, e incluido POCs de inyección de pulsaciones de teclado para 13 clickers de presentación comunes.
| Fabricante | Modelo | Protocolo | RFIC | Añadido |
|---|---|---|---|---|
| AmazonBasics | P-001 | AmazonBasics P-001 | nRF24 | 2019-04-20 |
| Canon | PR100-R | Canon PR100-R | PL1167 | 2019-04-20 |
| Funpick | Wireless Presenter | HS304 | HS304 | 2019-04-20 |
| AMERTEER | Wireless Presenter | HS304 | HS304 | 2019-04-20 |
| BEBONCOOL | D100 | HS304 | HS304 | 2019-04-20 |
| ESYWEN | Wireless Presenter | HS304 | HS304 | 2019-04-20 |
| Red Star Tech | PR-819 | HS304 | HS304 | 2019-04-20 |
| DinoFire | D06-DF-US | HS304 | HS304 | 2019-04-20 |
| TBBSC | DSIT-60 | TBBSC DSIT-60 | BK2451 | 2019-04-21 |
| Rii | Wireless Presenter | Rii Wireless Presenter | BK2451 |
Este es el protocolo estándar de teclado Logitech cifrado, tal como lo usa el R500. Es, en cierto modo, vulnerable al ataque de “inyección de pulsaciones cifradas” que documenté con los teclados Logitech Unifying como parte del proyecto MouseJack.
Digo en cierto modo porque no todos los códigos de escaneo HID son aceptados. En concreto, 0x04-0x1D (A-Z) se reemplazan por 0x00 cuando el dongle envía la trama al ordenador anfitrión, lo que significa que podemos inyectar lo que queramos, siempre que no sea una letra. La otra excepción es que las combinaciones de teclas con ctrl sí están permitidas, incluso cuando incluyen letras.
Una inyección de pulsaciones eficaz a través del R500 requiere que seamos un poco creativos.
Supongamos que el objetivo está en una sesión de bash. En bash, podemos codificar nuestros caracteres en base-8, codificando los comandos que contienen letras y que queremos que el objetivo ejecute.
Por ejemplo, ping google.com se codifica como $'\160\151\156\147' $'\147\157\157\147\154\145\056\143\157\155'. Si enviamos esta cadena a una sesión de bash y después enviamos enter, el comando ping google.com se ejecutará en la máquina objetivo.
Puedes encontrar la dirección de tu Logitech R500 usando nrf24-scanner de la siguiente manera:
sudo ./tools/nrf24-scanner.py -c {2..74..3} -l
Los paquetes deberían verse así:
[2019-04-21 13:11:52.507] 62 5 85:D1:9D:FE:07 00:40:00:08:B8
[2019-04-21 13:11:52.515] 62 5 85:D1:9D:FE:07 00:40:00:08:B8
[2019-04-21 13:11:52.523] 62 22 85:D1:9D:FE:07 00:D3:4D:6F:B6:1B:E6:05:A2:B4:8B:98:F9:C2:00:00:00:00:00:00:00:81
La inyección es posible porque el dongle no aplica el incremento de los contadores AES, por lo que puedes reproducir paquetes. Un paquete enviado OTA se compone de una carga útil USB HID que ha sido cifrada en modo contador AES, seguida del contador. Cuando pulsas un botón en el clicker de presentación, este genera un paquete de tecla presionada y un paquete de tecla liberada. El paquete de tecla liberada son todo 0, lo que nos proporciona un material de clave limpio que podemos reutilizar, aplicar XOR con nuestra propia carga útil y enviarlo OTA.
No debería ser difícil automatizar el proceso de escuchar una pulsación de botón, extraer el segundo paquete (de tecla liberada) y usarlo automáticamente como referencia de material de clave. Pero soy perezoso, así que primero tendrás que capturar tú mismo una pulsación de botón, que se ve así:
[2019-04-21 16:02:56.451] 65 5 85:D1:9D:FE:07 00:40:00:08:B8
[2019-04-21 16:02:56.459] 65 5 85:D1:9D:FE:07 00:40:00:08:B8
[2019-04-21 16:02:56.468] 65 22 85:D1:9D:FE:07 00:D3:E6:7B:35:8C:BB:2C:7D:5B:8B:98:FA:76:00:00:00:00:00:00:00:B9
[2019-04-21 16:02:56.475] 65 5 85:D1:9D:FE:07 00:40:00:08:B8
[2019-04-21 16:02:56.507] 65 5 85:D1:9D:FE:07 00:40:00:08:B8
[2019-04-21 16:02:56.516] 65 22 85:D1:9D:FE:07 00:D3:99:D6:D3:8D:49:25:F5:4D:8B:98:FA:77:00:00:00:00:00:00:00:1A
[2019-04-21 16:02:56.524] 65 5 85:D1:9D:FE:07 00:40:00:08:B8
[2019-04-21 16:02:56.532] 65 5 85:D1:9D:FE:07 00:40:00:08:B8
El segundo paquete de 22 bytes es el paquete de tecla liberada.
Elimina el último byte de la carga útil y copia y pega la cadena en logitech.py, reemplazando KEYUP_REF.
(No debería funcionar sin actualizar KEYUP_REF, pero si funciona, ¡por favor házmelo saber!)
Inyecta la secuencia de pulsaciones codificada en base-8 del comando bash ping google.com en un dongle Logitech R500 concreto (dirección 85:D1:9D:FE:07):
sudo ./tools/r500-injector.py -l -a 85:D1:9D:FE:07
Este es el protocolo Logitech estándar sin cifrar, utilizado por el R400/R800.
Puedes encontrar la dirección de tu Logitech R400/R800 usando nrf24-scanner de la siguiente manera:
sudo ./tools/nrf24-scanner.py -c {2..74..3} -l
Los paquetes deberían verse así:
[2019-04-21 12:58:43.466] 32 0 9D:9E:95:52:07
[2019-04-21 12:58:43.620] 32 10 9D:9E:95:52:07 00:C1:00:00:00:00:00:00:00:3F
Inyecta la secuencia de pulsaciones de prueba en un dongle Logitech R400/R800 concreto (dirección 9D:9E:95:52:07):
sudo ./tools/preso-injector.py -l -f logitech -a 9D:9E:95:52:07
El Rii Wireless Presenter (bolígrafo redondeado) está basado en el BK2451 (que parece ser un clon del nRF24). Esto parece un protocolo genérico, dada la prevalencia de dispositivos hermanos, así que probablemente lo recategorice después de obtener más datos.
Es funcionalmente un teclado inalámbrico sin cifrar, vulnerable a la inyección de pulsaciones.
El Wireless Presenter (bolígrafo redondeado) utiliza nRF24 Enhanced Shockburst a 250Kb/s (sin ACKs, al parecer) y una dirección de 5 bytes. Se observó que el dispositivo que probé permanecía en 2425 MHz, pero parece usar algún tipo de esquema de agilidad de frecuencia. En la práctica, es necesario enviar algunos paquetes de relleno en el canal objetivo antes de enviar los paquetes de pulsaciones.
Sospecho que hay cierto salto de canal, que no he caracterizado, pero apuntar a un solo canal es suficiente para demostrar la inyección de pulsaciones.
Puedes encontrar la dirección de tu Wireless Presenter (bolígrafo redondeado) usando nrf24-scanner de la siguiente manera:
sudo ./tools/nrf24-scanner.py -c 25 -l -R 250K -A 5
Ten en cuenta que el tuyo podría estar en otro canal.
Los paquetes deberían verse así:
[2019-04-21 11:50:33.116] 25 3 6D:8C:01:14:25 4B:51:00
[2019-04-21 11:50:33.212] 25 3 6D:8C:01:14:25 4C:00:00
[2019-04-21 11:50:33.522] 25 3 6D:8C:01:14:25 4D:51:00
[2019-04-21 11:50:33.565] 25 3 6D:8C:01:14:25 4E:00:00
Inyecta la secuencia de pulsaciones de prueba en un dongle Rii concreto (dirección 6D:8C:01:14:25):
sudo ./tools/preso-injector.py -l -f rii -a 6D:8C:01:14:25
El TBBSC DSIT-60 está basado en el BK2451 (que parece ser un clon del nRF24). Hay dispositivos hermanos aparentes (p. ej., este) que no he probado. Por el momento estoy categorizando esto como un protocolo distinto, pero eso probablemente cambiará cuando pruebe el/los dispositivo(s) hermano(s).
Es funcionalmente un teclado inalámbrico sin cifrar, vulnerable a la inyección de pulsaciones.
El DSIT-60 utiliza nRF24 Enhanced Shockburst a 250Kb/s (o al menos un equivalente OTA) con una dirección de 3 bytes. Se observó que el dispositivo que probé permanecía en 2406 MHz, pero no estoy seguro de qué otros canales podría usar.
Puedes encontrar la dirección de tu DSIT-60 usando nrf24-scanner de la siguiente manera:
sudo ./tools/nrf24-scanner.py -c 6 -l -R 250K -A 3
Ten en cuenta que el tuyo podría estar en otro canal, o podría haber una resintonización por agilidad de frecuencia que no he observado.
Los paquetes deberían verse así:
[2019-04-21 10:33:44.264] 6 4 87:02:09 0B:42:00:2B
[2019-04-21 10:33:44.269] 6 4 87:02:09 0B:42:00:2B
[2019-04-21 10:33:52.477] 6 4 87:02:09 01:42:00:28
Inyecta la secuencia de pulsaciones de prueba en un dongle TBBSC DSIT-60 concreto (dirección 87:02:09):
sudo ./tools/preso-injector.py -l -f tbbsc -a 87:02:09
Esto es casi con seguridad un protocolo genérico, pero todavía no he analizado ninguno de los dispositivos hermanos (p. ej., este). Por el momento lo estoy categorizando como un protocolo distinto, pero eso probablemente cambiará cuando pruebe el/los dispositivo(s) hermano(s).
El P-001 está basado en la familia de RFIC nRF24 y es funcionalmente un teclado inalámbrico sin cifrar, vulnerable a la inyección de pulsaciones.
El P-001 utiliza nRF24 Enhanced Shockburst a 2Mb/s con direcciones de 5 bytes y canales 2402-2476.
Puedes encontrar la dirección de tu P-001 usando nrf24-scanner.py.
Pulsar la flecha derecha debería generar paquetes con un aspecto similar a este:
[2019-04-20 12:59:13.908] 27 9 44:CB:66:A3:BE 00:00:00:00:00:00:00:00:01
[2019-04-20 12:59:13.909] 27 9 44:CB:66:A3:BE 00:00:00:00:00:00:00:00:01
[2019-04-20 12:59:13.999] 27 9 44:CB:66:A3:BE 00:00:4E:00:00:00:00:00:01
[2019-04-20 12:59:14.120] 27 9 44:CB:66:A3:BE 00:00:00:00:00:00:00:00:01
[2019-04-20 12:59:14.121] 27 9 44:CB:66:A3:BE 00:00:00:00:00:00:00:00:01
[2019-04-20 12:59:14.211] 27 9 44:CB:66:A3:BE 00:00:4E:00:00:00:00:00:01
Inyecta la secuencia de pulsaciones de prueba en un dongle AmazonBasics P-001 concreto (dirección 44:CB:66:A3:BE):
sudo ./tools/preso-injector.py -l -f amazon -a 44:CB:66:A3:BE
No estoy seguro de si este protocolo es exclusivo del Canon PR100-R, pero como es el único dispositivo que he observado que usa este protocolo, lo dejaré en su propia categoría hasta que los datos sugieran lo contrario.
El PR100-R está basado en el RFIC PL1167 y en un MCU desconocido.
El PR100-R es funcionalmente un teclado inalámbrico sin cifrar, vulnerable a la inyección de pulsaciones.
El PR100-R utiliza un protocolo FSK de 1Mb/s que opera en canales espaciados 5 MHz entre 2406 MHz y 2481 MHz.
Los paquetes están blanqueados y protegidos por un CRC de 16 bits.
No parece que el dongle envíe ACKs de vuelta, con la salvedad de que solo he hecho ingeniería inversa de este protocolo lo suficiente para demostrar la inyección de pulsaciones.
El protocolo parece utilizar un enfoque de agilidad de frecuencia para la selección de canal, y el dongle se asienta en un canal después de que el mando haya transmitido en él durante un cierto número de paquetes. En la práctica, es suficiente transmitir unos segundos de paquetes de relleno antes de transmitir los paquetes de pulsaciones.
Según el formato de paquete, no está claro si este protocolo usa una palabra de sincronización fija o una dirección por dispositivo. Solo examiné una unidad (debido al alto precio), así que no pude validar completamente el formato de paquete.
El script de inyección funciona contra mi PR100-R, pero puede necesitar modificaciones para uso general. Si tienes otro PR100-R y puedes validar esto, ¡por favor házmelo saber!
Inyecta la secuencia de pulsaciones de prueba en un dongle Canon PR100-R cercano:
sudo ./tools/preso-injector.py -l -f canon
El HS304 parece ser un RFIC específico de aplicación para clickers de presentación (o quizás teclados/ratones inalámbricos). El nombre proviene de la cadena de dispositivo USB HAS HS304, y era el mismo para todos los dispositivos de este conjunto.
Se observó que el RFIC era un encapsulado SOP-16 sin marcar, sin diferencias aparentes entre fabricantes.
Los dispositivos basados en HS304 son funcionalmente teclados inalámbricos sin cifrar, vulnerables a la inyección de pulsaciones.
El HS304 es un protocolo FSK de 1Mb/s que opera en tres canales de la banda ISM de 2,4 GHz (2407, 2433, 2463). No parece que el dongle envíe ACKs de vuelta al clicker de presentación, y la entrega de paquetes se asegura transmitiendo cada paquete en cada uno de los tres canales.
En la práctica, también se puede lograr una entrega fiable de paquetes transmitiendo cada paquete varias veces en un solo canal.
Los paquetes están blanqueados y protegidos por un CRC de 16 bits.
No hay esquema de direccionamiento ni emparejamiento, por lo que la inyección de pulsaciones no requiere detección de dispositivos; sin embargo, la ausencia de ACKs impide la detección activa de dongles.
Inyecta la secuencia de pulsaciones de prueba en dongles HS304 cercanos:
sudo ./tools/preso-injector.py -l -f hs304
Recibe y decodifica paquetes enviados por clickers de presentación NS304 cercanos:
sudo ./tools/preso-scanner.py -l -f hs304
| 2019-04-21 |
| Logitech | R400 | Logitech Unencrypted | nRF24 | 2019-04-21 |
| Logitech | R800 | Logitech Unencrypted | nRF24 | 2019-04-21 |
| Logitech | R500 | Logitech Encrypted | nRF24 | 2019-04-21 |