La sécurité du protocole Z-Wave

MISC n° 082 | novembre 2015 | Kevin Bontems
Creative Commons
  • Actuellement 0 sur 5 étoiles
0
Merci d'avoir participé !
Vous avez déjà noté cette page, vous ne pouvez la noter qu'une fois !
Votre note a été changée, merci de votre participation !
Qui n’a jamais rêvé d’apporter un peu d’intelligence dans sa maison ? Gérer son système d’alarme avec des notifications en temps réel, ouvrir/fermer ses volets en fonction de l’heure du jour et des saisons, adapter le chauffage de chaque pièce de sa maison au degré près, disposer de toutes les informations sur sa consommation en énergie et tout cela à distance depuis une plage à l’autre bout du monde… C’est pour réaliser ce rêve que la domotique a été inventée.

1. Introduction

Derrière le terme domotique, on retrouve un ensemble de solutions techniques visant à centraliser et automatiser le contrôle de différents systèmes de sa maison, en s’appuyant sur des capteurs et périphériques variés.

Bien que le concept ne soit pas nouveau, ce n’est que récemment que l’on a vu apparaître les premières solutions convaincanteset simples à mettre en place. De nombreux constructeurs proposent aujourd’hui leur propre «box »domotique,etil ne reste plus pour le consommateur final qu’à choisir sonsystèmeet acheter différents modules en fonction de ses besoins.

Cependant comme nous allons le voir dans cet article, la sécurité n’est pas toujours au rendez-vous.

2. Les scénarios en domotique

À labase de la domotique, on trouve une idée simple : pouvoir définir des scénarios d’automatisation pour son environnement. Ainsi, lors du changement d’état des modules présents sur le réseau, on pourra simplement déclencher une action :

- Si ma porte d’entrée s’ouvre entre 10h et 18h, m’envoyer une notification ;

- Si la luminosité ambiante est trop faible, allumer les lampes ;

- Si une personne connue entre dans la maison, lui lire un message personnalisé.

La plupart des interfaces domotiques permettent de définir des scénarios simples sans faire appel à un langage de programmation, en mettant en placepar exempledes briques visuelles.

Pour les technophiles, il est également possible de créer des scénarios plus élaborés en s’appuyant sur des langages de programmation (LUA, Python, bash…).

Du point de vue d’une personne malveillante, ces scénarios peuvent potentiellement servir de base à une attaque : si une condition peut être influencée, elle peut elle-même entraîner des effets de bords non souhaités (désactiver l’alarme, ouvrir les volets, etc.). Encore faut-il savoir communiquer avec l’environnement ciblé…

3. Une grande variété de protocoles

Derrière le terme « domotique », se cache en réalité une multitude de protocoles de communication. Le protocole « historique » X10, utilisant le réseau électrique pour relayer les signaux, semble aujourd’hui éclipsé par les protocoles radio comme HomeEasy, Z-Wave, Insteon, Plcbus, Zigbee, EnOcean, RTS (Somfy), X2D (DeltaDore France)… et des protocoles plus communs comme le Wifi ou le Bluetooth.

Nous avons décidé de porter notre attention sur le protocole Z-Wave, couramment utilisé et pour lequel une multitude de périphériques compatibles sont disponibles en France. Principalement utilisé pour un réseau domestique, celui-ci se trouve également dans certains hôtels, bateaux de croisière et centres de vacances [1].

4. Le protocole Z-Wave

Le protocole Z-Wave est un protocole propriétaire dédié à la domotique, originellement développé par la société Zen-Sys (rachetée par Sigma Design en 2008). Il utilise des bandes de fréquences différentes en fonction des pays : 868,42 MHz en Europe, 915 Mhz aux États-Unis, 919.82MHz à Hong Kong ou encore 921.42MHz en Australie. Il est réputé pour sa simplicité et sa fiabilité : il permet d’établir un maillage pour relayer les communications radio, et implémente un mécanisme de retour d’état (ACK) permettant de garantir que les messages ont bien été reçus et compris.

