Attack via Update Server, prevention with Secure Boot - HendrikVE/swp-telematik-ws-20-21 GitHub Wiki

In this attack a hypothetical security flaw (in practice we just have access) in an OTA update server for the ESP32 is used to upload a malicious firmware image. Without secure boot, the ESP32 would write the malicious image into its flash memory, however with secure boot it won't because the image is not signed. This shows the additional layer of security that secure boot provides.

OTA Update

The Espressif IDF for the ESP32 provides Over the Air(OTA) update functionality, meaning update over WLAN, in which the ESP32 receives a new firmware image and writes it into an empty partition. It then reboots from that partition. This is useful if your ESP is in a place where you can't easily connect to it with USB. In our case, the openhabian Raspberry Pi also runs an nginx server which hosts updated firmware images. So you would build the image on your computer (by running make, the binary is ESP32/window_alert/build/window_alert.bin), and then copy it to the Pi (with scp). Each time the ESP is reset it attempts an update. Of course, we could much easier update by just flashing over USB, this is just for demonstration purposes. The ESP looks for updates relative to the root directory defined in the ota.conf file, which after set-up is stored on the pi in /etc/nginx/conf.d. The default root is /var/www/html/.

Setting-up OTA

Most of the setup is done by the setup_ota_server.sh script, which was already executed when you ran the remote_setup_pi_openhabian.sh during setup. All you need to do is run make menuconfig in ESP32/window_alert. Go to the point OTA config and enter the IP of the Pi. The server doesn't actually require a password, so ignore the rest.

Location of Updates, Versioning Mechanism

We need to keep the ESP from updating in an endless loop. ESP32/window_alert/src/MANIFEST.h contains a variable called APP_VERSION_CODE. The ESP will look on the Update Server into a directory called APP_VERSION_CODE+1 for a new firmware image. If the directory doesn't exist or is empty, we assume there is no new update. The update request also depends on the Device ID set in Device config of make menuconfig, something like esp32-1. For example, assume the ESP starts with APP_VERSION_CODE==1 and has Device ID == esp32-1, then the update request to the server would look like https://192.168.178.19:4443/esp32-1/2/window_alert.bin. If there are folders esp32-1/2 on the server that contains window_alert.bin, it is downloaded and written into the second boot partition. The ESP resets. Then assume in this new image APP_VERSION_CODE==2. The Esp requests https://192.168.178.19:4443/esp32-1/3/window_alert.bin. This time, lets say there is no 3 directory, so the ESP stops the update and starts its normal processes. The routine that handles the updates is in ESP32/window_alert/src/manager/UpdateManager.cpp. This is not very elegant because you need to go into the code yourself to increase the APP_VERSION_CODE variable each time you push an update to the server, but what can you do.

What setup_ota_server.sh does

You don't have to read this, just from an earlier version of this page when we hadn't written the script yet. First we need to install nginx on the Pi.

sudo apt install nginx

Then get the config file for the nginx server located at openHABian/res/nginx/conf.d/ota.conf and copy it to the Pi at etc/nginx/conf.d. The config file enables ssl.

server {
    listen          4443 ssl default_server;
    listen          [::]:4443 ssl default_server;
    server_name     SERVER_IP;
    root            /var/www/html/;

    ssl_certificate /home/openhabian/CA/SERVER_IP.crt;
    ssl_certificate_key /home/openhabian/CA/SERVER_IP.key;

    ssl_client_certificate /home/openhabian/CA/ca.crt;
    ssl_verify_client on;

    location / {
        try_files $uri $uri/ =404;
        auth_basic "Restricted Content";
        auth_basic_user_file /etc/nginx/.htpasswd;
    }
}

You have to replace SERVER_IP at server_name with the IP of the Pi. The certs you can name whatever you want of course. We will reuse the certificates used to set up the mqtt connection. Meaning on you local machine in swp-telematik-ws-20-21/ESP32/window_alert/src/storage/certs there is already client.crt,client.key and ca.crt. And on the PI at /home/openhabian/CA, there is already ca.crt,192.168.178.28.crt and 192.168.178.28.key, generated by the remote_setup_pi_openhabian.sh script.

Now, restart nginx. sudo service nginx restart.

Secure Boot

Secure Boot enables the signing of firmware images so that only those who possess the private signing key can update the ESP32. A public key is stored somewhere on the ESP to verify the image. There are different flavors of secure boot.

The genuine hardware secure boot burns the public key into so called eFuse on the ESP. These eFuse are one time writable which means that even if you have physical access to the ESP you can't change the public key and can't flash a new image. So if you lose the private key the ESP is bricked. Because we might want to reuse the ESP at some point or give them back to University we won't do that.

We will use "Secure Boot light", which are signed app images. Same principle just that the public key is stored in the flash memory so it can be overwritten with physical access (i.e. over USB). However this still protects against attacks from the network, so it's enough for our OTA example. You can read more about Secure Boot here.

Enable Signed App Images

To enable signed app images, run make menuconfig in ESP32/window_alert. There go to Security Features and enable Require signed app images. Some new options will appear. Verify app signature on update and Sign binaries during build should be enabled, the rest not. Secure boot private signing key is the path to the keypair. Generate this keypair by running openssl ecparam -name prime256v1 -genkey -noout -out secure_boot_signing_key.pem. Now you can build and flash the ESP.

Testing Signed App Images

You can test the image signing by putting an unsigned update on the OTA server. (Disable Require signed app images, make and put the binary file onto the OTA server in a folder respective of APP_VERSION_CODE). Then when you ESP tries the OTA update, it should read

E (26232) secure_boot: image has invalid signature version field 0xffffffff
E (26234) esp_image: Secure boot signature verification failed
[MAIN]  N: Error Occurred. Error #: -9

Response to Pi

If invalid secure boot occurs, the ESP published a warning to the mqtt topic myhome/CONFIG_DEVICE_ID/error (i.e. myhome/esp32-1/error), and also openhab/alarm/warning. The OTA sitemap will then display a warning