Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Security Vulnerability in OTA Update Process - ArduinoOTA.cpp & espota.py #4113

Closed
bhass1 opened this issue Jan 7, 2018 · 12 comments
Closed

Comments

@bhass1
Copy link

bhass1 commented Jan 7, 2018

Hello! I think I have found two potential security issues in the OTA update protocol implemented by ArduinoOTA.cpp and espota.py. I have only analyzed and tested using the Arduino IDE update process, but it probably also affects the Web Browser, HTTP Server, and Stream Interface.

Basic Infos

A network user with eavesdropping capabilities can bypass the security mechanisms in place for the OTA Update Protocol using two different vulnerabilities.

Description

arduinoota update problems - bounce diagram

image

Mitigations

(1) Offline Password Brute Force - A proper password-based key derivation function (e.g. PBKDF2) and secure protocol using that derived key (e.g. using HMAC or CMAC) could fix the vulnerability. Using a strong password could mitigate the vulnerability. This should be made clear in the docs.
(2) Firmware Password Sniffing - Encrypted firmware delivery perhaps using a password-based key derivation function for the encryption key would fix the vulnerability.

@igrr igrr added this to the 2.5.0 milestone Jan 7, 2018
@igrr
Copy link
Member

igrr commented Jan 7, 2018

Since the password is just a plain string which is being embedded into firmware, it is not trivial to do some operations on it in compile time. Previously it has been suggested that an implementation based on c++ constexpr can be used to generate password hash at compile time. I haven't been able to find a ready made constexpr implementation of PBKDF2, but there's a SHA256 constexpr implementation around, which can probably be used as a stopgap measure.

Edit: it requires C++14, which might also require a compiler upgrade.

@bhass1
Copy link
Author

bhass1 commented Jan 7, 2018

If we accept a threat model where physical attackers are out of scope (which is currently the case due to easy ability to wire in and update via serial COM), I can think of other options instead of PBKDFs too:
Generate a symmetric key during or pre-compile time that's stored in ESP8266 firmware and the host machine's filesystem.
Generate an asymmetric key pair during or pre-compile time and place a certificate in the ESP8266 firmware and private key and certificate in the host machine's filesystem.

All three methods (PBKDF, symmetric, and asymmetric) require a bootstrapping installation where the initial update is done in a trusted environment because the initial firmware can't be decrypted because ESP8266 won't know the key.

The asymmetric scheme has nice security benefits because if the attacker reads the ESP8266 firmware, they can't update arbitrary code since the private signing key is secure on the host. If the attacker gets write permissions though, the trusted, stored certificate can be modified and accept rogue updates from a rogue signing key pair.

The symmetric scheme usually has better CPU performance and smaller memory overhead.

Any solution would require a cryptographic decryption function and MAC/signature verification function. I used WolfSSL for my personal project, but I'm not sure what the best route will be for this one?

@igrr
Copy link
Member

igrr commented Jan 8, 2018

I think that storing anything on the host machine's filesystem doesn't fit Arduino usage model very well. If we had a hypothetical tool to generate the key on the host, we would have to ask the user where to store the generated key, so it has to be a GUI tool. Then the user would have to copy this key to other computers they might be working on. On these computers, they would need to somehow tell us (Arduino IDE? Some tool?) where the key is stored. Overall, that would be a significant change from how OTA works right now and how it works with other Arduino boards.

To avoid having to maintain local storage of keys, we could, for example, derive ECDSA private key from a user-provided password; generate the public key and embed it into the application; on update, espota.py will sign the hash of the application binary, and send the signature to the device. This is still not secure against local attacks but would at least allow ArduinoOTA to be used in untrusted networks.

@devyte
Copy link
Collaborator

devyte commented Jan 15, 2018

I thought that ArduinoOTA was meant for development within a controlled environment? I'm not exactly an Arduino expert, but that's my understanding, and it's what is explained in our docs. After deployment, or when developing in untrusted networks, other methods should be used, e.g.: the secure webserver or secure updater, which we now have.
Given the previous, does this issue merit the additional effort and increase in code complexity?

@bhass1
Copy link
Author

bhass1 commented Jan 16, 2018

