exposing the EFI shell in Secure Boot mode can lead to security bypass

Bug #2040139 reported by Mate Kukri
272
This bug affects 1 person
Affects Status Importance Assigned to Milestone
lxd (Ubuntu)
New
Undecided
Unassigned

Bug Description

The EFI shell is available as a built-in Boot Option in LXD's OVMF builds, even when Secure Boot is enabled.

This application has known mechanisms for bypassing UEFI Secure Boot, and has already been barred from signing previously.

It should either: not be built into LXD's OVMF, or be disabled when Secure Boot is enabled in any capacity.

CVE References

Steve Langasek (vorlon)
affects: lxc (Ubuntu) → lxd (Ubuntu)
Revision history for this message
Thomas Parrott (tomparrott) wrote (last edit ):

If we build out the EFI shell, would this prevent users from setting custom UEFI variables?

This is something I believe the kernel team are doing. @xnox please can you advise?

Thanks

Revision history for this message
Mate Kukri (mkukri) wrote :

setting UEFI variables isn't the main issue, the main issue is that the shell can modify arbitrary physical memory.

building it out should solve this as there are no signed shells out there anymore, and the only reason it can run under secure boot is because its built into the firmware.

Revision history for this message
Thomas Parrott (tomparrott) wrote :

Yes, but will uefi variables still be able to be set if there isn't an efi shell is my question?

Revision history for this message
Thomas Parrott (tomparrott) wrote (last edit ):

As I understand they use the efi shell for their work.

Revision history for this message
Mate Kukri (mkukri) wrote :

UEFI variables are a firmware feature, and are entirely independent of the EFI Shell. The EFI Shell is merely a standalone application that provides a command line front-end for them, alongside exposing a variety of other interfaces.

For setting the boot order, and other purposes, UEFI variables are set either using a Linux kernel driver, which calls UEFI runtime services.
Applications at boot time such as shim's MokManager can also manipulate UEFI variables directly. The EFI Shell isn't necessary, it's merely a debugging tool, which a user could still later run by disabling Secure Boot if they decide that's appropriate for their usecase.

No commercial UEFI implementation I am aware of, nor Fedora's / RH's OVMF build contain the EFI Shell either.

Revision history for this message
Dimitri John Ledkov (xnox) wrote :

How do you believe this? Kernel team does not use EFI Shell ever.

For setting variables we use normal variable access from booted linux; or use the menus => none of which use EFI Shell, which is pain to use.

Revision history for this message
Dimitri John Ledkov (xnox) wrote :

just to be clean EFI shell is the interractive promt which allows to launch any EFI applications, load up EFI drivers, and do lots of interesting things.

It is not related to the UX/UI which EDK has for configuring keys, secureboot setting, boot loader menu items and etc (aka "bios settings screen").

Revision history for this message
Thomas Parrott (tomparrott) wrote :