Ce protocole offre également l’avantage de pouvoir sécuriserles communications en s’appuyant sur un chiffrement symétrique AES. Il a été retenu par des sociétés comme Orange (HomeLive) [2] et Somfy [3] pour leurs produits de domotique.

Une amélioration de ce protocole, baptisée Z-Wave+, a été présentée par Sigma Design en janvier 2014. Celle-ci est censée améliorer la portée du signal, permettre de réduire la consommation de la puce et augmenter la vitesse de communication.

Le standard n’étant pas ouvert, le SDK (actuellement en version 6.51) n’est accessible qu’aux clients directs de Sigma Design (après signature d’un « non-disclosure agreement »).

Une implémentation open source « OpenZWave » a cependant été développée [4] en analysant le fonctionnement du protocole(plus de 5 années de recherche par rétro-ingénierieont été nécessaires pour arriver à la version 1.0).

Deux types d’équipements sont présents sur un réseau Z-Wave :

- un ou plusieurs contrôleurs ;

- des nœuds esclaves (« nodes ») qui représentent les différents modules du réseau.

Un contrôleur principal permet d'inclure jusqu'à 232 nœuds dans un réseau. Ce réseau est représenté par un identifiant (HomeID) codé sur 32 bits. Il est communiqué lors d'une phase d'inclusion à tous les équipements souhaitant se rattacher au contrôleur. Chaque équipement du réseau se voit en outre attribuer un identifiant propre (NodeId) codé sur 8 bits. Le premier NodeID est toujours 0x01 (généralement attribué au contrôleur), et est automatiquement incrémenté à chaque ajout de module.

Certains nœuds peuvent également être utilisés pour relayer le signal Z-Wave à d’autres. Un tel relais permet de construire un maillage et rend le réseau plus tolérant aux erreurs de transmission et de communication.

Pour utiliser des modules Z-Wave, il est nécessaire de détenir un contrôleur (clé USB à brancher sur un PC ou BOX domotique) et d’avoir un logiciel (intégré à la BOX domotique ou via un logiciel à installer) qui permet de gérer les différents scénarios et avoir un état de ses différents périphériques.

4.1 Fonctionnement du protocole

Le protocole Z-Wave est basé sur le standard ITU-T G.9959 [5] (normalisant les couches basses MAC et PHY). Au-dessus de ces deux couches bas-niveau, une troisième couche applicative décrit toutes les informations permettant de contrôler les modules et d’en rapporter l’état :

Figure 1

Le HomeID, la source et destination d’un message sont définis sur la couche MAC. Pour la destination, si la valeur est définie à « 0xFF », la trame est de type « broadcast » et donc envoyée à tous les équipements du réseau. La couche applicative fournit quant à elle des informations sur le type de classe de commande Z-Wave utilisé, la fonction appelée ainsi qu’une liste de paramètres. En outre, le contenu des premiers champs de cette couche peut légèrement varier pour les trames multicast, routées ou chiffrées.

En effet, toutes les communications sont organisées en classes de commande (CC). Ces classes sont utilisées pour réaliser des actions précises, liées aux fonctionnalités offertes par l’équipement. Une cinquantaine de types de classes différentes existent actuellement [6].

Chaque classe peut ensuite définir une ou plusieurs commandes, avec différents paramètres. Par exemple, la classe COMMAND_CLASS_SWITCH_BINARY permet de spécifier trois types de commandes :

- définition d’un état (SET) ;

- demande d’un état (GET) ;

- fourniture d’un état (REPORT).

Si l’on souhaite allumer une lampe, on pourra donc utiliser la commande SET pour définir le nouvel état, en passant en paramètre la valeur de cet état (0xFF pour 1/allumée, 0x00 pour 0/éteinte). Si le contrôleur domotique souhaite connaître l’état du périphérique, il utilisera la même classe, mais avec la commande GET. La lampe enverra ensuite une commande REPORT pour signaler son état en paramètre.

