trezor-firmware
trezor-firmware copied to clipboard
Interrupting firmware update at the wrong time erases seed
To reproduce:
- set up device with seed
- start firmware update
- midway through, pull out the cable
Desired result:
After replugging, Trezor asks you to fix failed firmware update. When you install fw again, your settings and seed are there intact.
Actual result:
Device returns to factory state. Seed is wiped.
The above seems technically possible. One limitation comes to mind: bootloader would need to be able to determine which firmware vendor "owned" the storage, so that when you corrupt a signed firmware installation and then reinstall with unsigned, the storage is properly wiped.
For TT, this seems fixable. Store a vendor header identifier in storage when setting it up. If it matches the firmware being installed, keep storage, otherwise erase it. This would require the bootloader to understand storage to some extent. Either properly read public fields, or figure out some sort of trick, like verify storage integrity keyed off the vendor identifier?? ( ¯\_(ツ)_/¯)
For T1 a similar issue exists between signed / unsigned firmwares. We might be able to get around it by declaring that only if a firmware is signed, it gets to keep storage. This would allow an attacker to "upgrade" storage from an unsigned firmware to a signed one -- however, that does not seem like a big problem, because an attacker can achieve exactly that by (1) installing another unsigned firmware that dumps the storage, (2) extracting all relevant information from it, brute-forcing the PIN if necessary, (3) installing a signed firmware, (4) recreating storage contents. (there is still an opening in that a security issue might exist in signed firmware that can be triggered by invalid storage data. so an attacker installs unsigned firmware, writes bad data to storage, upgrades storage, signed firmware gets exploited. But the exploit would need to be persistent, otherwise the best the attacker can do is read back the storage contents they just installed :)) )
In any case, even if we plug this hole, there might not be enough space in the T1 bootloader to fit the additional logic.
cc @andrewkozlik for thoughts on the storage thing
For TT, this seems fixable. Store a vendor header identifier in storage when setting it up. If it matches the firmware being installed, keep storage, otherwise erase it.
problem: this still allows unsigned firmware to install storage contents that match some other vendor header (i.e. the official one)
The scenario where a malicious unsigned firmware places storage data "as if" from the official version does not seem interesting: this can be done today via the unsigned firmware overwriting itself with official image streamed from the host, and then placing whatever data into storage that it wants. Storage must be hardened against this sort of attack anyway.
On TT, it looks like the firmware is written linearly starting with the header chunk. It seems that we can easily detect a direct reinstall: if a vendor header with a valid signature is sitting in the right sector, but chunk hashes don't match, we can allow going to firmware upgrade. If the first chunk is literally the same thing that is now in flash, we can ignore the chunk mismatches and try to continue the installation with the subsequent chunks. At that point, all user confirmations must have already happened, the storage was already erased (or not), and we can just continue.
Overall it seems that we can trust the image header even if the chunk hashes don't match, for purposes of upgrade and vendor change detection. Of course, we can't allow a corrupted firmware to run, but there doesn't seem to be a reason to erase storage just because a firmware with a valid header is corrupted?
To close the small window between erasing the old header chunk and writing the new one, we could do something like (proposed by @andrewkozlik):
- erase sector 1
- copy over contents of sector 0 to sector 1
- erase sector 0
- write new header chunk to sector 0
- erase sector 1 again
- continue with firmware upload, erasing sectors as we go instead of all at once at start. (this would btw nicely smooth out the firmware upgrade progress bar)
then when sector 0 is garbage and sector 1 contains a valid signed header, we instead consider the header that is in sector 1
this would cause sector 1 to get double the wear of the other sectors; not sure if that is a problem?
General assumption: an attacker who can achieve arbitrary write to flash could on-the-fly replace the whole of signed firmware with an unsigned one, reboot to the unsigned firmware (without the bootloader noticing anything wrong) and use it to read out storage.
An attacker with partial write capability could be able to modify parts of flash which then get executed, possibly achieving something interesting.
Given the above: Allowing corrupted firmware on flash gives an attacker with partial write access one more capability. If the attacker can fully replace sector 0 with a valid unsigned firmware header, or corrupt sector 0 and fully replace sector 1 then such attacker can perform a vendor switch without erasing storage. Then they reboot to bootloader, fully install an unsigned firmware, and proceed to dump storage.
For completeness: It is virtually impossible to corrupt a header sector in a way that converts it to a different valid header sector without full write capability to that sector. This means that the above opening would become relevant only in the very narrow case of an attacker who somehow has full write access only to the header sectors but not elsewhere.
keeping this open.
2.1.4 bootloader shrinks the vulnerable window to the time it takes to erase one sector & write the header, which is roughly 2 seconds at start of the process. that is very good, but we might be able to do even better if we implement the "backup header" thing.
that is a much lower priority though.