Update Pureboot securely

During update process, Pureboot does not verify the .rom file before flashing it. I think it is quite dangerous because it allows malware in the operating system to gain privilege and persistency by modifying the .rom file in the usb drive.

I think Pureboot can secure this process by adding a “Verify and update BIOS” option to the menu, and by uploading a signature file for each released update. The “Verify and update BIOS” option should check both the signature and the time it is made against the embeded public key.

And for power users who want to flash their own BIOS. They can always use the existing update process or do it in the recovery shell. So there will be no vendor lock-in.

Update: To prevent TOCTOU related attack, Pureboot needs to copy the signature and .rom to a temp dir before verifying them. So the whole verify process will be 1) Copy the signature and .rom to a temp dir. 2) Verify the content of .rom file. 3) Verify the time the signature is made to prevent rollback.

1 Like

@jonathon.hall

Not disagreeing with your suggestion but it may be better in that case to update firmware from a live boot.

(For the truly paranoid, you can even get USB flash drives with a write-protect switch. I don’t know how PureOS live boot would go with booting in that scenario. It would be something to test. I have also heard some people complain that the write-protect switch is just a software switch that can be bypassed if the firmware in the flash drive is compromised but … well, it may still be worth making the effort.)

1 Like

I agree this should be part of the roadmap. PureBoot 29 already contains infrastructure to include an integrity check as part of the update. (The update is now a zip archive, and there’s a SHA-256 digest we can check.) Signing the updates that way would be similar to signing packages in PureOS; you can still flash/install whatever you want, but if you intended to apply a build from Purism and it was tampered, it’ll be detected.

That’s not a cryptographic signature so it won’t protect against tampering, just corruption. But it paves the way for it - the new update format (ZIP) gives us a good place to put the signature without making users move multiple files around.

I think there’s a lot to consider for the UX / infrastructure / etc., so it won’t happen overnight, but I do think it is something we should do.

2 Likes

I agree with you that malware in the operating system is bad news already :slightly_smiling_face:

But I think the point is that you might not always know there is malware present. So if you assume you don’t know, then in the current state, yes there is a benefit to downloading the update from a clean live boot. But if the updates were verified this way, you’d get the same benefit without having to do that - it’s easier for more people to be secure.

But malware in the OS can already do a lot of pretty bad things (xkcd: Authorization), so escalating from OS to firmware might be below some of those other things on my list of concerns.

2 Likes

You mean while the flashing is actually occuring inside the Heads payload runtime?
But this verification was made prior to flashing, through the use of the coreboot_util.sh script run from a terminal. True enough, though, this happened before the subsequent reboot to flash the .rom from the USB stick, and you may be cautious whether your OS at that point was not already compromised and created a forged firmware file. I don’t know how much trust we can put in this process of fetching the .rom file from the Purism repo - but I hope there are all sorts of hashes and signatures checking, to make sure the intended .zip archive is being downloaded. I have to trust Purism infrastructure and implementation at this point.
Now, if you agree to trust this process (coreboot_util script), you should immediately reboot and flash the created .rom file from the stick. Don’t leave it anywhere unattended or postpone flashing the firmware, the stick could be tampered with - but not if you do the entire firmware update in one go.
Some people download the .rom file directly from the repo, but Purism clearly advises not to do so. The only reason to do this would be to flash a new version of the firmware on an air-gapped system.
If this is not advisable, then I recon Purism thinks their coreboot_util.sh method to be the safest.
If you have doubts about your USB hardware, you can use something like the USB Armory device for storing the .rom file before flashing it.

Even a better option, since this would rule out any compromission of the OS. But only to the point that you can trust your live, Purism infrastructure, software build system, etc.

