Test der 'Netatmo Welcome' Smart Camera – Hardware Hacking

Netatmo Welcome ist eine intelligente Kamera, die Gesichter erkennen, Aufnahmen in die Cloud streamen oder den Besitzer im Falle eines Einbruchs alarmieren kann. Im Rahmen laufender Forschungen zur Sicherheit des Internets der Dinge haben wir unsere Analyse der Kamera fortgesetzt und Hardware-Hacking betrieben. Wir konnten eine Root-Shell auf der Kamera erhalten und können nun unsere eigenen Linux- oder Android-Images auf der Kamera bereitstellen. Wir haben auch herausgefunden, dass der IPsec-Modus eine ständige VPN-Verbindung bedeutet, die der Hersteller verwendet, um Befehle an die Kamera zu senden, aber könnte verwendet werden, um aus der Ferne auf jede Kamera zuzugreifen. Mit dem Wissen, wie der Passwortschutz funktioniert, kann jeder Zugriff erhalten. Alles, was Sie brauchen, ist ein USB-zu-RS232-Konverter.

Inhalt

  1. Demontage und Herstellen einer seriellen Verbindung
  2. Ausnutzen der Kamera über die U-Boot-Shell
  3. Analyse des Konsolen-Login-Skripts
  4. Abrufen/Flashen von Firmware-Updates
  5. Aktivieren von lokalem SSH/Deaktivieren des SSH-Zugriffs für Netatmo
  6. Fazit

1. Demontage und Herstellen einer seriellen Verbindung

Wir haben Teardowns für die Kamera im Internet gefunden und einen seriellen Anschluss auf dem Mainboard identifiziert! Die Kamera kann leicht zerlegt werden, man braucht nur den nötigen Torx-Schraubendreher und etwas Fingerspitzengefühl. Dann haben wir ein dreipoliges Kabel an die Anschlüsse gelötet für eine dauerhafte Verbindung und es mit einem seriellen-zu-USB-Konverter verbunden, den man für ein paar Euro in den meisten Online-Hardware-Stores bekommt (USB zu RS232 TTL UART PL2303HX Adapter). Die Pinbelegung ist auf der Platine beschriftet und der serielle Anschluss ist auf Standardwerte eingestellt (115200 8N1, mit Hardware-Flusssteuerung auf 1 und Software-Flusssteuerung auf 0), so dass wir uns einfach mit dem Tool minicom oder screen (screen /dev/ttyUSB0 115200,cs8,ixon) verbinden konnten. Kabel verbunden mit den seriellen Anschlüssen auf dem Mainboard der Kamera.

2. Ausnutzen der Kamera über die U-Boot-Shell

Was man sieht, während man mit dem seriellen Anschluss verbunden ist, ist das detaillierte Boot- und Ereignisprotokoll und eine Fehlermeldung beim Drücken der Eingabetaste, dass das Passwort falsch ist, gefolgt von der ausgegebenen Firmware-Version.

Incorrect password.
Firmware version: 73
NSC[70:ee:50:2e:33:09] password:

Wir konnten die Passwortabfrage mit CTRL+D beenden, um den Dienst neu zu starten, wodurch der Name console_login.sh enthüllt wurde. Während Brute-Force bei Firmware-Version 73 (die Version, mit der unsere Kamera geliefert wurde) möglich sein könnte, stießen wir beim Update auf Version 199 auf einige Schutzmaßnahmen an diesem Ende. Wir werden im nächsten Kapitel detaillierter auf diese Änderungen eingehen.

Wir versuchten zuerst eine Technik namens pin2pwn zu verwenden, fanden aber schnell heraus, dass eine U-Boot-Konsole sicher erreicht werden kann, indem man die ’s’-Taste gedrückt hält, während das Gerät bootet. Sobald wir in der U-Boot-Shell waren, sammelten wir Informationen darüber, welche Befehle verfügbar sind:

? - alias for 'help'
base - print or set address offset
boot - boot default, i.e., run 'bootcmd'
boota - boota - boot android bootimg from memory

