Typewritter
Volver al blog

LoRaWAN 1.0, vulnerabilidades y retrocompatibilidad en 1.1

Para entender las mejoras de seguridad introducidas por 1.1, es necesario comprender en profundidad los mecanismos de seguridad descritos en LoRaWAN 1.0.

Como se describió en el artículo anterior, sea cual sea el procedimiento de activación empleado, la comunicación se protege mediante dos claves AES128 de sesión: AppSKey y NwkSKey. Estas claves no se emplean para cifrar en modo bloque sino en modo flujo, a partir de una variación del modo CTR (Counter mode) denominada CCM*.

CTR es un método bien conocido para convertir un cifrado de bloques (como AES128) en un cifrado de flujo. Para ello, se divide el texto en claro en bloques del tamaño aceptado por el cifrado de bloques y cada bloque del texto en claro se combina (por ejemplo, bit a bit mediante XOR) con el bloque resultante de cifrar un contador de bloque y un IV / Nonce a partir de cierta clave que se presupone secreta.

La seguridad de este modo se reduce entonces a la administración adecuada del material criptográfico necesario para generar el flujo de claves.

CCM* emplea el modo CTR para cifrar el contenido del payload utilizando la AppSKey, y autentica el mensaje con un código CMAC en base a la NwkSKey. Los bloques generadores del flujo de claves (Ai) se cifran con una determinada clave (AppSKey o NwkSKey) en función del tipo del paquete, y el resultado se aplica a los bloques de 128 bits que componen el payload del mensaje:

Los subíndices indican el tamaño de cada campo concatenado en bits. Los bytes de los campos mayores de 8 bits se ordenan en modo little endian. El campo D (dirección) es 0 para uplink y 1 para downlink. El contador (Cnt) es el contador de mensaje empleado, en función de la dirección del mismo, y vale 0 al inicio de la sesión. El contador de bloque (i) enumera el bloque AES al que se aplica el resultado del cifrado. La clave K es NwkSKey o AppSKey en función del tipo de paquete MAC del que se trate (control o datos respectivamente).

El mensaje resultante se autentica mediante AES-128-CMAC para generar el código de integridad de mensaje (MIC) mediante la NwkSKey, independientemente del tipo de paquete. Este código de integridad se calcula sobre la cabecera, el payload ya cifrado y cierto bloque B0 concatenado al principio de ambos:

Con idéntico orden de bits. En ambos casos, el material criptográfico involucrado es:

  • Sentido de la trama (uplink / downlink).
  • Dirección (32 bits) del dispositivo.
  • Un contador de mensaje expandido a 32 bits (que en función de la dirección de la trama puede ser de 16 o 24 bits).
  • Una clave AES de 128 bits.