But how to implement this? Obviously you need a way to remotely attest what you are about to flash. That would be either a ToKey like the LK or a network connection to some site.
The LK IMO would not be able to do this attest (or I am not able to imagine how?) Furthermore, at this point, the .rom file from the stick has already been modified from the raw fetched original: the serial number of the machine was inserted inside the CBFS immutable FS, meaning each .rom file is unique to its MBD - no way to have a global hash or remote signature for everyboby.
So that brings my main concern and worst fears: we would need to implement a network stack inside Heads runtime in order to have a network connection for a remote attest.
Network stacks are ugly! They are huge, bloated and full of vulnerabilities. Implementing a network stack inside the firmware would IMO be a grave mistake, a massive and unneeded increase of the attack surface and an immense decrease of security.
It’s bad enough that we have to have an USB stack inside the payload, but I agree it is necessary (OS install from live, external USB boot, …).
I think there were good reasons why Purism didn’t want to implement network booting in PureBoot (while this is a standard and expected feature of UEFI)
Also is the question of the size of the SPI Flash: do we even have enough room for a full network stack? What other valuable features would we have to ditch? Will there be space left for future improvements and new features?

1 Like

I have been doing some thinking about this comment of mine I posted above.
I think that we could use the LK as a middle ground approach to the problem.
My feeling tells me that the coreboot_util.sh script is the weak link in this firmware update method. I think it’s in this area that we have to strengthen security.
First, we need a way to attest integrity and authenticity of this script before it executes. I don’t really know how this is currently implemented, but can we be sure of those two thing already? As long as we can trust that we will be executing the intended script in order to fetch the .zip archive, we can expect that we have the original untouched file downloaded. This should also perhaps be manually checked against published hashes and signature file (is there a way to do this already?)
From this point, the coreboot_util script would do its things, unzipping the archive and inserting the serial number of the machine into the CBFS, therefore modifying the original raw image.
And this is at this stage that I would use the LK, computing a hash of this uniquely modified .rom file, encrypting it with the gpg key and signing it. This file should then reside on the same USB flashdrive along with the .rom firmware ready for flashing.
Now, inside the Heads runtime - before preparing and flashing the update, integrity AND authenticity should be proved and at this point the user would insert the LK which will attest the signature and decrypt the hash file for it to be compared against the one computed from the firmware file.
This way, no need to drastically modify the Heads runtime and add the dreaded network stack. All we need for implementing this check is already there inside the initrd.
In fact, what we do here is transferring the trust we put in the networked environment that it can (we hope) securely attest what script is executed and what archive is being downloaded; and then being able to remote attest with the LK that we have this same unique .rom file flashed from Heads.

1 Like

Yes, in particular, coreboot_util.sh contains digests of all the ROM files it could download. This means it only accepts that exact file with that exact digest.

Yeah, that’s the OP’s point, and it is valid - malware running in your user account (would not need to be root) could alter the firmware file before it is flashed. It’s valid, and I think signing updates should be part of the roadmap, but it won’t be addressed immediately.

For PureBoot updates, coreboot_util.sh doesn’t do this. PureBoot’s update tooling knows how to preserve the serial number as well as its other settings. So a signature on the ROM would still be valid when PureBoot validates it, and after that it would preserver the serial number and settings. That already happens for the ZIP update integrity check coming in PB 29, so it could be extended to validate a signature as well.

When updating from PureBoot to a newer PureBoot, coreboot_util.sh really just downloads a file and hands it over, PureBoot itself does the rest. It’s all the other situations where coreboot_util.sh does more work - updating coreboot/SeaBIOS or switching firmware. coreboot_util.sh flashes the firmware directly in those cases, so it also preserves the serial number (and sets bootorder for SeaBIOS, etc.).

No, this wouldn’t be needed to sign updates. Per above, there’s an intact signature, and PureBoot builds would need to include a public key that it expects to sign future updates, similar to the way PureOS validates packages using a public key it already knows.

Same thing here, since the script does not actually modify the ROM/ZIP, this isn’t needed, but I will still add some clarifications.

The instructions have you download it via HTTPS, which authenticates the server. If you clone via Git, you can also verify the commit signatures.

You could manually compare the digests in coreboot_util.sh, or you can clone the releases repo via Git and check the signatures on the Git commits.

1 Like

That’s a good news for PureOS users. If this script is distributed through PureOS’s repo, it should protect PureOS users from initial exploitation by making them download a fake rom.

1 Like