bootd - boot default, i.e., run 'bootcmd'
bootelf - Boot from an ELF image in memory
bootm - boot application image from memory
bootvx - Boot vxWorks from an ELF image
cmp - memory compare
cp - memory copy
crc32 - checksum calculation
efex - run to efex
env - environment handling commands
exit - exit script
false - do nothing, unsuccessfully
fastboot_test- do a sprite test
**fatdown** - download data to a dos filesystem
fatinfo - print information about filesystem
**fatload** - load binary file from a dos filesystem
fatls - list files in a directory (default /)
go - start application at address 'addr'
help - print command description/usage
key_test- Test the key value

logo - show default logo
loop - infinite loop on address range
mass_test- do a usb mass test
md - memory display
memcpy_test- do a memcpy test
mm - memory modify (auto-incrementing address)
**mmc** - MMC sub system
mmcinfo - display MMC info
mtest - simple RAM read/write test
mw - memory write (fill)
nm - memory modify (constant address)
pburn - do a burn test
printenv- print environment variables
recovery- sunxi recovery function
reset - Perform RESET of the CPU
run - run commands in an environment variable
save_userdata- save user data
saveenv - save environment variables to persistent storage
setenv - set environment variables
showvar - print local hushshell variables
shutdown- shutdown the system
sprite_test- do a sprite test
standby - run to boot standby
sunxi_bmp_info - manipulate BMP image data
sunxi_bmp_show - manipulate BMP image data
sunxi_boot_signature- sunxi_boot_signature sub-system
**sunxi_flash** - sunxi_flash sub-system
test - minimal test like /bin/sh
timer_test - do a timer and int test
timer_test1 - do a timer and int test
true - do nothing, successfully
version - print monitor, compiler and linker version

die Speicherorte aus unserem Bootlog:

--------fastboot partitions--------
-total partitions:10-
-name- -start- -size-
bootloader : 1000000 1000000
env : 2000000 20000
boot : 2020000 2000000
system : 4020000 28000000
data : 2c020000 40000000
misc : 6c020000 1000000
recovery : 6d020000 2000000
cache : 6f020000 1000000
databk : 70020000 38000000
UDISK : a8020000 0
-----------------------------------

und welche Umgebungsvariablen gesetzt sind:

#printenv:

baudrate=115200
boot_fastboot=fastboot
boot_normal=**sunxi_flash read 40007800 boot;boota 40007800**
boot_recovery=sunxi_flash read 40007800 recovery;boota 40007800
bootargs=console=ttyS0,115200 root=/dev/nanddinit=/init loglevel=8 partitions=${partitions}
bootcmd=run setargs_nand boot_normal
bootdelay=3
console=ttyS0,115200
init=/init
loglevel=8
mmc_root=/dev/mmcblk0p7
nand_root=/dev/nandd
setargs_mmc=setenv bootargs console=${console} root=${mmc_root}init=${init} loglevel=${loglevel} partitions=${partitions}
setargs_nand=setenv bootargs console=${console} root=${nand_root}init=${init} loglevel=${loglevel} partitions=${partitions}
stderr=serial
stdin=serial
stdout=serial

Environment size: 699/131068 bytes

Der interessante Teil ist, dass wir über die U-Boot-Befehle (fatload, fatdown, fatls…) Zugriff auf die SD-Karte haben und auch wissen, wie das Boot-Image in den RAM geladen wird und wo es sich befindet. Wir nutzen dies, indem wir zuerst das verwendete Boot-Image herunterladen, es dann extrahieren, um das Login-Skript zu deaktivieren, es wieder komprimieren und in den RAM laden, um es schließlich zu booten.

sunxi_flash read 40007800 boot fatdown mmc 0:1 40007800 boot.img

Das Boot-Image wurde mit einem Tool namens bootimgtool extrahiert.

bootimgtool -x boot.img

Dann wurde die Ramdisk mit gunzip und cpio extrahiert:

gunzip -c ../ramdisk.img | cpio -i

Da wir den Namen des Passwortabfrage-Dienstes kannten, suchten wir einfach nach console_login.sh in der init.rc-Datei und entfernten die Dienstdefinition.

#service console /system/bin/console_login.sh
# class core
# console
# disabled
# user shell
# group log