4.2 Chiffrement des messages

Z-Wave permet aux modules de chiffrer les communications en AES 128 bits en utilisant la classe de commande COMMAND_CLASS_SECURITY. Celle-ci permet d’encapsuler les données de la couche applicative, comme présenté en figure 2 :

Figure 2

Son utilisation permet théoriquement de se protéger des attaques telles que l’interception des communications et le rejeu de requêtes.

Afin que le contrôleur soit en mesure de savoir s’il doit utiliser la classe de sécurité pour l’envoi des messages, une trame NIF (Node Information Frame) est envoyée lors de l’inclusion d’un module au réseau Z-Wave. Si vous utilisez OpenZWave, la liste des classes reconnues et sécurisées pour les différents périphériques est stockée dans le fichier de configuration zwcfg_[HomeID].xml.

Afin de chiffrer les communications, une clé dédiée au réseau Z-Wave cible est tout d’abord échangée entre le contrôleur et le périphérique lors de la phase d’inclusion (représentée en bleu dans la figure 3) :

 

Figure 3

 

Pour initier cet échange, la trame « 0x98 0x04 » (SecurityCmd_SchemeGet [7]) est envoyée au périphérique. Il est ensuite possible d’adresser la clé réseau à partir de la commande SecurityCmd_NetworkKeySet (« 0x06 »). Cette clé est chiffrée avec l’algorithme AES 128 bits en utilisant une clé temporaire de 16 octets (initialisée à « 0x00 »), avant d’être envoyée sur le réseau.

Source : Security.cpp - OpenZWave

 

uint8_t SecuritySchemes[1][16] = { {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } };

Théoriquement, lors de la phase d'inclusion du périphérique sur le réseau, la portée du signal est réduite à quelques centimètres afin de limiter le risque d’interception et cette phase est très courte (généralement 20 à 30 secondes). Cependant, en pratique, nous avons constaté qu’il est possible de récupérer cette clé réseau même à une distance de plusieurs mètres du contrôleur.

La clé réseau obtenue est alors dérivée en deux clés par les deux parties :

- la clé de chiffrement est obtenue par le biais de l'algorithme AES en mode ECB en chiffrant le message «0xAA» (sur 16 octets) avec la clé réseau ;

- la clé d'authentification est également obtenue par le biais de l'algorithme AES en mode ECB en chiffrant le message «0x55» (sur 16 octets) avec la clé réseau.

Pour rappel, le mode AES-ECB est un chiffrement par bloc qui a pour principale lacune de chiffrer à l'identique deux blocs avec le même contenu.

Les clés dérivées permettront au final le chiffrement des données en AES en utilisant le mode OFB (Output feedback) et l'authentification en AES-CBCMAC (d’une longueur totale de 8 octets) qui garantit que le message n’a pas été altéré durant le transport.

De plus, un vecteur d’initialisation d'une longueur totale de 16 octets est utilisé afin de bloquer les attaques de type rejeu (8 octets générés par la source + 8 octets reçus préalablement de la destination via la trame SecurityCmd_NonceReport()).

Le code d’authentification (MAC) est calculé à partir des 16 octets du vecteur d’initialisation, d’un « sequence header » (type de message envoyé), de l’ID de la source et de la destination, de la longueur du payload et des données.

Il est intéressant de noter que la version 1.3 d’Open-ZWave dispose d’un nonce fixe (« 0xAA ») pour les 8 premiers octets, ce qui réduit fortement l’aléa du message chiffré. Ce défaut, rapporté par un utilisateur [8], a été corrigé depuis la version 1.4.

Source : Security.cpp – OpenZWave

 

/* Regenerate IV */

/* the first 8 bytes of a outgoing IV are random */

for (int i = 0; i < 8; i++) {

//initializationVector[i] = (rand()%0xFF)+1;

initializationVector[i] = 0xAA; }

/* the remaining 8 bytes are the NONCE we got from the device */

