sMug@replicatorbe
~/Publications/Domotique/detecter-l-appui-sur-la-sonnette-d-un-portier-neo-coolcam-avec-tcpdump

Détecter l'appui sur la sonnette d'un portier Neo Coolcam avec tcpdump

Domotique / /10 min de lecture

Ce portier IP n'expose aucune API : impossible de récupérer l'appui sur le bouton. En écoutant le trafic du routeur en OpenWrt, on repère la trame UDP qu'il envoie vers son cloud à chaque sonnerie — et on s'en sert pour afficher la caméra sur la télé.

Note ajoutée depuis : le billet où j'expliquais tout le mal que je pensais de ce portier n'a pas encore refait surface dans mes backups. J'ai remplacé mon adresse IP publique de l'époque par XX.XX.XX.XX dans les traces ; les adresses distantes, elles, sont d'origine — elles font partie de la démonstration. Le texte d'origine était rédigé à la troisième personne, je l'ai remis au « je ».

Je vous ai expliqué dans un autre article que je ne recommandais absolument pas l'utilisation de ce portier vidéo : le prix est vraiment intéressant, mais le rapport qualité-prix est exécrable. Pas de chance pour vous, vous l'avez quand même acheté et vous voulez en faire quelque chose ?

Vous souhaitez récupérer l'appui sur le bouton de la sonnette du portier IP Doorbell de Neo Coolcam et déclencher une action chez vous ?

Vous l'aurez compris : ce portier n'offre aucune API à laquelle on pourrait dire « quand on appuie sur le bouton, appelle cette URL ». C'est bien dommage. Les product owners de ces boîtes électroniques devraient se renseigner un peu mieux auprès de leur clientèle !

Ce que vaut la méthode, avant de commencer

Soyons clairs tout de suite : ce qui suit est un contournement. La fonction de base n'existe pas, et il est difficile de trouver quelque chose de propre et de réalisable facilement. Cette méthode génère parfois quelques fausses alertes — vous ne pouvez donc pas l'utiliser pour, par exemple, réveiller quelqu'un ou déclencher une alarme.

Dans mon cas, elle sert à afficher l'image de la caméra de surveillance de la porte d'entrée sur la télévision, via Kodi, quand quelqu'un appuie sur le bouton. S'il y a une fausse alerte, ce n'est pas bien grave : l'image apparaît et disparaît quelques secondes plus tard.

En revanche, dans l'autre sens, c'est du 100 % : quand quelqu'un sonne, c'est détecté à chaque fois, sans exception. Zéro raté. C'est le compromis, et pour cet usage-là il me va très bien.

Le principe : écouter ce que le portier raconte à son cloud

Un objet connecté de ce genre ne sait pas travailler tout seul. À chaque événement, il va bavarder avec ses serveurs. Si on ne peut pas lui demander de nous prévenir, on peut au moins écouter ce qu'il dit aux autres.

J'utilise pour ça une analyse avec tcpdump, et je vous invite à faire la même chose sur votre propre installation : vous allez probablement arriver à isoler une adresse IP ou une trame qui part vers le cloud au moment précis où quelqu'un sonne.

Lors de mes analyses, j'ai remarqué deux choses :

  • la même adresse IP distante était contactée à chaque appui sur le bouton, et toujours sur le même port ;
  • un chiffre identique (277) revenait dans la ligne à chaque fois, et ne changeait jamais.

Avec ces deux constatations, vous pouvez vous baser soit sur la combinaison IP + port, soit sur IP + 277. Pour mener ce genre de test, il faut quelques notions de réseau et surtout une bonne connaissance de votre réseau local : sans savoir ce qui est normal chez vous, vous ne verrez pas ce qui est anormal.

Qu'est-ce que tcpdump ?

tcpdump est un analyseur de paquets en ligne de commande. Il permet de voir le trafic qui passe par une interface réseau — dans mon exemple, l'interface br-lan, le pont qui relie tous les équipements du réseau local.

Dans cet article, l'analyse est réalisée directement sur le routeur, un Linksys sur lequel tourne OpenWrt. C'est le bon endroit : c'est le seul point par lequel tout le trafic de la maison passe obligatoirement. Sur un PC branché sur un switch, vous ne verriez que ce qui vous est destiné.