Und schließlich wurde alles wieder zusammengesetzt und auf die Speicherkarte kopiert, indem die folgenden Befehle im Ramdisk-Verzeichnis ausgeführt wurden:

find . | cpio -o -H newc | gzip > ../newRamdisk.img bootimgtool -r newRamdisk.img -c bootNew.img

In U-Boot wird die SD-Karte mit dem Befehl mmc erkannt/gefunden und fatload wird verwendet, um das neue Image in den RAM zu laden. Schließlich verwenden wir den Befehl boota, auf die gleiche Weise, wie er für das normale Booten verwendet wird, um das System mit unserem neuen Image zu starten.

fatload mmc 0:1 40007800 new3.img boota 40007800

Wenn das Booten abgeschlossen ist, haben wir direkten Zugriff auf das System, ohne ein Benutzerpasswort eingeben zu müssen. Die Rechteausweitung ist so einfach wie das Tippen von ‘su’, um Root-Rechte zu erhalten.

3. Analyse des Konsolen-Login-Skripts

Das war also eine Überraschung, das Login-Skript prüft auf das Geheimnis, das wir bereits in unserem ersten Blogbeitrag über die Netatmo Welcome Kamera gefunden haben. Da wir dies wissen, ist der direkte Shell-Zugriff nun unkompliziert! Besonders lustig ist der Kommentar in der ersten Zeile des Skripts ‘A simple “login” equivalent to replace the open serial console on production, to slow-down reverse-engineering. If you can read this, it didn’t work.’. Nun, es scheint, dass es für uns nicht funktioniert hat.

#!/system/bin/sh

# A simple "login" equivalent to replace the open serial console on production,
# to slow-down reverse-engineering.
# **If you can read this, it didn't work.**

if [ -e /system/etc/netatmo-production.txt ]; then
  PRODFLAG=`cat /system/etc/netatmo-production.txt`
  if [ "$PRODFLAG" -eq 0 ]; then
    exec /system/bin/sh
  fi
  # prod=1 or file missing : ask for password
fi

DBLIBTOOL=/system/bin/dblibtool
# md5 of the md5 of rsa
BIGSECRET="4ca9f3a5ce53a81a12af0f6c22a5775d"
NBTRY=0