Además de la ausencia de seguridad extremo a extremo mencionada en el artículo anterior, el manejo inadecuado de este material puede comprometer la seguridad de las comunicaciones. Esta y otras debilidades están detalladas en el artículo Security of LoRaWAN v1. 1 in Backward Compatibility Scenarios. Dönmez, T.C.; Nigussie, E. Procedia computer science 2018, 134, 51–58. En particular, en la versión 1.0.2 de la especificación del protocolo, las debilidades encontradas fueron las siguientes:

  • Reutilización de contadores de mensaje: Como se ha visto antes, las tramas en LoRaWAN 1.0 se cifran en modo CTR. Este método no es inherentemente inseguro. Sin embargo, una débil política de actualización de claves puede hacerlo inefectivo. Cuando se usa ABP, el dispositivo suele comunicarse con la red con el contador de mensaje reiniciado a su valor inicial (por lo que el IV es siempre el mismo). Esto motiva un tipo de ataque plaintext llamado crib dragging que, en función del conocimiento que se tenga de la estructura de los mensajes, puede llegar a comprometer toda la comunicación. Evidentemente, cuantas más tramas se capturen, más probabilidad hay de romper este cifrado.
  • Reuso de nonces: Durante el proceso de Join, existe un intercambio de nonces entre el dispositivo final y la red (denominados DevNonce y AppNonce), los cuales se utilizan para evitar ataques de replay. Debido a que la generación de estos nonces no queda adecuadamente registrada, la reutilización de algún nonce no se puede prevenir completamente.
  • Sin mecanismos de protección contra replay de Join-accept: El dispositivo final es incapaz de detectar nonces repetidos durante OTAA, por lo que se pueden efectuar replays de mensajes de tipo Join-accept.
  • Protección contra replay de mensajes de tipo Join-request pobre: El servidor de red solo detecta cierto número N inespecífico de DevNonces, por lo que un atacante solo debe esperar N mensajes antes de realizar un replay de un mensaje Join-request.
  • Los mensajes de tipo ACK no se asocian a mensajes confirmados: En LoRaWAN 1.0.2, los mensajes de tipo confirmación no están asociados a ningún mensaje específico, lo cual podría permitir ataques de replay.
  • Mensajes de tipo Join-accept no se asocian a Join-requests: No hay forma de conectar las solicitudes de Join realizadas en OTAA en LoRaWAN 1.0.2 con su correspondiente accept, más allá de su ocurrencia temporal.
  • Sin mecanismos para la confirmación de cambios de contexto de seguridad: Se entiende por “contexto de seguridad” el conjunto de contadores y claves necesarios para el cifrado y autenticación de las tramas. Cuando se produce un cambio en dicho contexto, el servidor de red lo confirma por su mera presencia en una trama de subida (es decir, desde el dispositivo final). Sin embargo, el dispositivo final no tiene forma de confirmar estos cambios de contexto, y puede quedar desasociado del servidor de red.
  • Sin protección extremo a extremo: Y por último, como ya se comentó en la anterior entrada, debido a que la AppKey está compartida tanto por el servidor de red como por los servidores de aplicación, nada impide al servidor de red interceptar el contenido de las tramas dirigidas a los servidores de aplicación.

Contramedidas

LoRaWAN 1.1 introduce una serie de cambios que corrigen estas vulnerabilidades de distintas maneras:

  • El reuso de contadores se corrige adelantándose al desbordamiento de los mismos (empleando un nuevo mensaje denominado Rejoin request) y, en el caso particular de ABP, almacenando el último valor del contador en memoria no volátil.
  • El reuso de nonces se previene registrando adecuadamente los nonces generados, evitando su reutilización.
  • El replay de los mensajes de tipo Join accept se previene por el dispositivo final manteniendo un valor llamado JoinNonce_last, con el último JoinNonce generado por la red. Si el dispositivo final no observa un JoinNonce incremental comparado con el JoinNonce_last, lo descarta silenciosamente.
  • El replay de los mensajes de tipo Join request se previene por parte de la red manteniendo un valor llamado DevNonce_last, impidiendo DevNonces no incrementales, de un modo equivalente al caso anterior.
  • La prevención contra los mensajes de ack no asociados a ningún mensaje se corrige introduciendo el número de paquetes recibidos por el otro extremo (basado en el FCnt) dentro del propio MIC, acoplando su cálculo a su posición relativa en las comunicaciones.
  • La asociación de Join requests con Join accepts se corrige introduciendo en el cálculo del MIC del Join Accept el DevNonce del correspondiente Join request.
  • La confirmación de cambios de contexto de seguridad se corrigen introduciendo comandos MAC adicionales, primero para solicitar dicho cambio (RekeyInd) y para confirmarlo por el otro extremo (RekeyConf).
  • Y por último, la protección extremo a extremo en el caso de OTAA se consigue proporcionando dos claves distintas para la red (NwkKey) y para la aplicación (AppKey), a partir de las cuales se derivan el resto de claves.

Vuelta a la casilla de salida

Aunque estas medidas individualmente son efectivas, implican cambios en el protocolo que lo hacen fundamentalmente incompatible con LoRaWAN 1.0. Si un dispositivo 1.1 quiere comunicarse con una red 1.0, el dispositivo debe soportar 1.0. Del mismo modo, si una red 1.1 quiere ofrecer soporte a dispositivos 1.0 (que siguen siendo la mayoría del mercado), debe hacer un downgrade a 1.0.