for (int i = 0; i < 8; i++) {

initializationVector[8+i] = _nonce[i];

}

Chaque message chiffré est alors encapsulé dans une trame de type SecurityCmd_MessageEncap («0x81»). Dans la requête, nous retrouvons le nonce de 8 octets de la source, le payload initial chiffré avec l’algorithme AES (qui contient la classe de commande, la commande et les différents paramètres) ainsi que la trame d’authentification (MAC).

La destination pourra ensuite déchiffrer le message à partir de la clé réseau dérivée et du nonce envoyé dans la requête SecurityCmd_NonceReport ainsi que celui présent dans SecurityCmd_MessageEncap.

5. Attaque du protocole Z-Wave

La première étape pour attaquer un réseau Z-Wave est l’écoute passive du trafic pour capturer des données cruciales telles que le HomeID et les informations sur les différents périphériques présents. Les clients de Sigma Design qui disposent du SDK peuvent pour cela utiliser un outil dénommé zniffer [9] qui permet d’écouter les communications. Pour les autres, il est nécessaire de trouver une solution alternative.

Nous tentons d’abord de passer notre contrôleur Z-Wave en mode PROMISCUOUS, en nous appuyant sur la fonction FUNC_ID_ZW_SET_PROMISCOUS_MODE (« 0xD0 ») documentée dans OpenZWave.

Celle-ci semble toutefois désactivée sur les contrôleurs que nous testons : toutes nos requêtes sont ignorées. Une altération du firmware du contrôleur permettrait peut-être de l’activer, mais nous n’avons pas étudié cette possibilité.

Afin de pouvoir écouter des trames Z-Wave, nous nous basons donc sur les travaux de recherche de la société « SensePost », présentés lors de la conférence BlackHat USA en 2013 [10]. Cette société a développé un outil (zforce [11]) qui permet d’intercepter et de transmettre des paquets. Pour utiliser zforce sur notre réseau de maquette, nous nous appuyons sur un émetteur et un récepteur utilisant une carte Texas Instrument C1110 [12], facilement trouvable dans le commerce et coûtant moins de 70 euros.

Figure 4

Une carte est utilisée pour la transmission, l’autre pour la réception. Elles sont connectées à notre PC en utilisant un pont USB-série [13]. Pour fonctionner, elles nécessitent l’installation de firmwares spécifiques, disponibles sur le dépôt Google Code de SensePost [14].

Cette première étape nous permet d’observer le trafic Z-Wave sur notre maquette. Une rapide analyse des différents périphériques en notre possession nous permet de constater qu’aucune trame présente sur le réseau n’est chiffrée, malgré le fait que ce protocole utilisé (Z-Wave) permette théoriquement l’utilisation d’AES pour protéger les messages applicatifs.

Nous pouvons donc écouter l’intégralité des échanges et, a priori, envoyer nos propres requêtes. Il nous suffit pour cela de connaître :

- le « HomeID » du réseau à attaquer (facilement identifiable avec une écoute passive) ;

- la source ;

- la destination ;

- un « payload » converti en hexadécimal.

Bien que nous soyons en mesure de recevoir et envoyer des trames, nous constatons rapidement que l’application z-force est restée à l’état de preuve de concept et dispose de fonctionnalités très limitées (affichage des seules données brutes pour chaque trame, envoi de commandes à l’aveugle, plantages réguliers de l’application…).

Afin de nous permettre d’étudier (et d’attaquer) le protocole Z-Wave plus simplement, nous décidons donc d’implémenter notre propre outil sur la base de ces travaux.

5.1 Présentation de l'outil Z-Attack

Ce nouvel outil (Z-Attack, disponible ici [15]) est basé sur un script python disposant d’une interface graphique avec un détail des actions sur la console standard. Les objectifs premiers de cet outil sont de pouvoir :

- décoder les trames Z-Wave, y compris les classes de commande, actions et paramètres connus, afin de faciliter l’analyse des trames capturées ;

- permettre de cartographier un réseau Z-Wave, en créant un graphe des modules et contrôleurs identifiés ;

