Live data from Hacker News

mimmutable() for OpenBSD

lwn.net

11–20 of 41 posts

Re: mimmutable() for OpenBSD

#11
post #6

>One of those marks executable memory that is empowered to call into the kernel; on OpenBSD systems, only the C library is given that capability. That will prevent hostile code loaded elsewhere from making direct system calls; protecting the rest of a process with mimmutable() will prevent the changing of protections to allow system calls from elsewhere (such changes would be done with msyscall() on OpenBSD). This is…

> you can simply JOP to one of the authorized syscall instructions and entirely defeat the mitigation But now you need to know where the libc is mapped in order to find those syscalls instructions yeah? Which increases the complexity of the exploit, because instead of making a syscall you have to find where the libc is mapped, and the entire point of ASLR is making that difficult, no?

The main binary has to be able to call into libc somehow, and thus it's typically easy to get a libc pointer if you have an existing method of code execution.

I don't know how it works on OpenBSD, but on Linux there's a section of memory called the PLT that contains pointers for all the functions from dynamic libraries, including those from libc. Also, there's typically libc pointers all over the stack.

Re: mimmutable() for OpenBSD

#12
post #6

>One of those marks executable memory that is empowered to call into the kernel; on OpenBSD systems, only the C library is given that capability. That will prevent hostile code loaded elsewhere from making direct system calls; protecting the rest of a process with mimmutable() will prevent the changing of protections to allow system calls from elsewhere (such changes would be done with msyscall() on OpenBSD). This is…

> you can simply JOP to one of the authorized syscall instructions and entirely defeat the mitigation But now you need to know where the libc is mapped in order to find those syscalls instructions yeah? Which increases the complexity of the exploit, because instead of making a syscall you have to find where the libc is mapped, and the entire point of ASLR is making that difficult, no?

If you've managed to map shellcode at all, you necessarily already know where a valid syscall instruction is since otherwise you would not have been able to trigger the mmap syscall to map the shellcode. In any event, mapping shellcode is entirely optional anyways since to even get to that point you must already have full control through ROP, at which point you could do everything the program could (albeit in a significantly more annoying way).

Re: mimmutable() for OpenBSD

#13
post #9

Earlier quoted context omitted.

> trying to block code execution against an attacker who already has kernel read/write is an utterly ridiculous idea iOS does this. Kernel code is immutable (obviously), page tables are locked at reset and cannot be modified except by PPL code. ROP and JOP are mitigated against using hardware CFI (PAC). As you mentioned, when vendors actually understand what exploits look like and what attackers need to do, it enable…

I believe I should have couched that statement with a "for most everyone else" :) The PPL is a strong mitigation, I agree, but it is definitely one that is really only supported through their ability to ship custom hardware. Pulling off a PPL-like thing using MPK or a hypervisor is technically possible but in practice no real platform other than Apple is actually able to enforce memory mappings on all devices on an S…

Right, iOS definitely has a different threat model which requires (and takes advantage of) Apple's ability to make custom hardware. In this case, though, OpenBSD is looking to protect userspace, and they could provide guarantees for that (iOS does this via codesigning, but you could also just force security-sensitive programs to stop using dlopen).

Re: mimmutable() for OpenBSD

#14
post #6

>One of those marks executable memory that is empowered to call into the kernel; on OpenBSD systems, only the C library is given that capability. That will prevent hostile code loaded elsewhere from making direct system calls; protecting the rest of a process with mimmutable() will prevent the changing of protections to allow system calls from elsewhere (such changes would be done with msyscall() on OpenBSD). This is…

> "This too is trivially defeated. You simply need to add another stage to your payload in which you copy your ROP payload onto the legitimate stack, pivot, and continue executing."

Have you, or someone else as knowledgeable as you, been able to provide a succesful POC doing this? I admit that I haven't gone about looking for results on this topic and method of attack but I would be very interested in reading about it and see a practical example achieving it, since it's (in your own words) trivially defeated and simply needs just this or that thing.

Re: mimmutable() for OpenBSD

#15
post #2

Im not a c programmer or otherwise work this low level, so im probably missing something obvious. What is the point of making something immutable if you can just create a new write-exec region? Surely if you have enough control over the process to change permissions on an existing memory segment you have enough to mmap a new region with the permissions you want?

No, you're not missing anything. The primary reason this thing was designed was so try to patch out some holes in another unusual "mitigation", where system calls are checked to make sure they come from OpenBSD's libc. In this case adding new pages with syscalls in them would not work, but it seems like Theo read a paper at some point where they mprotected libc and patched its code and this spooked him into realizing…

