Follow @Openwall on Twitter for new release announcements and other news
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20200620222314.GA14426@openwall.com>
Date: Sun, 21 Jun 2020 00:23:14 +0200
From: Solar Designer <solar@...nwall.com>
To: lkrg-users@...ts.openwall.com
Subject: Re: rootkit detection

On Sun, Jun 14, 2020 at 08:21:39PM +0200, Mikhail Morfikov wrote:
> On 14/06/2020 17:37, Solar Designer wrote:
> > Of course, none of these rootkits tried to bypass LKRG.  If they wanted
> > to, they could e.g. simply "rmmod p_lkrg" before loading themselves into
> > the kernel.  (Or the attacker could do that manually.)  In LKRG
> > development, we're currently more focused on exploits run before the
> > attacker got legitimate-looking privileged access to the system.  We're
> > not as focused on rootkits, which are loaded with legitimate-looking
> > privileged access.
> 
> Removing the LKRG module via "rmmod p_lkrg" sometimes may not be so easy. In the
> case of my Debian system, I have the Secure Boot mode enabled. Also since kernel
> 5.4 there's the kernel lockdown mechanism, and in Debian it also was extended by
> the CONFIG_LOCK_DOWN_IN_EFI_SECURE_BOOT option, which automatically sets the
> kernel lockdown mode when the system is booted with Secure Boot enabled. So with
> the kernel lockdown, you can't load modules that weren't signed by some verified
> keys, and loading modules like the Keysniffer would simply fail in such systems.
> I also removed the default EFI certs and added my own certs to the firmware in
> the place of the older ones to make sure that the kernel/bootloader (and
> anything else) can be loaded only when it was signed by me, and only me.
> 
> Also even if someone doesn't use the Secure Boot/Kernel lockdown mode, there's
> CONFIG_MODULE_SIG_FORCE, which can prevent loading unsigned modules and stop
> them from doing some harm in the system, though it's not default in the distro
> kernels. But having either one setup would make extremely hard or impossible to
> load any additional unsigned modules in a working system.

Sure there are ways to harden the system like that.  However, as long as
those protections work, LKRG's LKM rootkit detection is irrelevant.  And
when they fail, the LKRG unload attack (or its variation) would work.

Besides, someone with legitimate-looking root access can also disable
LKRG via sysctl's.  So hardening LKRG against rmmod only makes sense
along with hardening against sysctl.  Like I wrote, in a future version
both interfaces might be optionally password-protected (or disabled).

> Even if none of the above solutions would be used, there's still the
> CONFIG_MODULE_UNLOAD option that can prevent the LKRG module from being
> unloaded, but since LKRG depends on this option (it has to be set to "yes"), it
> asks itself for troubles, especially on the systems where Secure Boot/Kernel
> lockdown isn't an option. So not forcing people to have CONFIG_MODULE_UNLOAD
> enabled would simply help the LKRG module (and other security modules) from 
> being unloaded in such cheap way.

Yes, we should probably enhance LKRG to support builds without
CONFIG_MODULE_UNLOAD.  However, that wouldn't currently matter for
security because of the sysctl's.

> Also there's, a kernel sysctl option provided by LKRG, which is
> lkrg.block_modules, but this is rather weak now, since it simply block loading
> of modules when it's set. It actually doesn't prevent the LKRG module from being
> unloaded. But there's also the sysctl option kernel.modules_disabled, and this
> one (when set) blocks loading/unloading of modules, and reboot is required to
> change the value of this parameter. So to prevent LKRG from being unloaded, a
> user can set that last option to "1", but it always has to be done in working
> system when it boots, so it's not as strong as CONFIG_MODULE_UNLOAD, but it's
> better than nothing. :)

FWIW, here's my tentative proposal from a few months ago for replacing
lkrg.block_modules, which we ended up not doing yet:

---
modules_enforce 0 no enforcement, 1 log only, 2 block loading, 3 block loading and unloading, 4 also lock this setting itself
(lkrg.modules_enforce=4 will be same as kernel.modules_disabled=1, except it'd also have LKRG's logging)
---

Do you like this?  We might revisit it later.  One reason why we didn't
go for it yet is that it'd be a tiny subset of the functionality to be
expected from a securelevel-alike feature.  Adam actually had more of
the securelevel-alike functionality in LKRG experimental branch, which I
didn't like because I'd expect very few LKRG users to manage such
systems right (it's tricky, inconvenient, and time-consuming).  That
branch is now abandoned and we don't intend to revitalize it.  Besides,
upstream Linux got this lockdown thing instead of securelevel anyway.

This proposal also didn't fit in well with our other *_enforce controls,
where there's no "no enforcement" option (and the value 0 means logging)
because there's a *_validate control where we have "disabled" as an
option.  This would be the only *_enforce without a *_validate,
resulting in this inconsistency.  Maybe using -1 for "no enforcement"
would be cleaner, so that the rest isn't shifted vs. other *_enforce?

For now, we're going to document lkrg.block_modules as follows:

---
- lkrg.block_modules (0)
  Whether or not to block further loading of kernel modules.  Allowed values
  are 0 (no) and 1 (yes).

  This feature is meant primarily to prevent unintended user-triggered (or
  attacker-triggered) auto-loading of maybe-vulnerable modules provided in a
  distribution after all intended modules have already been loaded.  This
  feature is not effective (nor is meant to be) against attackers who already
  have root privileges and try to load a module explicitly (they could simply
  flip this setting or even unload LKRG first).
---

Alexander

Powered by blists - more mailing lists

Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.