[PATCH] Deny preload of files on NOEXEC mounts

Jann Horn jannh@google.com
Mon Aug 23 21:09:19 GMT 2021


On Mon, Aug 23, 2021 at 1:30 PM Florian Weimer <fweimer@redhat.com> wrote:
>
> * Jordan Abrahams:
>
> > In response to Alexander,
> >
> >> this is not about refusing mmap from a noexec mount, this is a counter-measure
> >> against specially crafted ELF files taking over the dynamic loader even before
> >> it attempts a PROT_EXEC mmap, as demonstrated in glibc bug #21718
> >
> > Indeed, it's not about refusing an mmap from a noexec mount. These are
> > handcrafted ELF files which take over the loader. If you would like to
> > see the proof of concept of the attack, we can add you to the allowed
> > listings for the linked bug thread
> > (https://bugs.chromium.org/p/chromium/issues/detail?id=1182687). We
> > have an ELF for x86_64 devices which shows the above behaviour on our
> > current kernel of ChromeOS. I'm uncertain of the status of other OSes
> > (I would need to reach out again to our security team), but I doubt it
> > would be restricted to just ChromeOS.
>
> There have been public discussions about such bugs before, not sure why
> you are restricting it.
>
> I expect that it will always be possible to take over the dynamic loader
> until we are more restrictive when it comes to use of MAP_FIXED in the
> loader, probably by using MAP_FIXED_NOREPLACE.  The challenge is to make
> the change so conservative that existing (slightly broken, or ia64)
> binaries are not impacted.

My understanding is that the ChromeOS security folks (I'm not on that
team) would like to ensure that, as a security hardening measure,
attempting to load an attacker-controlled library from a noexec mount
doesn't give the attacker the ability to run code in the context of
the loader.

As far as I understand, the kind of ELF file I used in the linked bug
(sorry about the access restriction) looks fairly legitimate: It
doesn't clobber any existing mappings, instead it uses a ".init_array"
section containing a relocation into glibc, like this:

        .section        .init_array,"aw"
        .align 8
        .quad   rmdir+0x774

By picking an appropriate gadget in glibc, and making use of the
register contents at the indirect call to the fake constructor
function, it is possible to use that to e.g. start a ROP chain.


I think there are also more fundamental things that a library without
any executable segments could do, by design, to compromise the
integrity of the process - e.g. depending on when the evil library is
loaded, it might be able to override a pointer symbol that was
supposed to be located in a ".bss" section to point at the evil
library's ".data" section instead and use that to effectively clobber
a NULL-initialized pointer variable with an arbitrary value, or
something like that?

This is why I think that as long as you accept the premise that NOEXEC
should robustly prevent library loading, the loader (possibly in
collaboration with the kernel) has to ensure that even libraries
without executable segments cannot be loaded from non-executable
files.

(Btw, this also means that IMA-based auditing of library loading based
on executable mappings is currently bypassable. I don't think anyone
really cares about that though - I brought it up with the IMA
maintainers back in 2018 and IIRC they didn't see it as a problem.)


More information about the Libc-alpha mailing list