> But using it to prevent the introduction of new code is not all that effective, unless you are far more stringent about how you allow processes to allocate executable regions. For example, if you prevent a program from mapping in any new PROT_EXEC pages after it's initialized itself and then mimmutable all its code, then no new code can be introduced, which is a useful property. But you need that extra bit which mimmutable by itself doesn't give you.

You mean like pledge(2)? The vast majority of the base system is pledged. So programs without the prot_exec promise cannot make new PROT_EXEC mappings, or add it to any existing pages.

https://man.openbsd.org/pledge#prot_exec

Perhaps you should spend more than 5 minutes reading about OpenBSD's layers of mitigations.

Re: mimmutable() for OpenBSD

#16
post #6

>One of those marks executable memory that is empowered to call into the kernel; on OpenBSD systems, only the C library is given that capability. That will prevent hostile code loaded elsewhere from making direct system calls; protecting the rest of a process with mimmutable() will prevent the changing of protections to allow system calls from elsewhere (such changes would be done with msyscall() on OpenBSD). This is…

If you look at the mitigations OpenBSD is doing as attack surface reduction, it means ultimately fewer tools in the attackers toolbox.

It seems many of you are missing the forest for the trees.

Re: mimmutable() for OpenBSD

#17
post #6

>One of those marks executable memory that is empowered to call into the kernel; on OpenBSD systems, only the C library is given that capability. That will prevent hostile code loaded elsewhere from making direct system calls; protecting the rest of a process with mimmutable() will prevent the changing of protections to allow system calls from elsewhere (such changes would be done with msyscall() on OpenBSD). This is…

If you look at the mitigations OpenBSD is doing as attack surface reduction, it means ultimately fewer tools in the attackers toolbox. It seems many of you are missing the forest for the trees.

It also means fewer tools in developers' toolboxes. Not being able to make your own system calls directly, or exercise control over your own address space, means that anything that doesn't conform to a C runtime won't run in OpenBSD. Perhaps that's a tradeoff that the OpenBSD developers are willing to make, but even so, these particular "mitigations" do not address the root cause of many security vulnerabilities: failure to verify untrusted input.

Re: mimmutable() for OpenBSD

#18

Earlier quoted context omitted.

If you look at the mitigations OpenBSD is doing as attack surface reduction, it means ultimately fewer tools in the attackers toolbox. It seems many of you are missing the forest for the trees.

It also means fewer tools in developers' toolboxes. Not being able to make your own system calls directly, or exercise control over your own address space, means that anything that doesn't conform to a C runtime won't run in OpenBSD. Perhaps that's a tradeoff that the OpenBSD developers are willing to make, but even so, these particular "mitigations" do not address the root cause of many security vulnerabilities: fai…

I think that only Linux had some syscall stability. Every other operating system provides libc or other kind of library with stable interface but syscalls are not stable. Yes, technically you can execute those instructions and your software will work on a fixed kernel version, but that's not what people do.

Re: mimmutable() for OpenBSD

#19

Earlier quoted context omitted.

No, you're not missing anything. The primary reason this thing was designed was so try to patch out some holes in another unusual "mitigation", where system calls are checked to make sure they come from OpenBSD's libc. In this case adding new pages with syscalls in them would not work, but it seems like Theo read a paper at some point where they mprotected libc and patched its code and this spooked him into realizing…

> But using it to prevent the introduction of new code is not all that effective, unless you are far more stringent about how you allow processes to allocate executable regions. For example, if you prevent a program from mapping in any new PROT_EXEC pages after it's initialized itself and then mimmutable all its code, then no new code can be introduced, which is a useful property. But you need that extra bit which mi…

Isn’t pledge(2) trivially easy to escape anymore? This used to be one of the differences compared to eg capsicum(2).

Re: mimmutable() for OpenBSD

#20

Earlier quoted context omitted.

It also means fewer tools in developers' toolboxes. Not being able to make your own system calls directly, or exercise control over your own address space, means that anything that doesn't conform to a C runtime won't run in OpenBSD. Perhaps that's a tradeoff that the OpenBSD developers are willing to make, but even so, these particular "mitigations" do not address the root cause of many security vulnerabilities: fai…

I think that only Linux had some syscall stability. Every other operating system provides libc or other kind of library with stable interface but syscalls are not stable. Yes, technically you can execute those instructions and your software will work on a fixed kernel version, but that's not what people do.

Lack of syscall stability isn’t a showstopper, it just means that the functions invoking the syscall need to be versioned according to uname(). The uname() call itself can be done via direct syscall if you’re confident that its ABI won’t change; or, if you want to be extra sure, you can use the libc wrapper for uname() and then munmap() the C runtime to replace it with whatever. Either way requires the maintainer to be proactive with implementing code paths for upstream upcoming kernel versions before they are mainlined.

OpenBSD will make the above approach impossible, because with mimmutable(), libc will not be unmappable.

Post reply on HN