- faire appel à des classes Z-Wave précises dans l’outil de transmission sans avoir à connaître exactement les références en hexadécimal ;

- déchiffrer les communications chiffrées à partir de la clé par défaut d’OpenZWave ou toute autre clé définie ;

- déchiffrer la clé réseau échangée lors de la phase d’inclusion.

Attaquer un réseau Z-Wave devient maintenant beaucoup plus intuitif. Pour les équipements qui n’utilisent pas la classe de sécurité (donc sans chiffrement), l’analyse et l’envoi de données sont relativement simples.

On trouve en premier lieu le prologue de trame, le « HomeID » (0xCEE1DF17), la source (0x01), le contrôle de trame (0x4102), la longueur (0x0D), la destination (0x02), la classe Z-Wave (0x25), la commande (0x01), un paramètre (0xFF) et pour finir le checksum et la fin de trame. Toutes ces données sont donc décodées pour être affichées dans un langage plus compréhensible.

Figure 5

Ensuite, si nous souhaitons interagir avec un module, nous pouvons simplement utiliser le menu dédié à cet effet en accédant à une fenêtre de sélection du HomeID, de la source, destination et de la classe à envoyer.

La figure 6 illustre l’envoi de la classe COMMAND_CLASS_MANUFACTURER afin de déterminer le constructeur et le type de produit exact associés à un module. Pour chaque classe envoyée, et par souci de simplicité, les éventuels paramètres de la requête sont actuellement directement intégrés dans le code python.

Figure 6

Il devient donc ici très simple de commander à distance des périphériques Z-Wave en se faisant passer pour le contrôleur (télécommandes, interrupteurs, détecteurs de porte, détecteurs de mouvement...).

Si nous sommes en présence d’un périphérique qui chiffre les communications, seul le nom de la classe COMMAND_CLASS_SECURITY sera affiché dans la capture sans informations supplémentaires. Nous ne serons donc a priori pas en mesure de savoir ce qui a été échangé et incapable d'interagir avec le périphérique.

Cependant, si certaines conditions sont réunies, Z-Attack permet de déchiffrer le trafic :

- Si l'utilisateur utilise OpenZWave avec la clé par défaut (0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10). Dans ce cas les trames seront déchiffrées à la volée.

- Si la clé réseau est connue, il suffit de la renseigner dans l'interface. Les trames seront ensuite déchiffrées à la volée.

- S'il est possible (avec un peu de chance) d'écouter les trames au moment de l'inclusion d'un périphérique, Z-Attack pourra capturer cette clé et l'afficher dans l'interface.

Figure 7

5.2 Vulnérabilités dans le chiffrement Z-Wave

En 2013, la société « SensePost » a présenté un défaut d’implémentation sur un système d’ouverture de porte Z-Wave qui utilise la commande de classe SECURITY [10]. L’envoi du payload « 0x98 0x04 » (SecurityCmd_SchemeGet) force le périphérique à réaliser de nouveau un échange complet de la clé réseau et permet d’en définir une à notre convenance. L’équipement ne contrôlait pas si une clé avait préalablement été définie. Cette vulnérabilité a cependant été corrigée sur les périphériques récents.

Lors de notre étude, nous avons également pu mettre en lumière un défaut lié à la récupération des trames sécurisées sur le contrôleur : au lieu d’envoyer directement des ordres à un périphérique comme cela peut être fait avec un équipement qui n’utilise pas la commande de classe SECURITY, nous envoyons une requête malveillante au contrôleur Z-Wave en nous faisant passer pour le périphérique sécurisé.

Nous constatons alors que le contrôleur accepte certaines commandes de rapport d’état non chiffrées, alors qu’elles devraient utiliser une trame d’encapsulation SecurityCmd_MessageEncap.

Figure 8