OK thanks for confirmation. I just thought it would be prudent to check before we build it out in case you used it because I thought that is how you were setting uefi variables (keeping in mind our earlier conversations where I said I'm not experienced in uefi variables at all :)).

Revision history for this message
Thomas Parrott (tomparrott) wrote :

Next question, as it may help us speed up a fix, do you know how to build it out?

Otherwise we'll look into it.

Revision history for this message
Thomas Parrott (tomparrott) wrote :

Also, for my further understanding, is this only a security issue for users who have console access but otherwise don't have access to disable secureboot by lxc config set v1 security.secureboot?

Revision history for this message
Mate Kukri (mkukri) wrote :

No, in theory this is any root access to Secure Boot bypass with no user interaction, using a process like:
- Use efibootmgr as root to set the first boot option to "EFI Shell"
- Put startup.nsh with malicious code at the root of the ESP
- Startup.nsh starts OS patched with malware

I have written no PoC tho, so it is only a theoretical attack, but I think it is definitely possible.

Revision history for this message
Mate Kukri (mkukri) wrote :

For building it out, you should be able to something like:

> OvmfPkg/build.sh -a IA32 -b RELEASE -DBUILD_SHELL=FALSE

Revision history for this message
Thomas Parrott (tomparrott) wrote :

Great stuff thanks!

Revision history for this message
Aleksandr Mikhalitsyn (mihalicyn) wrote :

Tom is absolutely right in that we depend on UEFI Shell thing to enroll Secure boot keys and generate NVRAM:
      # 4MB variant
      ./edk2-vars-generator -f "${FIRMWARE}" \
        -e ../../edk2/build/Build/*/*/*/EnrollDefaultKeys.efi \
        -s ../../edk2/build/Build/*/*/*/Shell.efi \
        -c "${CRAFT_STAGE}/share/qemu/OVMF_CODE.4MB.fd" \
        -V "${CRAFT_STAGE}/share/qemu/OVMF_VARS.4MB.fd" \
        -C "$(cat ubuntu-sb.crt)" \
        -o "${CRAFT_PART_INSTALL}/share/qemu/OVMF_VARS.4MB.ms.fd"

Source: https://github.com/canonical/lxd-pkg-snap/blob/208ea1256a64c3f7116c5f8e5e279bd0238705d2/snapcraft.yaml#L962

So, we can't just disable it. Before that we need to learn how to generate NVRAM and enroll Secure Boot keys without it.

Theoretically, we can build a firmware with shell at first step, then generate NVRAM, then build firmware without shell but take NVRAM from the previous step. NVRAM format is compatible between different builds of UEFI if they have the same FD_SIZE.

Revision history for this message
Mate Kukri (mkukri) wrote :

@mihalicyn

There is a library called python-uefivars which allows you to modify UEFI variables FDs without having to boot a VM at all. I think it might be viable to write a script that enrolls the keys externally using it.

Eventually I'd like to get the library into the archive, currently I have packaged it for my own use: https://git.launchpad.net/~mkukri/+git/python-uefivars.

In the meantime, it doesn't seem to me that you actually need the shell to be built into the CODE fd itself here.
Is my understanding correct that the shell is put in a FAT image as `boot{x64,a64}.efi` and ran that way? And it seems to run before SecureBoot is enabled anyways, so shouldn't it be possible to still build the Shell as an external EFI binary but not include it in the `code_{.*}.fd`s?

Revision history for this message
Aleksandr Mikhalitsyn (mihalicyn) wrote :

>There is a library called python-uefivars which allows you to modify UEFI variables FDs without having to boot a VM at all. I think it might be viable to write a script that enrolls the keys externally using it.

Yep, we have it in our plan to look on python virt-firmware and integrate it with LXD. As far as I understand python-uefivars is yet another python package that allows to edit NVRAM images.

>In the meantime, it doesn't seem to me that you actually need the shell to be built into the CODE fd itself here.
Is my understanding correct that the shell is put in a FAT image as `boot{x64,a64}.efi` and ran that way? And it seems to run before SecureBoot is enabled anyways, so shouldn't it be possible to still build the Shell as an external EFI binary but not include it in the `code_{.*}.fd`s?

You are partially right. We run shell using a Shell.efi application, at the same time when BUILD_SHELL = false, this application won't be built. I have checked that experimentally. I haven't dived into the edk2 code very deeply but it looks like Shell.efi is just a "client-side" part, while the main Shell API is in the firmware itself. That's why it makes no sense to build Shell.efi without having built-in shell components in the firmware.

Revision history for this message
Aleksandr Mikhalitsyn (mihalicyn) wrote :

Hm, I'm looking into the packaging code for edk2 package in Ubuntu and as far as I understand it has the same problem as we discuss here, right?

https://git.launchpad.net/ubuntu/+source/edk2/tree/debian/rules?h=applied/ubuntu/noble-devel

I can't see BUILD_SHELL=FALSE anywhere.

Do we have the same security issue for edk2 Ubuntu package or not? If yes, then what edk2 package maintainers think about the ways to handle it? I believe that we have to sync about approaches to the problem.

---

btw, I have played with https://pypi.org/project/virt-firmware/ a little bit and I was able to generate NVRAM file with Secure Boot enabled and some extra certificates enrolled. It looks like that:
virt-fw-vars -i OVMF_VARS.4MB.fd -o OVMF_VARS.4MB.ms.fd --distro-keys windows --set-pk 64e53e5c-5b54-4095-bd2e-4a8fceb7b4c3 ../ubuntu-sb.pem --add-kek 64e53e5c-5b54-4095-bd2e-4a8fceb7b4c3 ../ubuntu-sb.pem --sb

I think we can replace existing "edk2-vars-generator"-based solution with this. The only problem is that final UUIDs are not the same as we have after edk2-vars-generator. We can solve that by hard-coding microsoft certificates with the same UUIDs as we have them now instead of using "--distro-keys windows" option.

Revision history for this message
Mark Esler (eslerm) wrote :

This vulnerability affects both edk2 and LXD.

I have subscribed everyone from this bug to https://bugs.launchpad.net/ubuntu/+source/edk2/+bug/2040137

Revision history for this message
Aleksandr Mikhalitsyn (mihalicyn) wrote :

Great, thanks!

Revision history for this message
Mate Kukri (mkukri) wrote :

The python-uefivars package mentioned above has been uploaded to the archive now

Revision history for this message
Mark Esler (eslerm) wrote :

Which releases are affected? Will python-uefivars need to be in main in each release to resolve this?

Is a Coordinated Release Date of the last week of January to release fixes reasonable?

Revision history for this message
Mark Esler (eslerm) wrote :

Please refer to this issue as CVE-2023-49721.

Revision history for this message
dann frazier (dannf) wrote :

I've posted some thoughts about patching Ubuntu's edk2 packages in bug 2040137.

Since it was mentioned, just an fyi that I'm working on packaging virt-firmware, initially to provide a migration path away from 2M images for Debian/Ubuntu edk2 users:
  https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1055987
And, eventually, I plan to replace edk2-vars-generator.py in the edk2 build with that.

I don't know that we need to switch edk2 builds over to that before we can patch this. We can always generate our pre-enrolled VARS images at build time using an image that does have a shell, whether or not we choose to continue shipping such an image in the deb.

Revision history for this message
Christian Ehrhardt  (paelzer) wrote :

Hi, I just got notified of this and bug 2040137 and catching up.

> Will python-uefivars need to be in main in each release to resolve this?

No it would not need to be. It would only be a build time dependency right? Replacing the use of the intermal shell in edk2-vars-generator.py. Would we want this to be high quality - yes, would we want it to be in main - I guess, but just strictly looking at the rules it does not have to be in main as a pure build time dependency.

But DannF had multiple alternatives to that, either use a non secure-boot image to enroll (minimal change, but I'm not sure that would apply to the LXD build). Or using virt-firmware once available, same constraints as for python-uefivars would apply.

Revision history for this message
Mate Kukri (mkukri) wrote :

I've posted a comment on #2040137 . Would it be possible to merge the discussion on these somehow?

Revision history for this message
Christian Ehrhardt  (paelzer) wrote :

I was wondering the same as we all started to say "and also here ...".
Marked as a duplicate - 2040137 is the one that remains.

Mate Kukri (mkukri)
information type: Private Security → Public Security
To post a comment you must log in.
This report contains Public Security information  
Everyone can see this security related information.

Other bug subscribers

Remote bug watches

Bug watches keep track of this bug in other bug trackers.