Comment by woodruffw
2 years ago
Without a pre-formed opinion: does anybody have an intuition for the security benefits this provides? My first thought is that it’s primarily mitigating cases of attacker-introduced shellcode, which should already be pretty well covered by techniques like W^X. Code reuse techniques (ROP, JOP, etc.) aren’t impacted, right?
I would also think this would cause problems for JITed code, although maybe syscalls in JITed code aren’t common enough for this to be an issue (or the JIT gets around it by calling a syscall thunk, similar to how Go handled OpenBSD’s earlier syscall changes).
> Code reuse techniques (ROP, JOP, etc.) aren’t impacted, right?
Unless I'm mistaken, this should restrict what you can do with ROP gadgets that contain syscalls. You will only be able to use the gadget with its intended arguments, since other syscall types will be disallowed.
> I would also think this would cause problems for JITed code
They can probably just jump into precompiled code that performs the needed syscall. Also, making syscalls directly from something like JITed JavaScript is generally avoided anyways. AFAIK browsers don't even let the processes that run JavaScript touch much of the system at all, instead they have to use an IPC mechanism to ask a slightly more privileged process to perform specific tasks.
> You will only be able to use the gadget with its intended arguments, since other syscall types will be disallowed.
That makes sense, although "intended" arguments here means still being able to invoke `execve(2)`, etc., right? The gadget will still be able to mangle whatever it likes into the arguments for that syscall; it just won't be able to mangle a `wait(2)` into an `execve(2)`, I think.
Your points about JITs make sense, thanks.
Yes, that's how I understand it; you can't mangle one syscall into another, but you can still mangle the syscall's other argument values.
The other comment on this thread mentions that it also does something else:
>disables all the system calls not explicitly invoked by the program text of a static binary
This means that if the original library didn't have an execve call in it, you would'nt be able to use it even if with ROP. In short, this seems useful to block attackers from using syscalls that were not originally used by the program and nothing else. It can be useful.
Sure, assuming your programs don't execute other programs. I don't know much about OpenBSD specifically, but spawning all over the place is the "norm" in terms of "Unix philosophy" program design.
(I agree with the point in the adjacent thread: it's hard to know what to make of security mitigations that aren't accompanied by a threat model and attacker profile!)
*Agent Smith voice*
But what use is a fork() if you're unable to exec()?
It is now less normal for programs to run programs on openbsd.
> assuming your programs don't execute other programs.
What about language runtimes? They don't execute other programs in the sense of ELF executables (although the programs they interpret might), but they have to support every syscall that's included in the language. So, for example, the Python interpreter would have to include the appropriate code for every syscall that Python byte code could call (in addition to whatever internal syscalls are used by the interpreter itself). That would be a pretty complete set of syscalls.
6 replies →
> Code reuse techniques (ROP, JOP, etc.) aren’t impacted, right?
One thing to note is that system calls can no longer be made from the program's .text section; only from within libc. This is highly important because of ASLR: in order to ROP into a syscall, an attacker must now know where libc is located in the virtual address space. Before this mitigation, an attacker that only knew the address of the program binary could search for a sequence of bytes within the .text section that happened to decode to a syscall instruction, and use that for ROP (code reuse techniques can often access a lot of unexpected instructions by jumping into the middle of a multibyte instruction, due to x86's complex and variable-length instruction encoding).