Dans la mesure où ce type de requête permet de fournir l'état d'un périphérique au contrôleur, il est possible de définir un faux statut afin de faire croire qu’une modification d’état a eu lieu. Même si cela ne modifie pas réellement la valeur du capteur, le statut affiché sur le contrôleur change, ce qui permet par exemple de forcer l’activation de scénarios basés sur cet état.

Reprenons un exemple de scénario du type « Si la luminosité ambiante est trop faible, allumer les lampes ».Il suffitalorsd'envoyer un rapport d'état de lumière faibleau contrôleur et nous fairepasser pour le capteur de luminosité. Cette requête lanceraalorsle scénario défini.

Il semblerait que toutes les commandes de classes qui implémentent la commande REPORT soient vulnérables à ce type d'attaque.

Cette vulnérabilité a été remontée aux éditeurs concernés. La correction semble cependant plus compliquée que prévu dans la mesure où certains constructeurs de périphériques Z-Wave implémentent mal le protocole et utilisent nativement la commande REPORT sans chiffrement même si la classe SECURITY est utilisée pour le reste des communications.

Conclusion

La domotique est actuellement en pleine expansion dans nos foyers. Les discours et publicités sont bien rodés et présentent la domotique comme un moyen de contrôler et sécuriser son habitat.

Cependant, nous avons pu constater que cela restera difficile tant que les constructeurs ne feront pas de la sécurité une priorité.

Il est actuellement relativement simple d’écouter et rejouer les communications Z-Wave non chiffrées. Il peut être trivial par exemple de désactiver un système d’alarme, ouvrir/fermer des volets, allumer/éteindre des lumières, modifier le thermostat d’un logement ou encore augmenter la température d’une pièce si nous connaissons la classe de commande à envoyer.

En pratique, la classe COMMAND_CLASS_SECURITY n’est que très peu implémentée même si cela tend à s’inverser avec l’arrivée du protocole Z-Wave+. Parmi les 524 équipements Z-Wave recensés par le site pepper1.net [16], seulement 48 utilisent la classe SECURITY (soit environ 10% en janvier 2015). Un réseau Z-Wave a donc actuellement de grandes chances d’être exposé aux attaques.

Nous avons également pu constater des défauts d’implémentation qui fragilisent la sécurité globale et permettent potentiellement à une personne malveillante de déclencher des scénarios domotiques.

Toutefois, il est important de noter que les vulnérabilités liées au chiffrement ont pour origine l’implémentation du protocole. Il est donc possible, en théorie, de protéger correctement un réseau Z-Wave constitué uniquement de modules utilisant correctement les fonctions de sécurité.

Enfin, il est intéressant de souligner que Z-Wave fait partie des protocoles qui permettent de sécuriser les échanges alors que pour d'autres (HomeEasy par exemple) la sécurité est inexistante…

Références et liens

[1] http://z-wavealliance.org/about_z-wave_technology/

[2] http://homelive.orange.fr/accueil/

[3] http://blog.domadoo.fr/2014/12/18/somfy-dit-oui-au-z-wave-et-aux-objets-connectes/

[4] https://code.google.com/p/open-zwave/

[5] http://www.eeherald.com/section/newss/nwss201201173.html

[6] http://wiki.micasaverde.com/index.php/ZWave_Command_Classes

[7] Le nom de ces fonctions est tiré du code source d’OpenZwave.

[8] https://github.com/OpenZWave/open-zwave/issues/373

[9] http://z-wave.sigmadesigns.com/dev_kits#z-wave_whats_in_kit

[10] https://www.sensepost.com/cms/resources/conferences/2013/bh_zwave/Security%20Evaluation%20of%20Z-Wave_WP.pdf

[11] https://code.google.com/p/z-force/

[12] http://www.ti.com/tool/cc1110dk-mini-868

[13] https://www.silabs.com/products/mcu/Pages/USBtoUARTBridgeVCPDrivers.aspx

[14] http://z-force.googlecode.com/svn/trunk/firmware/

[15] Disponible sur le site http://www.advens.fr/

[16] http://www.pepper1.net/Z-Wavedb/