tcpdump -i br-lan -l -vvv dst host 192.168.0.206
  • -i br-lan — l'interface à écouter ;
  • -l — sortie ligne par ligne, indispensable si on veut enchaîner derrière un while read ;
  • -vvv — bien bavard ;
  • dst host 192.168.0.206 — on ne garde que ce qui part vers le portier. Enlevez ce filtre pour voir les deux sens.

Test 1 — ce que le cloud envoie au portier

11:51:13.024892 IP 47.88.6.65.10006      > XX.XX.XX.XX.20922: UDP, length 60
11:51:13.189065 IP 47.90.49.123.10006    > XX.XX.XX.XX.20922: UDP, length 60
11:51:13.205056 IP 139.196.111.149.10006 > XX.XX.XX.XX.20922: UDP, length 60
11:51:13.244063 IP 115.28.10.13.10006    > XX.XX.XX.XX.20922: UDP, length 60
11:51:13.675231 IP 202.181.238.11.10006  > XX.XX.XX.XX.20922: UDP, length 60

Test 2 — ce que le portier envoie au cloud

11:52:32.303700 IP XX.XX.XX.XX.20922 > 202.181.238.11.10006 : UDP, length 60
11:52:32.305070 IP XX.XX.XX.XX.20922 > 82.165.134.205.10006 : UDP, length 60
11:52:32.305088 IP XX.XX.XX.XX.20922 > 115.28.10.13.10006   : UDP, length 60
11:52:32.306242 IP XX.XX.XX.XX.20922 > 139.196.111.149.10006: UDP, length 60
11:52:32.306265 IP XX.XX.XX.XX.20922 > 47.88.6.65.10006     : UDP, length 60
11:52:32.306327 IP XX.XX.XX.XX.20922 > 47.90.49.123.10006   : UDP, length 60
11:52:32.336955 IP 82.165.134.205.10006 > XX.XX.XX.XX.20922 : UDP, length 60
11:52:32.474109 IP 47.88.6.65.10006     > XX.XX.XX.XX.20922 : UDP, length 60

Une rafale d'UDP de 60 octets vers une demi-douzaine de serveurs différents, en moins d'une seconde, et tous sur le port 10006 : c'est la signature. Le portier arrose plusieurs serveurs à la fois pour que l'application mobile puisse le joindre où qu'elle soit. Peu élégant, mais parfaitement régulier — et c'est cette régularité qui nous intéresse.

Le script qui détecte l'appui et envoie une requête

Une fois la signature identifiée, le reste tient en quelques lignes de shell. Ce script est installé directement sur le routeur OpenWrt et tourne en permanence :

#!/bin/sh
tcpdump -q -i br-lan -tttt | while read ligne
do
  if echo "$ligne" | grep "sonnette" | grep "277" ; then
    if echo "$ligne" | grep "almeda" ; then
      echo 'non-action'
    else
      curl -silent 'http://requete'
    fi
  fi
done

Explication ligne par ligne :

  1. tcpdump -q -tttt sort une version courte de chaque trame, horodatée avec la date complète. La sortie part directement dans la boucle while read, qui traite les lignes au fil de l'eau.
  2. On filtre sur sonnette : c'est le nom d'hôte que j'ai attribué au portier dans la configuration réseau du routeur. tcpdump affiche les noms plutôt que les adresses, ce qui rend le filtre bien plus lisible qu'une IP en dur — et surtout, ça continue de fonctionner si l'adresse change.
  3. On filtre ensuite sur 277, le nombre qui apparaît à chaque appui sur le bouton.
  4. Afin d'éviter une boucle sans fin, j'exclus le client almeda — c'est le serveur domotique qui gère l'ensemble de la maison et qui, justement, va chercher l'image de la caméra pour l'afficher sur la télé. Sans cette exclusion, le trafic déclenché par l'action se remet à matcher le filtre et on repart pour un tour.
  5. Ce qui reste déclenche le curl vers l'URL de mon choix — chez moi, un scénario côté domotique.

Deux mots au passage : donnez un nom d'hôte à chacun de vos équipements dans le routeur. Ça ne coûte rien, et le jour où vous lisez une capture, vous relisez des noms au lieu de déchiffrer des adresses.