Yes, that’s a good idea. The recovery shell of Pureboot provides a trusted environment where one can verify the iso and flash it to a usb drive. And the next question is how to find a suitable live boot. One common issue with live boots is that they are always out of date. And connecting an out-of-date system to the internet can be risky. Tail OS may be a good candidate because their images are signed and they release them frequently.

2 Likes

Trudat.

Well, yes, but.

With a separate firewall and the discipline to access only the required HTTP server in order to download only the required file(s), you can limit your risk. (You are always going to be exposed to possible DNS exploits.) It’s not like you will receive any email (for example) while live booted. I guess, in that environment, making it IPv4 only (no IPv6) also helps.

1 Like

Yes, malware in the OS can be nasty. But malware in the BIOS can be even more nasty. Because it’s harder to detect them. Many security tools do not look there. And I guess a malware can even do some bad things and then hide itself from OS by manipulating memory mapping. What’s more, it is much harder to remove malware from BIOS. Those are why I am so concerned.

Yes maybe, but let’s hope this area can be improved some day.

2 Likes

Yes, firewall and good habits can limit the risk. But I almost forgot another attack vector. If attackers gain root access in live boot, they can then modify the EC firmware and/or BIOS.

So users need to harden their live boot before connecting it to the internet.

1 Like

I seem to have some confusion about this and your reply puzzled me.
I thought that coreboot_util.sh did - as a last step before exiting - inject the serial number in the CBFS file.
I believed this because of doing some experimentation with trying to find a method for updating the PureBoot firmware on an Air-gapped Librem. I used a feature the script is proposing at its third dialog: if you selected PureBoot (#2) as firmware type to flash, it then asks you this:

Set the device serial number:

1 - Extracted from your local system (NB15I77500US-REDACTED)
2 - Enter serial number manually
3 - Do not set a serial number

so in order to prepare for the Air-gapped machine, I would select either option 3 (then Heads runtime would inject it with the other local settings before the actual flashing) or also I sometimes used option 2 - entering the real SN of the target machine (to be noted here: the default option is 1. Which is usually the case because you want to create a .rom file for the machine you are executing the script on)
Whatever option was chosen anyway, the script would display a brief message:
Injecting serial number into firmware image
before exiting.
And your reply here says that coreboot_utils.sh does not modify the .rom file it just downloaded.
So I decided to check this. I used the script version for Pureboot 28. I first rolled back my firmware to PureBoot 27.1, so the script would not exit (no update available) and propose an new firmware v28. Then I ran the script 3 times in a row, each time selecting the option for the serial# from 1 to 3…and the result was 3 .rom files that were each with a unique and different sha256 digest! So, it appears this script does something to the .rom file, otherwise they would all be the same digest whatever option for the serial was chosen.
I don’t understand…

1 Like

You’re right @TiX0, I didn’t mention that case, and it does set a serial number then.

In this case, since you are running coreboot_util.sh on a different device, it does not know whether the target device currently has coreboot/SeaBIOS or PureBoot. At the end, it gives you two different instructions to flash the device - one to switch from SeaBIOS and one to upgrade - since it doesn’t know which it is.

If you run it on the target device, then it knows the current firmware type and how you intend to upgrade. In that case, if it’s a PureBoot to PureBoot upgrade, it does not have to inject the serial number.

If/when we start signing PureBoot updates, it’s just a matter of improving the script interface so that it knows not to inject a serial number for an off-host PureBoot to PureBoot update. This means it needs to know whether the target device already has PureBoot or you are switching from SeaBIOS - it’ll essentially have to ask you one way or another.

Today, it doesn’t really matter so it injects a serial number all the time. If you are updating PureBoot, that serial number won’t be used anyway (you can try it if you want, the PureBoot update will remove whatever the utility script inserted to preserve the current serial number).

2 Likes

If you’re curious, PureBoot persists the serial number when flashing here: initrd/bin/flash.sh · purism_next · firmware / PureBoot · GitLab

1 Like

Thank you for explaining.

Yes, I had already understood this. Whatever Serial # you would insert while running the script, would be overridden anyway by Heads runtime replacing it with the current one.

1 Like