Algunas de las contramedidas que aplican únicamente a las políticas de tiempo de ciclo de vida de las claves pueden utilizarse en 1.0 (como la prevención del reuso de contadores). Sin embargo, esto solo es posible del lado del dispositivo final, cuando el dispositivo es 1.1 y la red es 1.0. En el otro sentido, lo único que puede hacer la red es bloquear el acceso al dispositivo cuando su estado de seguridad está siendo reutilizado. Se concluye por lo tanto que la seguridad de una red LoRaWAN 1.1 solo se puede conseguir si se asegura que no existan elementos 1.0 en la misma.

Un ejemplo práctico

Una de las fallas más críticas de LoRaWAN 1.0 tiene que ver con el reuso del flujo de claves, lo cual sucede cuando el valor empleado como IV (el contador de paquete) en el modo CTR de AES se reinicia. En el caso de ABP, esto sucede cuando el dispositivo se reinicia y no mantiene el último valor de contador. En el caso de OTAA, cuando los contadores sufren un desbordamiento (lo cual es estadísticamente más improbable). En el caso particular de ABP, las características del tráfico cifrado (medidas de sensores) lo hacen especialmente vulnerable.

Para ilustrar el alcance de esta vulnerabilidad, vamos a suponer un escenario bastante común: un conjunto de sensores simples (humedad, temperatura, etc) gestionados por un microcontrolador, que reenvía las medidas mediante una pequeña placa LoRa, empleando LoRaWAN 1.0/ABP para la activación y alimentado de forma autónoma (sea por placa solar o por batería).

Podemos entonces ejecutar una observación continuada del tráfico empleando un dispositivo como el HT-M01, una pasarela LoRa normal y corriente, controlador por emulador serie USB integrado.

HT-M01: Un gateway LoRa conectado a una antena casera de 868 MHz.

HT-M01: Un gateway LoRa conectado a una antena casera de 868 MHz.

El HT-M01 es un dispositivo originalmente concebido para el despliegue de redes LoRaWAN, actuando como pasarela entre los dispositivos finales y el resto de la infraestructura de red. Sin dicha estructura, el HT-M01 actúa como un auténtico sniffer LoRa (no necesariamente LoRaWAN), redireccionando las tramas demoduladas a otro host mediante UDP. Estas tramas y sus metadatos se codifican en JSON, facilitando su manipulación desde lenguajes de script, y tienen el siguiente aspecto:

{"rxpk":[{"tmst":3881858452,"chan":1,"rfch":1,"freq":868.300000,"stat":1,"modu":"LORA","datr":"SF7BW125","codr":"4/5","lsnr":9.5,"rssi":-27,"size":17,"data":"QOGUkACAAAAB5pyQk6nCOeM="}]}

Donde el campo data es el payload PHY de la trama LoRa capturada, codificada en base 64. El resto de campos especifican información como el timestamp de la recepción, la modulación, la tasa de codificación, relación señal/ruido y demás.

Lo interesante de los paquetes LoRaWAN es que, aunque el payload puede estar cifrado, las cabeceras no lo están. Estas cabeceras están bien documentadas y se distribuyen del siguiente modo:

De entre todas estas cabeceras, el campo FCnt es de especial importancia al proporcionar el contador de trama, empleado como IV para el cifrado del FRMPayload. Esto permite clasificar las tramas en base a su contador (FCnt) y al dispositivo (DevAddr) del que provienen.

Si el dispositivo se reinicia (sea porque se agota la batería, porque deja de recibir luz solar o, en el caso de que la batería se cargue mediante la placa solar, esta se acabe viciando), los contadores volverán a 0 y el keystream se reiniciará. Si las claves de la activación ABP no han sido modificadas después de varios reinicios del contador (digamos, cada día por la mañana, cuando la placa solar vuelve a alimentar la placa), las tramas clasificadas podrían tener el siguiente aspecto:

FCnt: 0000
    FRMPayload: e6 9c 90 93 
    FRMPayload: e6 9c 90 fd 
    FRMPayload: e6 9c 90 f5 
    FRMPayload: e6 9c 90 de 
FCnt: 0001
    FRMPayload: b2 e1 27 4c 
    FRMPayload: b2 e1 27 3c 
    FRMPayload: b2 e1 27 36 
    FRMPayload: b2 e1 27 2b 
FCnt: 0002
    FRMPayload: c0 bd cb 9b 
    FRMPayload: c0 bd cb f2 
    FRMPayload: c0 bd cb e0 
    FRMPayload: c0 bd cb ff 
FCnt: 0003
    FRMPayload: 4e cb ed 10 
    FRMPayload: 4e cb ed 02 
    FRMPayload: 4e cb ed 6e 
    FRMPayload: 4e cb ed 39

Lo cual nos permite concluir varias cosas. La primera, para un mismo valor del contador, los bits más significativos de las capturas no parecen cambiar. Esto es por un lado esperable al estar reutilizando el mismo keystream, y por el otro compatible con cabeceras de tipo Cayenne LPP, en las que los dos primeros bytes indican el canal y el tipo de dato respectivamente y, por tanto, tampoco van a cambiar. Los dos últimos bytes se corresponden entonces con el dato a transmitir, lo cual sugiere además un entero de 16 bits y big endian ya que se observa más variabilidad en el byte más a la derecha.

Los ataques tradicionales a cifrados en modo CTR con reutilización del keystream se basan en aplicar crib dragging con texto plano conocido, o aplicar correlaciones de distinto tipo. En este caso tal cosa no es tan fácil: los datos posiblemente provengan de un sensor, pueden ser valores numéricos en cualquier rango y pueden estar afectados por ruido. Aun así, y por el hecho de ser un sensor, podemos explotar una propiedad de estos datos para romper algunos bits de la clave: un sensor (sea de temperatura, humedad, nivel de agua, etc) no espera cambios extremadamente bruscos entre medidas consecutivas, más allá del ruido.

Esto es algo que se justifica a partir de la naturaleza de dichas magnitudes (por ejemplo, si se hace una medida de la temperatura cada minuto, es muy improbable que esta esté variando aleatoriamente entre 0 y 100 grados de una medida a la siguiente). Del mismo modo, si se representan los 32 bits de las tramas cifradas como enteros a lo largo de una serie de observaciones en una gráfica, se obtendrá algo como esto:

Es decir, una señal con una dispersión de cerca de 4·109 unidades (aproximadamente el rango de valores cubiertos por un entero de 32 bits), algo fundamentalmente distinto de lo que se espera de una magnitud como la temperatura, las coordenadas de un GPS, o la humedad del aire.

 

En una primera aproximación (naif) al problema, se pueden comparar los bits de las tramas de diferentes días pero con el mismo contador y estimar la probabilidad de que sean uno o cero en base a dichas comparaciones. Los bits que son siempre 0 o 1 tendrán probabilidad 1, mientras que los bits que experimentan cierta variación (los más a la derecha) tendrán una probabilidad intermedia. Si se compone un flujo de claves provisional con ceros o unos en función de la probabilidad de bit, eliminamos los efectos de la clave en los bits más significativos pero menos variables. Es importante tener en cuenta que este primer flujo de claves no tiene por qué parecerse en absoluto al original: simplemente servirá para anular los bits que menos cambian y poder observar mejor los bits cifrados en los que se codifica la información real.

Para esta prueba de concepto, se asumirá que el dispositivo está enviando medidas de la temperatura, aunque en principio no tendríamos que saber qué tipo de magnitud es más allá de que sea la medida de un sensor. Al aplicar este flujo de pseudo-claves a las medidas cifradas, gran parte de la variabilidad observada desaparece, y se obtiene una gráfica como la siguiente:

Lo cual sugiere que solo están variando aproximadamente 5 bits de las medidas (puesto que los valores están entre 0 y 25, y el valor más pequeño observado es 1). Estos son los bits del flujo de claves que potencialmente se podrán averiguar en cada trama.

