Tuesday, October 20, 2020

kernel Lockdown

No more easy tasks during the "kernel Lockdown"

No more easy tasks during the “kernel Lockdown”

Today I wanted to show my Exein fellow how to extract a standard x86_64 Bios image from a live PC.
Quite easy and common task that should not have any issue.
So I triggered the dd command to accomplish the task:

sudo dd if=/dev/mem of=bios.rom bs=64k skip=15 count=1

No problem was expected; still something weird caught my attention:

$ sudo dd if=/dev/mem of=pcbios.rom bs=64k skip=15 count=1
dd: failed to open '/dev/mem': Operation not permitted

Investigating on the issue, it came out that a reasonably new LSM was acting behind the scenes preventing me from accomplishing the easy task I wanted to do.

$ sudo cat /sys/kernel/security/lockdown 
none [integrity] confidentiality
$ dmesg
[    0.000000] Kernel is locked down from EFI Secure Boot mode; see man kernel_lockdown.7
[...]
[    0.787348] Lockdown: swapper/0: hibernation is restricted; see man kernel_lockdown.7
[...]
[   21.638946] Lockdown: systemd: /dev/mem,kmem,port is restricted; see man kernel_lockdown.7
[...]
[   24.578882] Lockdown: Xorg: raw io port access is restricted; see man kernel_lockdown.7
[...]
[   26.249451] Lockdown: systemd-logind: hibernation is restricted; see man kernel_lockdown.7
[...]
[   26.254389] Lockdown: systemd-logind: hibernation is restricted; see man kernel_lockdown.7
[...]
[  921.376121] Lockdown: dd: /dev/mem,kmem,port is restricted; see man kernel_lockdown.7

Now the question is, what this Lockdown is, and what is it doing on my PC.
According to LWN, Lockdown is a new way to keep the kernel secure from cyber-attacks, which is part of the Linux kernel starting from Aug 20, 2019, when the commit 000d388ed3bbed745f366ce71b2bb7c2ee70f449 integrated it within the official main branch of the Linux Kernel.

This was the 5.4-RC1 version.
What exactly does this Lockdown restrict?
First off, it will restrict access to kernel features that may allow arbitrary code execution, block processes from reading/writing to /dev/mem, and /dev/kmem memory, and /dev/port.
Other features include:

  • Enforcing kernel module signatures.
  • Prevents even the root account from modifying the kernel code.
  • Kexec reboot.
  • Restrict the use of hardware that could potentially generate direct memory addressing (DMA).
  • Restrict the use of KDADDIO, KDDELIO, KDENABIO and KDDISABIO console ioctls.

There are two modes available to the Lockdown module: Integrity and Confidentiality. When in Integrity mode, kernel features which would allow userland code to modify the running kernel are disabled. When in Confidentiality mode, userland code to extract confidential information from the kernel will be disabled.
At the end of the day, Lockdown is installed by default on many Linux distributions, including the one I was using.

This also means, It is like I can not dump the BIOS right now.

Now let’s see what can be done to disable it.
Where to start, the kernel log mentioned kernel_lockdown.7; let’s start from here!

Quick note: the distro I am using, Linux Mint 20 does not include the mentioned man page.

Sure, I can find it on the internet; for example, here is the man page mentioned in the kernel logs.

According to this source and others I found on the internet, there’s no way to disable Lockdown LSM but by being at the main console and pressing a SysReq combination: “SysReq+x”.

Trying to do what is suggested, nothing happened, and looking in the kernel message log, the following message appeared:

sysrq: HELP : loglevel(0-9) reboot(b) crash(c) terminate-all-tasks(e) memory-full-oom-kill(f) kill-all-tasks(i) thaw-filesystems(j) sak(k) show-backtrace-all-active-cpus(l) show-memory-usage(m) nice-all-RT-tasks(n) poweroff(o) show-registers(p) show-all-timers(q) unraw(r) sync(s) show-task-states(t) unmount(u) force-fb(V) show-blocked-tasks(w) dump-ftrace-buffer(z)

which in turn indicates the combination has not been implemented.
Investigating on this latter, more obscure scenario is shown; in the Linux Kernel source tree, as far as the last available Linux Kernel version (5.9.1), there’s no evidence this combination has been ever implemented.

*sysrq_key_table[36] = {
[...]
        &sysrq_showstate_blocked_op,    /* w */
        /* x: May be registered on mips for TLB dump */
        /* x: May be registered on ppc/powerpc for xmon */
        /* x: May be registered on sparc64 for global PMU dump */
        NULL,                           /* x */
        /* y: May be registered on sparc64 for global register dump */
        NULL,                           /* y */
        &sysrq_ftrace_dump_op,          /* z */
};

Further investigations have shown that the mentioned feature has indeed been proposed, but it has not yet been merged with the main branch.

Looking carefully at the official Linux kernel code, another fact is realized:
There’s not even a location within the official Linux Kernel source tree that changes the kernel_locked_down, the variable that keeps the LSM status.
This leads to another question: why the Lockdown is enforced on my PC?
Reading at the Kernel log messages, one of the very first lines suggest the cause…

[    0.000000] Kernel is locked down from EFI Secure Boot mode; see man kernel_lockdown.7

Looking at the message text, it can be guessed that a call to lock_kernel_down("EFI Secure Boot mode", LOCKDOWN_INTEGRITY_MAX) may have this message generated, but it is not yet clear where this call has been issued. Considering the message timestamp, it appears clear that it must be an early just after the kernel takes the control.
The function setup_arch(char **cmdline_p) in the /arch/x86/kernel/setup.c file seams to be a perfect fit for this call. Investigating on this direction, another patch can be found on the internet.

Situation now is that Linux Mint 20, but also Ubuntu 20.04 are using a Linux Kernel with an unofficial patch for implementing the Secure Boot Lockdown trigger, but they do not bother to implement any unofficial mechanism to disable the Lockdown if needed.
Why has this happen is not known, but many discussions can be found on the necessity EFI Secure boot to enable the Kernel Lockdown. LWN, Linux Journal, Linux GIT
Back to the main topic, read /dev/mem, please note that Lockdown do not expect to reduce the Lockdown level.

static int lock_kernel_down(const char *where, enum lockdown_reason level)
{
        if (kernel_locked_down >= level)
                return -EPERM;

        kernel_locked_down = level;
        pr_notice("Kernel is locked down from %s; see man kernel_lockdown.7\n",
                  where);
        return 0;
}

So the fact is that at this moment, I can do nothing to read the Bios and show my colleague how easy it can be doing such a thing.

Am I still sure it is easy?!

That’s what brought me to the following solution to decrease the Lockdown level.

To compose a solution I’ve been through a few issues:

  • The symbol kernel_locked_down is not exported.
  • Starting from Kernel 5.7 kallsyms_lookup_name, a function that does the essential task of looking for unexported symbols, is no more exported.
  • Due to the fact that Kernel Lockdown level is LOCKDOWN_INTEGRITY_MAX, install unsigned modules is restricted.