while true; do
  MAC=`${DBLIBTOOL} -get 1`
  SECRET=`${DBLIBTOOL} -get 3`

  echo -n "Firmware version: "
  cat /system/etc/netatmo-version.txt

  VALIDSECRET=1
  if [ -z "$MAC" -o -z "$SECRET" ]; then
    echo "NSC[] : dblib not configured yet"
    VALIDSECRET=0
  fi

  if [ ${#SECRET} -lt 6 ]; then
    echo "NSC[$MAC] dblib secret invalid"
    VALIDSECRET=0
  fi

  # Only need to type first 6 characters of SECRET
  SHORTSECRET=`echo -n "$SECRET" | /system/bin/busybox cut -c 1-6`

  echo -n "NSC[$MAC] password: "

  # "read -s" is not implemented, do it old-fashion
  busybox stty -echo
  read password
  busybox stty echo

  if [ $VALIDSECRET -eq 1 ]; then
    if [ "**$password" == "$SHORTSECRET" -o "$password" == "$SECRET"** ]; then
      echo "Password accepted."
      exec /system/bin/sh
    fi
  fi

  # In case of a corrupted dblib, be able to login with a master password
  password_hash=`echo -n "$password" | /system/bin/busybox md5sum - | /system/bin/busybox cut -d' ' -f 1`
  if [ **"$password_hash" == "$BIGSECRET"** ]; then
    echo "Master password accepted."
    exec /system/bin/sh
  fi

  echo "Incorrect password."
done

Das Login-Skript enthielt am Ende neuen Code mit 2 Sekunden Wartezeit und wurde nach 3 Versuchen deaktiviert. Während in der ersten Version 6 Zeichen für das kurze Geheimnis ausreichten, wurden später 16 Zeichen verlangt. Das Skript wurde durch Erzwingen einer Endlosschleife deaktiviert, die einfach durch Drücken von Control+C umgangen werden konnte, um das Login-Skript zu beenden und neu zu starten, was durch einen trap ’’ INT-Befehl behoben wurde.

# Only need to type first 16 characters of SECRET
SHORTSECRET=`echo -n "$SECRET" | /system/bin/busybox cut -c 1-16`
sleep 1
echo "Incorrect password."
sleep 1
NBTRY=$((NBTRY+1))
if [ $NBTRY -gt 3 ]; then
  echo "Login disabled."
  while true; do
    sleep 10
    echo "."
  done
fi

Wir haben eine einfache Änderung am Skript für die Persistenz vorgenommen, indem wir den Vergleich auf ungleich änderten und aus Bequemlichkeitsgründen ‘-c su’ hinzufügten:

if [ "$password_hash" **!=** "$BIGSECRET" ]; then
  echo "Master password accepted."
  exec /system/bin/sh **-c 'su'**
fi

4. Abrufen/Flashen von Firmware-Updates

Wir haben das Firmware-Update-Skript analysiert und den korrekten API-Aufruf für Firmware-Anforderungs-Updates gefunden.

curl --silent --data 'mac=70:ee:de:ad:be:ef&secret=$SECRET&fw=73&hw_version=246' https://apicom.netatmo.net/api/getcamerainfo
{
  "body": {
    "timezone": "Europe/Berlin",
    "share_info": false,
    "firmware_info": {
      "fw_url": "https://fw-556112.c.cdn77.org/nsc-v250-hk-fix-faceconvert-prod.zip"
    },
    "home_id": "5bd724ed2d3e046ad38bd32f"
  },
  "status": "ok",
  "time_exec": 0.007094144821167,
  "time_server": 1540826441
}

Dies gibt uns eine statische, immer noch gültige URL zum Herunterladen des zum Zeitpunkt des Schreibens neuesten Firmware-Images als ZIP-Datei.

Sie können das neueste (13.10.2022) Firmware-Image hier herunterladen: https://n3tfw.blob.core.windows.net/nacamera-clients/nsc-v5012000-41480bb7baf7d3852020a1f23e1ca616.zip

Die ZIP-Dateien sind typische Android-Update-ZIPs/JAR-Dateien, mit einem Zertifikat (CERT.RSA) und einer Liste von SHA1-Summen für jede Datei (MANIFEST.MF). Die Liste selbst ist mit dem Zertifikat signiert. Das Ändern einer Datei innerhalb der ZIP führt zu einem Fehler bei der Integritätsprüfung. Das private Zertifikat, mit dem diese ZIPs signiert werden dürfen, ist nicht in unserem Besitz, also müssen wir die Recovery ändern, die diese Zertifikate prüft! Wir verwendeten die Tools/ubiquitären Android-Debug-Keys, aus diesem XDA-Post: https://forum.xda-developers.com/nook-touch/general/solution-customize-update-factory-image-t3027759

Kurz gesagt: Holen Sie sich zuerst eine Kopie Ihres Recovery-Images, verwenden Sie dann scp, um es auf Ihren lokalen Computer zu bringen, und extrahieren Sie es auf die gleiche Weise wie zuvor: dd of=/def/block/by-name/recovery if=recovery.img b=1024 bootimgtool -x recovery.img gunzip -c ../ramdisk.img | cpio -i

Als nächstes müssen Sie den Standardschlüssel in der Datei /res/keys in Ihrer extrahierten Ramdisk ersetzen/hinzufügen. Dann packen, scp und flashen Sie Ihre neue Recovery:

find . | cpio -o -H newc | gzip > ../newRamdisk.img
bootimgtool -r newRamdisk.img -c recoveryNew.img
scp ...
dd of=recoveryNew.img if=/def/block/by-name/recovery b=1024

Sie können dann die heruntergeladene Firmware nach Ihren Wünschen ändern, aber vergessen Sie nicht, das signapk-Tool mit dem Standard-Android-Schlüssel zu verwenden, um sie anschließend zu signieren! java -jar signapk.jar -w testkey.x509.pem testkey.pk8 nsc-v250-softscheck2.zip nsc-v250-softscheck2.signed.zip

Sie können die .zip flashen, indem Sie sie auf das Gerät kopieren und die folgenden Befehle aus dem Netatmo-Update-Skript verwenden (vergessen Sie nicht, den Pfad zu Ihrer Datei anzupassen):

mkdir -p /cache/recovery
echo "boot-recovery --update_package=$DIR/ota.zip" >; /cache/recovery/command
/system/bin/netatmo_reboot.sh 3 recovery

5. Aktivieren von lokalem SSH/Deaktivieren des SSH-Zugriffs für Netatmo

Es ist viel bequemer, über SSH auf die Kamera zuzugreifen. Fügen Sie dazu die Zeile “ListenAddress 0.0.0.0” in /data/ssh/sshd_config ein. Sie sollten Netatmo möglicherweise daran hindern, über SSH auf Ihr Gerät zuzugreifen, indem Sie die Zeile löschen, die “ListenAddress 10.255.145.118” enthält.

In neueren Firmware-Versionen reicht dies allein nicht aus, da der Verkehr auf Port 22 immer noch von der Firewall blockiert wird. Benennen oder entfernen Sie /system/bin/iptables_ssh.sh und löschen Sie die Zeilen 13 und 19 in /system/bin/check_ssh.sh (unten gezeigt), um iptables-Regeln zu entfernen, die den SSH-Port blockieren. Nach dem Neustart sollten Sie sich per SSH verbinden können.

#!/bin/sh

IS_PROD=$(cat "/etc/netatmo-production.txt")
if [ "$IS_PROD" -eq 0 ] ; then
  exit
fi
# default to 0 if file does not exist
SSH_ALLOWED_DELAY=$(cat "/data/netatmo/var/ssh-timeout.txt" || echo 0)
if [ "$SSH_ALLOWED_DELAY" -lt "$(date +%s)" ]; then
  # Check if there is an already set rule to prevent adding more than one rule
  if [ "$(iptables -L INPUT --line-numbers | grep ssh | busybox awk '{print $1}')" -eq 0 ]; then
    # Disable ssh port
    iptables -A INPUT -j REJECT -p tcp --dport 22
  fi

  # Do the same with ipv6
  if [ "$(ip6tables -L INPUT --line-numbers | grep ssh | busybox awk '{print $1}')" -eq 0 ]; then
    # Disable ssh port
    ip6tables -A INPUT -j REJECT -p tcp --dport 22
  fi
fi

Der Einfachheit halber können Sie einfach den RSA-Schlüssel verwenden, der sich bereits auf dem Gerät befindet (/data/ssh/authorized_keys). Fun Fact: In der Firmware-Version 250 schien dieser private Schlüssel fpotter zu gehören, was sehr wahrscheinlich Fred Potter ist, der CEO von Netatmo.

ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDDj/6pdOoky0qsAiKZxfyMoUwiBpFkrAUBUA2ZaSCZIobARH3ANYYWaJoKN8+mjo6UpShK9GVSgBnRaLV0vwkr+QP1anTBX3s9zRhm2vDA5nfwnogWq/MZ4VaVONl4aLnpw29bBYuomhgU1Oxexzp6dP//XmbqUjLXH9ND1fR6LBkHkhpWNWa27O7UhpEs+fkuZlMuEasUNntgoyU5818950uxrtK3rHz1HN3UdxknREsy5VMPQgchS23kNr+w2lirqDWz0q8YpGqU89YCuQlNKeAPOdlw7APBqi719LxlusCwjnqi/x+B3mpGWLdukNT327EBAP8cwaIrwicRhTcF fpotter@dell-fpotter

6. Fazit

Sobald wir in der Bootloader-Shell waren, waren die Dinge ziemlich einfach. Wenn Sie eine serielle Verbindung haben, reicht das Setzen eines Passworts nicht aus, um Ihr Gerät zu sichern. Wir empfehlen, den Boot-Prozess weiter zu sichern, so dass nur von Netatmo signierte Images gebootet werden können. Ein Angreifer, der in der Lage ist, in sein eigenes Image zu booten, kann das Gerät vollständig übernehmen.

Lesen Sie über die andere Schwachstelle, die wir gefunden haben, hier.

Lesen Sie über weitere interessante Themen in unserem Blog.