Follow @Openwall on Twitter for new release announcements and other news
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20181118132923.GA31102@openwall.com>
Date: Sun, 18 Nov 2018 14:29:23 +0100
From: Solar Designer <solar@...nwall.com>
To: lkrg-users@...ts.openwall.com
Subject: Re: LKRG Exploit Detection bypass (LOL again)

Hi Ilya,

On Sun, Nov 18, 2018 at 01:49:32AM +0400, Ilya Matveychikov wrote:
> One more bypass:
> 
> https://github.com/milabs/kernel-exploits/commit/3c9758def171b672d4df4318514efc2bfb4ce8e5

Thanks, this is quite helpful to our current discussion.  Here's a peek
(excerpts from two of my e-mails to Adam yesterday):

On Sat, Nov 17, 2018 at 07:04:25PM +0100, Solar Designer wrote:
> I think LKRG possibly already went too far by having CI at all, and as a
> non-optional feature.
> 
> And yes, maybe we should just accept that an exploit going after a very
> specific kernel version and calling kernel functions can do whatever it
> wants.  So maybe we shouldn't defeat Ilya's bypass.
> 
> As an alternative to UMH, he could probably call lower-level
> implementations behind chmod() and chown(), which are normally used past
> privilege checks - lookup the dentry, lookup the inode, change the
> inode in 3 separate calls or so.  This could become filesystem type
> specific if he's not reading the corresponding function pointers from
> structs (would need to locate those structs first), but so what.

Assuming your new bypass works, you've demonstrated that only the dentry
needs to be looked up explicitly (I think I erroneously recalled ancient
APIs pre-dating "struct path"; I'm rusty) and that it's possible to use
the generic simple_setattr().  When I grepped the kernel tree for this
yesterday, I ended up thinking simple_setattr() would only be right for
fs types without an inode->i_op->setattr.  Now I see
ramfs_file_inode_operations defines .setattr to simple_setattr, so maybe
that's why it works for you - I guess on tmpfs?

Anyway, it's helpful to have this approach validated.  So thanks again.

There's also:

On Sat, Nov 17, 2018 at 08:51:26PM +0100, Solar Designer wrote:
> A simpler and more portable alternative would probably be
> override_creds(), *chmod(), *chown(), revert_creds().  This is probably
> similar to what kernel nfsd does legitimately.

We didn't support override_creds() in LKRG before 0.5, but that caused
occasional false positives.  Adam reluctantly added an exception for
override_creds() ... revert_creds() in 0.5.  Exploits can use this too,
mimicking normal kernel behavior.

There are many other convenient bypass possibilities as well.  Adam's
thinking is we could detect many (ab)uses of kernel APIs from unexpected
places by analyzing call chains (through stack frames).  My thinking is
we didn't intend to become a control flow integrity enforcement project,
and doing so from an LKM would be at best difficult.

Then, if we're to provide "security through diversity" only, then even
having non-optional code integrity checking is excessive - exploits
typically don't patch kernel code, but only modify process credentials.
LKRG's code integrity checking does happen to detect some pre-existing
rootkits, though:

https://www.defensive-security.com/blog/playing-with-linux-kernel-runtime-guard-lkrg

Discussion on Twitter:

https://twitter.com/cr0nym/status/1063368081530077184
https://twitter.com/solardiz/status/1063782606779469824

So rather than try to address these bypasses via ROP chains, maybe we
should take a step back and make CI optional, to be able to more
consistently target only typical exploits that don't try to bypass LKRG.

On the offensive side, you could want to look into making exploits such
as Andrey's PoC you started with more portable across different kernel
builds.  Instead of hard-coding many per-kernel offsets, can you
possibly find and program a weird machine that would pattern-search for
all other offsets it needs?  Would this weird machine program require
fewer hard-coded addresses than it needs to find for the actual exploit?
If so, it'd be a net win.  Pattern search is easier done when you're
running custom code (I had pattern search for WinExec in my Windows NT
shellcode in late 1996 for some portability across Windows builds while
not dealing with lower-level syscalls directly), and is harder to do
from a ROP chain (I'm not aware of this having been implemented yet - so
it sounds like fun and an appropriate challenge for you now).  This
would be relevant both with and without LKRG.

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.