Aunque en principio no se tiene ninguna pista del valor real de estos bits, se puede aplicar la siguiente hipótesis de trabajo: la variación de las medidas a lo largo del tiempo es suave, por lo que el valor absoluto de la derivada de las medidas descifradas a lo largo del día debe ser pequeña. Esto es lo contrario a lo que se observa a las medidas cifradas incluso eliminando el efecto de los bits del MSB (ver arriba), donde la variabilidad es demasiado grande. Esto motiva reformular el ataque a estos datos como un problema de optimización sin gradiente: la clave es aquel conjunto de bits que, aplicando mediante XOR a las medidas cifradas, minimiza una medida del valor absoluto de sus derivadas:

Donde Si son el conjunto de enteros de 32 bits que componen el flujo de algún flujo de claves, Ki el flujo de claves candidato y Cij el conjunto de medidas cifradas tras eliminar los efectos de los bits menos variables a lo largo de un día y durante varios días (por ejemplo, C23 representaría la medida cifrada dentro de la trama con FCnt 2 en el día 3 empezando en 0).

La función de coste U(Si, Cij) es simplemente una medida de la dispersión de las derivadas de las curvas Cij tras aplicarles un XOR con el flujo de claves Si:

Con N el número de días en los que se hizo una observación del tráfico con un reinicio del contador, y M el número de tramas capturadas cada día.

La implementación del optimizador puede hacerse de varias maneras. Para esta prueba se ha elegido un enfriamiento simulado por su simplicidad, aunque posiblemente existan mejores alternativas. La implementación de la prueba de concepto simula un conjunto de medidas de temperatura (360) a lo largo de varios días (100) que varían suavemente con distintas amplitudes, retardos, medias e incertidumbres:

Medidas de temperatura simuladas a lo largo de 100 días. Cada curva representa la variación de la temperatura a lo largo de un día.

Medidas de temperatura simuladas a lo largo de 100 días. Cada curva representa la variación de la temperatura a lo largo de un día.

Acto seguido, las cifra con el mismo flujo de claves aleatorio y aplica el optimizador descrito antes. Se ha podido comprobar que, si las medidas hechas a lo largo de distintos días son lo suficientemente diferentes, esta técnica es capaz de recuperar las medidas originales, con todo el error condensado en el bit menos significativo de las medidas:

Resultados de esta técnica, de izquierda a derecha y de arriba abajo: 1) medidas originales, sin cifrar. 2) Medidas cifradas, tras corregir el efecto de los MSB menos variables. 3) Medidas candidatas, a la salida del optimizador. 4) Error absoluto, comparado como la diferencia entre las medidas cifradas y descifradas.

Resultados de esta técnica, de izquierda a derecha y de arriba abajo: 1) medidas originales, sin cifrar. 2) Medidas cifradas, tras corregir el efecto de los MSB menos variables. 3) Medidas candidatas, a la salida del optimizador. 4) Error absoluto, comparado como la diferencia entre las medidas cifradas y descifradas.

Conclusiones

Las amenazas a la confidencialidad de las comunicaciones LoRaWAN 1.0 no son meramente teóricas: la naturaleza suave de los datos de los sensores puede comprometer gravemente la confidencialidad de las transmisiones y, por lo tanto, el flujo de claves. Aunque el ataque presentado antes es bastante simple y se parece demasiado a un ataque de correlación (ya que explota la regularidad de los datos originales respecto de los cifrados), el autor ha sido incapaz de encontrar referencias a esta técnica en particular en la literatura. Cualquier artículo, manual o publicación en la que se describa en profundidad será bienvenida.

 

Aprovechamos para agradecer a Alfonso Muñoz su ayuda a la hora de discutir los pros y los contras de este ataque.

Más artículos de la serie LoRaWAN

Este artículo forma parte de una serie de articulos sobre LoRaWAN

  1. Ciberseguridad en LoRa y LoRaWAN – Contexto y un poco de historia
  2. LoRaWAN 1.0, vulnerabilidades y retrocompatibilidad en 1.1

Deja un comentario