9 ms·
OpenBSD system-call-origin verification
- Animats 7y agoThis effectively treats libc as a trusted interface. Checking should be at the protection boundary, the system call. This just makes attacks more complicated, not less successful.
- miohtama 7y agoIn a long run the non-complicated solution is to get rid of anything that can compile to something with a buffer overflow. Just half trolling.
- jacquesm 7y agoThere goes Rust I guess...
- pjmlp 7y agoI doubt we will ever see a UNIX clone written in Rust. An OS that happens to expose some support for POSIX, yeah (e.g. Redox), but anything else wouldn't be an UNIX clone per se.
- clarry 7y agoAnd that's all good, we don't need more people cloning an OS that was good 50 years ago. Modern software is sidestepping the UNIX way left and right. Rightly so, as anyone who's tried to write anything serious in shell would know. Or anyone who realizes that non-blocking I/O was an afterthought (look into the history of select(2) and why libraries like libev exist and what problems they run into on unix derivatives..) and that there's still nothing like a standard, robust, well designed async I/O API.
- pjmlp 7y agoUNIX was good, for an OS given away with source code and an almost free license (versus what other mainframe OS were asking for). History would be quite different if AT&T was allowed to sell it from day one.
- deleted 7y ago[deleted]
- alexgartrell 7y agoIf you can change libc then you have equal to or greater permissions than the calling process. So this strengthens the security model.
- jacquesm 7y agoDoes that mean that linking against libc is mandatory and that applications are not allowed to bring their own version of libc along for instance when linked statically?
- saagarjha 7y agoStatically linked programs automatically lose this protection, apparently.
- ori_b 7y agoPartly. If they jit, or have shellcode injected, that code is not mappable with syscall privilege.
- tedunangst 7y agoNo. There's nothing special about libc. (Oh, double checked, and uh, maybe. ld.so does treat libc specially if it finds it in the library list, but at that point, you are linking with libc. But afaik there's no requirement you use the system ld.so either.)
- jacquesm 7y agoThen isn't Animats' point valid?
- tedunangst 7y agoThe point of doing this is not to protect against people bringing their own bad libc.
- 7y ago
- vlovich123 7y agoYes it’s more complicated but that’s how all security works and why security exploits are more expensive today than before. The reason this defense requires libc is because syscalls are not a function calls (you push args onto the stack and then trigger a software interrupt). This defense technique is both simple (in terms of relative change from how things work today) and effective in that buffer overflows can’t ever call into the kernel but now instead have to go through libc which has had its location randomized on the application launch. To provide this protection at the syscall layer would require some kind of randomization of how a syscall is performed which is something that has no prior art since that’s just pushing arguments into the stack and triggering an interrupt through a single instruction meaning complexity of the work is unknown in terms of how to secure a buffer overflow from being able to do that (ie harden an arbitrary process to not be able to directly jump into a syscall). Additionally because there’s no prior art the scope of that kind of change wouldn’t have the benefit of having well-understood behavior. Not impossible or prohibitive but raises the bar for the benefit you’d have to provide (& probably get a chip manufacturer and compiler vendors to go along with you). Also even if you could 3rd party binaries wouldn’t gain that protection magically. Putting this in libc is fantastic. Arguably Linux’s decision to treat libc as an external unrelated project and keep ABI comparability at the syscall layer prevents a protection mechanism like this.
- Animats 7y agoYes it’s more complicated but that’s how all security works and why security exploits are more expensive today than before. That's how security theater for software works. Today's attackers are well-funded and technically competent. This is a a defense against script kiddies. The way to get rid of buffer overflows is to get rid of C in trusted code, not requiring users to go through libc. It isn't hard any more. We have Go, Rust, C#, Java, and even node.js and Python for server side. If you're running C in a user-facing server application, you're doing it wrong.
- saagarjha 7y agoI hope you're not suggesting that modern mitigations such as W^X and ASLR are "security theater" :/
- saagarjha 7y ago> Checking should be at the protection boundary, the system call. This doesn’t make sense, though. This would mean the OS would need to know the control flow of the program itself to figure out whether it “should” allow a system call to proceed.
- gpvos 7y ago> Since OpenBSD is largely in control of its user-space applications, it can enforce restrictions that would be difficult to enact in a more loosely coupled system like Linux, however. In which ways is OpenBSD more in control of userspace applications, and in which ways is Linux more loosely coupled? > Switching Go to use the libc wrappers (as is already done on Solaris and macOS) Did Solaris and macOS also do that for security reasons? (A linked article mentions ABI instability as the reason, but maybe there's more to it.)
- monocasa 7y agoIt's just ABI instability. In BSD, Solaris, and MacOS the kernel and libc are distributed by essentially the same people as an atomic unit, and the syscall layer changes over time. On Linux, it's the syscall layer that's stable.
- pas 7y agoIt's entirely doable in Linux too. Introduce new syscalls for the new interface, plus one lockdown functionality that disables the old interface so sysadmins/distributions can switch to the new interface. Of course this can and should be coupled with compile time switching, so eventually the old interface will become deprecated, and if no one steps up to maintain it, it will be removed. It's simply a different approach to doing backward compatibility. (Instead of maintaining very different trees for different versions, the functionality is concurrently available in multiple versions, but with feature switches [compile- and run-time configuration].)
- ori_b 7y ago> and if no one steps up to maintain it, it will be removed. "You don't break userspace". How many decades until people who no longer have source for the broken binaries that they run will stop complaining? How long until sysadmins know to turn the knobs on? How many knobs are sustainable? How much will they interact, and in how many ways will those interactions expose vulnerabilities? The Linux way to introduce this is hell for everyone involved.
- blinskey 7y agoFolks interested in this sort of news might also enjoy the OpenBSD Journal, which covered this recently, albeit more succinctly: https://undeadly.org/cgi?action=article;sid=20191202105849 https://undeadly.org/cgi?action=article;sid=20191202105849
- claudiawerner 7y agoTotally off-topic, but it's interesting how the URL uses ; as the GET parameter separator rather than &.
- pas 7y agoThe internal syntax and semantics of the query part is not really defined by HTTP. (https://tools.ietf.org/html/rfc3986#section-3.4 https://tools.ietf.org/html/rfc3986#section-3.4) Also: "Historic RFC 1866 (obsoleted by RFC 2854) encourages CGI authors to support ';' in addition to '&'" https://en.wikipedia.org/wiki/URL#cite_note-23 https://en.wikipedia.org/wiki/URL#cite_note-23
- haecceity 7y agoSo what's stopping the attacker from searching the libc export table for the right syscall wrapper?
- saagarjha 7y agoASLR, for one.
- haecceity 7y agoCan't they use dlopen to find libc?
- asveikau 7y agodlopen is a dynamically linked function, so they would have to find that too. Its location will also be randomized. Note we are talking about exploit code here, i.e. you have just exploited a buffer overflow, not ELF code loaded in a well behaved fashion.
- saagarjha 7y agoThere are easier ways to leak libc, such as reading from the stack or the GOT, but these would require more work than a simple ROP chain.
- gok 7y agoThis is a pretty weak security mitigation since any ROP attack will typically achieve return-to-libc anyway, but yelling at people who make syscalls directly is good.
- saagarjha 7y agoI think it might protect against JIT shellcode.
- throwaway2048 7y agoYou still need an information leak of exact address of a libc function to achieve that, that will be different on every program invocation due to ASLR, and brute forcing is useless on a 64bit memory space. Even information leaks about the location of other less useful libc functions + offset isn't enough, because OpenBSD randomly relinks libc every boot, so just having the libc of a release to harvest offset information isn't useful.
- nineteen999 7y ago> but yelling at people who make syscalls directly is good. Why though? Doesn't this mean that all non-C based languages are going to be treated somewhat as second class citizens, having to link the standard C library (eg. as Go is doing on some platforms) in order to call into the kernel? While languages such as Go and Rust are aiming to replace/displace C due to it being designed in an age where security was considered less of an issue, it seems counter-intuitive to me that we should insist that they should link in the apparent attack surface of the standard C library. The syscall boundary seems an ideal place to make the delineation between the kernel and userland via an established API, and I would have expected that languages that want to displace C be able to use that interface directly in order to bypass the standard C library. That would seem to allow userlands to be built that include no C code whatsoever. But I'm very obviously no expert.
- throwaway2048 7y agoNothing about this prevents applications from being built that do not depend on libc, this is a flag set on elf binaries that the program loader honors, tooling to build things that really want to generate syscalls can just flag all sections as syscalls allowed.
- jedisct1 7y agoAnd then WebAssembly will ruin all these efforts.
- quotemstr 7y agoI strongly support this policy not only for security, but also for general operating system robustness. Linux is pretty much the only system that places the ABI compatibility boundary and the machine privilege level boundary in the same place in the stack. I think that's the wrong place: it pushes a lot of complexity that could be in userspace into the kernel, because the kernel is the first place past the ABI break. Linux would be better if nobody except libc were allowed to make system calls and we just made libc (or a giant VDSO) the ABI support boundary. We should at the very least have a VDSO for every system call. There should be some opportunity to run code in userspace before a privilege transition. Doing so would give us a lot more flexibility than we have now. For example, consider socketcall(2): for a long time, all Linux socket system calls (like recvmsg) were multiplexed through a single system call. A few years ago, the kernel community realized that this multiplexing was a bad idea and made individual system calls for all the traditional socket operations. But since we have to support old programs, we have socketcall(2) and the new system calls in the kernel. Why should we? If the ABI support level had been libc all along, we could have changed the system call strategy (from multiplexing to fine-grained calls) transparently without bloating the kernel with compatibility code.
- saagarjha 7y ago> We should have a VDSO for every system call. This doesn't really make sense; the vDSO only works for system calls that do nothing but read a value from the kernel and return it to userspace.
- quotemstr 7y agoNot the case. Some VDSOs just forward to regular system calls. Try clock_gettime with CLOCK_BOOTTIME sometime. (I need to send a patch to fix this. Maybe over Christmas.) Requiring that every system call go through a VDSO would be great because it would give us a "hook" for changing system behavior before entering the kernel, which is a good thing, because in the age of speculative execution mitigations, entering the kernel is expensive, just like in the days of yore.
- 7y ago