La trace complète, un soir de sonnerie

Voici ce que donne la même écoute, filtrée sur le nom du portier, au moment où quelqu'un appuie sur le bouton :

root@OpenWrt:~# tcpdump -q -i br-lan -tttt | grep "sonnette"
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on br-lan, link-type EN10MB (Ethernet), capture size 65535 bytes
2017-08-26 22:23:13.828178 IP huaweip9.lan.17873 > sonnette.lan.20932: UDP, length 16
2017-08-26 22:23:14.910148 IP sonnette.lan.20932 > huaweip9.lan.17873: UDP, length 16
2017-08-26 22:23:15.837131 IP huaweip9.lan.17873 > sonnette.lan.20932: UDP, length 16
2017-08-26 22:23:16.361555 IP sonnette.lan.20932 > 47.88.6.65.10006: UDP, length 60
2017-08-26 22:23:16.362371 IP sonnette.lan.20932 > s17745602.onlinehome-server.info.10001: UDP, length 60
2017-08-26 22:23:16.362389 IP sonnette.lan.20932 > 202.181.238.11.10006: UDP, length 60
2017-08-26 22:23:16.363024 IP sonnette.lan.20932 > 139.196.111.149.10006: UDP, length 60
2017-08-26 22:23:16.363024 IP sonnette.lan.20932 > 115.28.10.13.10006: UDP, length 60
2017-08-26 22:23:16.391814 IP s17745602.onlinehome-server.info.10001 > sonnette.lan.20932: UDP, length 60
2017-08-26 22:23:16.523478 IP 47.88.6.65.10006 > sonnette.lan.20932: UDP, length 60
2017-08-26 22:23:16.691365 IP 202.181.238.11.10006 > sonnette.lan.20932: UDP, length 60
2017-08-26 22:23:16.717209 IP 139.196.111.149.10006 > sonnette.lan.20932: UDP, length 60
2017-08-26 22:23:16.735700 IP 115.28.10.13.10006 > sonnette.lan.20932: UDP, length 60

Les trois premières lignes sont l'application mobile qui discute avec le portier en local. Puis, à 22:23:16.36, la rafale part : cinq destinations en trois millièmes de seconde, et les réponses qui reviennent dans la foulée. C'est net, c'est franc, ça ne ressemble à rien d'autre dans la journée.

Ce qu'on voit aussi au passage

Ce n'était pas le but de la manœuvre, mais l'exercice est instructif : ce portier envoie du trafic en permanence, vers une poignée de serveurs répartis un peu partout dans le monde, sans qu'on sache trop ce qu'il raconte ni à qui. C'est le lot commun de ce type de matériel bon marché.

Si ça vous chagrine — et ça devrait un peu —, l'endroit pour agir est le même que celui où l'on a écouté : le routeur. On peut parfaitement isoler ce genre d'objet dans son coin du réseau et lui interdire de sortir. Mais alors l'application mobile ne fonctionnera plus, puisque tout passe par le cloud du fabricant. À vous de choisir votre camp ; le mien est fait.

Améliorations et méthode alternative

Il reste à peaufiner le principe pour éviter les fausses alertes. Quelques pistes si vous voulez creuser :

  • ajouter un anti-rebond : ignorer toute détection survenant moins de dix secondes après la précédente. C'est la modification qui rapporte le plus pour le moins d'effort ;
  • compter les paquets de la rafale sur une fenêtre d'une seconde et n'agir qu'à partir de quatre ou cinq, plutôt que de réagir au premier venu ;
  • relever le contenu des trames plutôt que leur seule enveloppe, avec -X, et chercher si l'événement « bouton » se distingue du trafic de maintien de connexion.

Je vous laisse réfléchir et investiguer. À mes heures perdues, j'améliorerai le mien, mais pour mon usage personnel, c'est totalement suffisant.

Et si cette façon de faire ne vous plaît pas à cause des faux positifs, il y a une autre approche que je n'ai pas encore testée avec ce portier : installer un module qui relève la consommation électrique en direct, et considérer qu'une augmentation soudaine signifie que quelqu'un vient de sonner. Cette méthode aurait un gros avantage — elle fonctionnerait avec n'importe quel portier, y compris non connecté, puisque la consommation grimpe dès que la vidéo se met en route.