Thanks @igrr for your input, that makes sense. However, I believe it isn't quite possible to derive an ECDSA private key from a user-provided password. I looked into it a little bit and what I found wasn't very promising. However, I will still look into it some more this week.

@devyte I don't think it is explained very well in the docs that the password-based update mechanism is not secure against a network adversary (which is a common threat model in wireless networks). I would argue that if the current ArduinoOTA is meant for development in a controlled environment, the "Security" section should be removed completely and the current implementation should remove the password-based update mechanism to avoid giving other developers a false sense of security. On the other hand, I think it would be better if we can offer a default secure solution...

From my point of view, the current problem is that there is no crypto library we can easily leverage for a secure protocol. The bootstrapping problem I mentioned in my last comment is solved because you need to do the first installation using the serial COM port anyways for the first install. I am willing to work on this issue, but it will take some time... I will try to post back this week with an initial proposal.

@igrr
Copy link
Member

igrr commented Jan 16, 2018

@bhass1 Indeed it looks like it is not possible to derive ECDSA private key from an arbitrary user password in a straightforward way. Will also look into other options we could use.

@devyte I think that this discussion has value beyond ArduinoOTA (which indeed could be said to only work on private networks), but also to OTA over HTTP (client and server). As you know, OTA over HTTPS stretches the RAM usage to such an extent that makes it hard to use in many cases. Adding image verification would be help such cases: one could download a binary over an open channel and verify that it is trusted.

@devyte
Copy link
Collaborator

devyte commented Jan 16, 2018

@igrr I agree, but this issue is specific to ArduinoOTA. If the discussion is targeted at httpclient/httpserver, and it happens to cover ArduinoOTA in a more or less transparent manner, then awesome.

@bhass1 as I understand it, the ArduinoOTA authentication is meant to cover simple access restriction, in the same way as http simple authorization does. It is not meant to be secure, I.e.: it is not meant to protect against malicious access. As an example, consider a home network. Security is provided by the wifi WPA encryption, but access control inside, like against accidental access by your kids, can be provided by simple authorization. Unless your kids become evil hackers, that is more than enough.
Of course, I could be wrong. Like I said, I'm no expert here, but I did read up on these things not too long ago.

@Ivoz
Copy link

Ivoz commented Jan 16, 2018

@igrr if we have the assumption that the two communicating parties (OTA host and esp8266) initially start off with a shared secret (a password initially passed along with the firmware), then there is no need to worry about asymmetric encryption schemes or digital signatures!

You just use a HMAC to hash (and then verify) the OTA file. Also, esps have SHA1 at least, right? Would be nice to switch to using that instead of MD5.

@igrr
Copy link
Member

igrr commented Jan 16, 2018

@Ivoz one part of the issue is that the firmware is transmitted in plain text. This means that the shared secret (a password, or its hash used in HMAC construction), embedded inside the firmware in plain text, can be extracted by third party listening to the transmission. Then they can use this secret to forge a firmware which will be accepted by the device. This is the motivation for embedding the public key only, and using it to verify the signature.

@Ivoz
Copy link

Ivoz commented Jan 17, 2018

Again, no need for public key crypto (although you could put it in).

Just use a PBKDF to derive a cipher key from the shared password; encrypt the payload, HMAC the encrypted payload, and send over both.

@bhass1
Copy link
Author

bhass1 commented Jan 17, 2018

@devyte I think that argument would mean we should strip out the current security implementation (password) because it is not secure without some other form of security like WPA2.

I agree that encryption + HMAC would suffice, but is there a good crypto library to use @Ivoz, @igrr? I don't feel I'm in the position to choose which one is used as I am not a maintainer. There are some good candidates out there that are small, but will add dependencies. EDIT: It looks like WiFiClientSecure uses axtls. This could work.

@igrr
Copy link
Member

igrr commented Jan 17, 2018

@Ivoz okay, "encrypt the payload" is the thing that makes the difference. In that case HMAC would indeed suffice.

I would like to merge this ticket with #2103, as it seems that we have converged towards what was initially discussed there.

Please follow the issue link and click "subscribe" to receive further notifications, and let's continue the discussion there.

@igrr igrr closed this as completed Jan 17, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants