Contadores Inteligentes – Una prueba de concepto: secuestrando un contador
Tabla de contenidos
En los artículos anteriores se han recabado evidencias suficientes (tanto desde la teoría como la observación pasiva del tráfico PLC) para demostrar que el protocolo PRIME es vulnerable debido a una combinación de un protocolo obsoleto con defectos de diseño y una configuración no adecuada de la seguridad de las comunicaciones. Esta situación impide que una simple actualización del firmware por parte de los fabricantes (tanto de contadores como de los concentradores) pueda solventar estas deficiencias sin romper la compatibilidad con PRIME 1.3, por lo que cualquier medida de mitigación deberá llevarse a cabo por parte de las distribuidoras. La pregunta que cabe hacerse ahora es, ¿cómo de explotables son estas vulnerabilidades?
En el presente artículo se pondrán a prueba algunas de las técnicas enumeradas en el artículo anterior con el fin último de secuestrar un contador inteligente y manipularlo remotamente. Para la efectuación de estas pruebas se ha modificado el firmware de las placas de test de Microchip para permitir el control del hardware por puerto serie. Este nuevo firmware aceptará órdenes en forma de tramas binarias describiendo comandos como la presentación de una trama PRIME, la modificación del contenido del LCD o el cambio de estado de los LEDs de la placa.
En el otro extremo del enlace se encontrará un portátil con una distribución de GNU/Linux ejecutando el software PLCHack, una herramienta desarrollada por Tarlogic como apoyo a las labores de auditoría de este tipo de redes. PLCHack interactuará con el puerto serie e intercambiará con la placa de Microchip las distintas tramas PRIME que se reciban o se deban transmitir a la red PLC.
Una aproximación ingenua: anunciando un nuevo concentrador
Más allá de las técnicas de jamming y denegación de servicio usuales, el ataque más simple que se puede plantear consiste en hacerse pasar por un concentrador (distinto a cualquier otro), anunciarse a la red mediante varias tramas de tipo BEACON y esperar a que algún concentrador se enganche a él. Las tramas de tipo BEACON, de acuerdo a la especificación de PRIME 1.3, tienen la siguiente estructura:
Donde se espera un orden de bytes de red (big endian), y donde el bit más significativo aparece siempre a la izquierda. La cabecera MAC de estas tramas se reduce a la inicialización de los dos bits de HDR.HT a 2 (102) para identificarla como una trama tipo BEACON. El resto de campos se deben inicializar como sigue:
Campo | Descripción |
BCN.QLTY | 3 bits indicando la calidad de la conexión con el nodo base (en el caso de que este beacon sea transmitido a un switch). En nuestro caso, al hacernos pasar por un nodo base, lo inicializaremos a 7 (calidad excelente). |
BCN.SID | Identificador de este switch en la red. Como nos hacemos pasar por un concentrador, lo estableceremos a 0. |
BCN.CNT | Número de beacons en esta trama. Como sólo hay un beacon, se establecerá a 1. |
BCN.POS | Slot empleado en esta trama. Para beacons provenientes de concentradores, este campo debe valer 0. |
BCN.CFP | Offset del inicio del período libre de contención. No ofreceremos ninguno, o sea que estableceremos este campo a cero. |
BCN.LEVEL | Nivel en la jerarquía de nodos base y switches. Como somos un concentrador, este nivel será igual a 0. |
BCN.SEQ | Número de secuencia de este beacon. Se incrementa en una unidad por cada beacon transmitido. |
BCN.FREQ | Frecuencia con la que se envían estas tramas. Elegiremos una frecuencia de 0 para indicar que se envía un beacon en cada trama. |
BCN.SNA | Dirección MAC del concentrador. Debemos ser consistentes con este valor (podemos poner algo como aa:aa:aa:aa:aa:aa) |
BCN.UPCOST
BCN.DNCOST |
Coste de envío o recepción de una trama desde este switch hasta el nodo base. Como nos estamos haciendo pasar un concentrador, en ambos casos será 0. |
CRC | Suma de comprobación de esta trama, con el polinomio de CRC G(x) = x32+x26+x23+x22+x16+x12+x11+x10+x8 +x7 +x5 +x4 +x2 +x+1 |
Al mismo tiempo que inyectamos esta trama debemos interceptar las distintas tramas que se reciben por la red, a la espera de una trama de control tipo REG. Estas tramas son enviadas por los nodos de servicio que intentan asociarse a una estación base, y cuya estructura es la siguiente:
Los campos REG.SNK y REG.AUK sólo son relevantes para registros empleando el Perfil 1.
Resultados
Desgraciadamente sólo se ha podido capturar una solicitud de registro tras un ciclo entero de 24 horas, y no se ha podido reproducir desde entonces. Es posible que debido a la simplicidad del ataque no se estén respetando los timings de los beacons, lo cual descartaría la mayor parte de las mismas. Esto nos lleva a una segunda iteración del ataque:
Un poco más difícil: inyectando tráfico
En una segunda aproximación, podríamos aprovechar el estado de una asociación existente para inyectar tráfico al contador. Para determinar ese estado debemos partir de una observación pasiva durante cierto período de tiempo (en un ciclo de 24 horas cabe esperar múltiples intercambios de tramas de datos con el concentrador) y conseguir por lo menos los siguientes parámetros:
- NID (SID + LNID) del contador.
- LCID de la conexión (casi siempre 0100)
- LEVEL (nivel de profundidad en el árbol de switching)
- SNA de la subred en la que se encuentra (los 6 bytes de la MAC del concentrador).
- PKTID del último paquete de datos, el cual identifica el paquete que se está enviando.
- ACKID del último paquete de datos, el cual identifica el siguiente paquete que se espera por parte del otro extremo de la comunicación.
Algunos de estos parámetros se encuentran distribuidos en los múltiples niveles de encapsulación de las tramas PRIME de datos, mientras que otros se deben deducir de los mismos. En particular:
Data payload (solicitudes y respuestas DLMS) |
SSCS (IEC 61334-32’s Connectionless LLC Sublayer) |
SAR (parte común de la subcapa de convergencia) |
ARQ bytes (PKTID y ACKID) |
PRIME DATA GPDU (NID, LCID, LEVEL) |
Nótese que en este diagrama no se encuentra el SNA: este valor se emplea de forma implícita en el CRC de la trama, concantenando el SNA al contenido de la misma.
Además de los valores enumerados, necesarios para inicializar las distintas cabeceras de esta encapsulación, existen otros tantos que deben ser inicializados adecuadamente:
- El SAR (Segmentation and Reassembly) forma parte de la parte común de la capa de convergencia. Consiste en un byte que proporciona información de fragmentación acerca del payload del paquete. Como nuestras tramas van a ser en general pequeñas, este byte se puede inicializar a 0, indicando el primero de un único fragmento.
- La subcapa IEC 61334-4-32 proporciona servicios de LLC, y forma parte de la parte específica de la capa de convergencia (SSCS). La especificación de esta subcapa es de pago (~300€), por lo que se deberá inicializar en base a observaciones experimentales.
El estudio de las comunicaciones del protocolo ha servido para determinar que la subcapa de convergencia en las pilas PRIME + DLMS/COSEM consiste en tres bytes, de los cuales el primero es siempre 0x90, y los dos siguientes dependen tanto del sentido como de la intencionalidad del tráfico intercambiado. En particular:
- Para tráfico destinado a consultas de solo lectura (y esto incluye tanto la autenticación DLMS como todos los mensajes intermedios hasta la finalización de las consultas con un mensaje de liberación), los dos bytes restantes tienen el significado de “dirección de origen” y “dirección de destino” respectivamente. Estas direcciones son asignadas a nivel de LLC por un procedimiento que no se conoce. La dirección del concentrador es siempre 01.
- Para tráfico destinado a la realización de consultas de lectura y escritura (incluyendo la ejecución de métodos DLMS) ambos bytes deben ser 01, tanto en el tráfico de subida como en el de bajada.
Despejando las incógnitas
La única forma de determinar si un paquete de datos de cierta subred ha llegado correctamente es conociendo la dirección de subred del mismo (SNA), la cual coincide siempre con los 6 bytes de la MAC del concentrador. PLCHack es capaz de averiguar la SNA escuchando de forma pasiva beacons de concentradores y computando CRCs en base a las SNAs observadas en los beacons que ha recibido. Como los beacons pueden tardar varios segundos en llegar, también se contempla la definición manual.
Ante la recepción de cualquier trama PRIME de datos válida y correctamente asociada a su subred, la obtención del NID, LCID, LEVEL, PKTID y ACKID del paquete que se quiere inyectar es directa. Existen raros casos en los que PKTID y ACKID pueden no aparecer (lo cual solo sucede si tanto contador como concentrador han acordado no emplear mecanismos ARQ, lo cual a día de hoy no se ha observado). En el resto de casos, inmediatamente después de haber obtenido estos parámetros, se puede (y se debe) construir la trama PRIME deseada, calcular su CRC en base a su SNA e inyectarla directamente al medio. Lo único a tener en cuenta llegados a este punto es que, a diferencia del resto de parámetros, la simple copia del PKTID y del ACKID no basta: estos parámetros están relacionados con la secuenciación del tráfico de datos, y deben evolucionar con cada trama intercambiada:
- Si la trama de datos recibida viene del concentrador (bit HDR.DO de la cabecera igual a 1), el ACKID de la trama que se quiere inyectar debe ser igual al ACKID de la trama capturada. Esto se debe a que el siguiente PKTID que se espera del contador sigue siendo ACKID. Sin embargo, el PKTID de la trama inyectada debe ser igual al PKTID de la trama recibida + 1. Otra forma de verlo es que, puesto que estamos suplantando al concentrador, basta con capturar la última trama de datos enviada por este, sustituir su payload e incrementar su PKTID.
- Si la trama de datos viene del contador (bit HDR.DO de la cabecera igual a 0), la situación se invierte: la trama del contador indica que el siguiente paquete de datos que se espera debe tener un PKTID igual al ACKID de la trama capturada, y al revés, el ACKID de la trama que se quiere inyectar debe ser igual al PKTID de la trama capturada + 1.
El comando seqsnoop de PLCHack es capaz de mostrar este conjunto de parámetros por cada trama de datos capturada y, opcionalmente, sugerir otros comandos de PLCHack para inyectar tramas en base a estos parámetros:
Procediendo con la inyección
Los comandos sugeridos por seqsnoop incluyen todo el conjunto de parámetros necesarios para inyectar un determinado tipo de mensaje DLMS. Estos comandos son:
- authenticate: Envía un mensaje al contador para escritura, con una contraseña dada. En términos de DLMS, está enviando un mensaje AARQ.
- cut: Envía una orden de corte de suministro, abriendo el interruptor interno del contador. En términos DLMS, es un ACTION_REQUEST hacia un método específico del objeto COSEM relacionado con el control de desconexión.
- connect: Envía una orden de restablecimiento de suministro, cerrando el interruptor interno del contador. Se trata de otra ACTION_REQUEST hacia el mismo objeto COSEM empleado por el comando cut, variando simplemente los parámetros del método llamado.
En la siguiente captura se puede observar el empleo de los comandos seqsnoop, authenticate, cut y connect para provocar un corte temporal del suministro.
Yendo más allá
El problema del ataque planteado en el punto anterior tiene que ver con la necesidad de una observación pasiva del tráfico de datos, el cual puede tardar bastante tiempo en aparecer. Una estrategia un poco más interactiva consiste en recibir cualquier tipo de tramas PRIME de tipo genérico (GPDU), asociarlas a una SNA y examinar sus campos LEVEL y NID. En la práctica, el como el único LCID que se observa es el 0x100, los únicos parámetros que queda por determinar son el PKTID y el ACKID. Pruebas de laboratorio han demostrado que, debido al escaso tamaño de estos identificadores (ambos de 6 bits), es posible construir un pequeño programa que recorra todos los ACKIDs y PKTIDs enviando el mismo payload, a la espera de una respuesta del contador. Una vez obtenida la respuesta, se puede reconstruir la totalidad del estado de la conexión, y comenzar una comunicación bidireccional con el otro extremo.
Esto es justamente lo que implementa el comando dataprobe de PLCHack: partiendo del SNA, NID, LCID y LEVEL de las tramas que debe inyectar, comienza a recorrer todos los ACKIDs y PKTIDs en bucle, hasta que se produce una respuesta de datos del otro extremo. Esta respuesta de datos se utiliza para reconstruir el estado de la conexión y comenzar una comunicación DLMS más compleja: se envía un mensaje DLMS de autenticación (AARQ), se espera por una respuesta (AARE) y, si esta ha sido exitosa, se comienza un intercambio de solicitudes de conexión y desconexión del interruptor interno, probado así que el secuestro de un contador es posible. El resultado de la ejecución de este programa en el laboratorio de contadores de PLC de Tarlogic se puede comprobar en el siguiente vídeo:
Descubre nuestro trabajo y nuestros servicios de ciberseguridad en www.tarlogic.com/es/
En TarlogicTeo y en TarlogicMadrid.
Este artículo forma parte de una serie de articulos sobre Contadores Inteligentes
- Contadores Inteligentes – El escenario español y el sistema de Telegestión.
- Contadores Inteligentes – Amenazas a los contadores y ataques PRIME
- Contadores Inteligentes – Una prueba de concepto: secuestrando un contador
- Contadores Inteligentes – Evaluando el riesgo del concentrador
- Seguridad en las redes PRIME – Estado actual
- PLCTool, la navaja suiza de los contadores inteligentes
- Soporte para plugins en PLCTool