5 ms·
How many exploits are made unreasonably hard just by making the executable code execute-only? Also, what about making syscall table something that is passed to
by fwsgonzo 6y ago
How many exploits are made unreasonably hard just by making the executable code execute-only?
Also, what about making syscall table something that is passed to each process on start - containing syscalls in random order?
I largely don't care about the current reality of things. Having fixed system call numbers, fixed function order, merged text and rodata - all these things just make me tired and want to start over with the whole thing.
Also, is nobody concerned that JIT is normalizing creating new executable code at run-time?
- unwind 6y agoUh, if the syscalls are in random order how would your code know how to reach the proper one? If there is identifying meta-data (such as names) associated with each then I guess malicious code can parse that too, or steal it from some authorized source after that has parsed it, or whatever. On the other hand, I s*ck at security so perhaps this is the best idea ever ... honest question.
- fwsgonzo 6y agoWell the table would have fixed indexes that translate either into addresses that go to trampoline functions that would then execute the system call, or just some kind of translation 0 -> write etc. It's not important. That said, I have no idea what works and what doesn't for security. I'm just asking questions, too. :)
- fuzzybear3965 6y agoI'm confused about this. I think you understand what you're saying. I've seen your RISC-V runtime library. Can you explain in a bit more detail how I navigate this randomized table as a syscall user to call the right one? I call socket(), say. And that is mapped to index 12 of the randomized syscall table. How is it found at runtime?
- fwsgonzo 6y agoWhen Linux loads your program it actually puts quite a bit of stuff on the stack. The first thing it puts is the argc followed by an array of argv pointers, followed by a zero. After that it puts the environment variables, followed by a zero. And then it puts the aux-vector, which contains lots of useful things. And that's where you could pass the address of a system call translation/jump table. This aux-vector is already used to enable multi-threading, because it contains the necessary information to create the thread-local storage. One drawback here is that your simple hello-world write/printf in assembly becomes complicated, because you need to get the table address so you can access the write/exit information. As far as what this would help for security, I have no idea, other than making it harder to exploit cases where you don't have a lot of room. We don't hear a lot about all the exploits that didn't happen because of all the extra hoops you have to jump through with each change over the years.
- Karliss 6y agoOnce you get to execute arbitrary code there is little you can't do that the real program can, but unless everything is very bad a vulnerability won't allow you to directly execute arbitrary malicious code. A buffer overflow might only allow you to overwrite few bytes or do a single jump. Attacker then has to leverage this very limited execution and turn it into running bigger piece of code. Many mitigation techniques focus on making this part as difficult as possible. If locating or parsing information to disable security features requires running few hundred lines of code but vulnerability allows you to directly execute only 10 instructions which can't even be freely chosen, attack might be prevented.
- saagarjha 6y agoRandomizing system calls is probably not going to be all that helpful, as your exploit code will just read the table where you keep this information. Some OSes (OpenBSD) will do randomization based on object files and sections, which you can do things with like place each function in a different section and relink. This would require an exploit to leak addresses, which is a better situation I guess; the downside is that this adds complexity and is fundamentally incompatible with certain operating systems (Darwin). As for JITs…I mean, they kind of need to exist, but I am unsure what to do there other than "your app doesn't seem like it needs a JIT" will mean, especially as more programs ship with language runtimes and browser engines.
- johncolanduoni 6y ago> Some OSes (OpenBSD) will do randomization based on object files and sections, which you can do things with like place each function in a different section and relink. This would require an exploit to leak addresses, which is a better situation I guess; the downside is that this adds complexity and is fundamentally incompatible with certain operating systems (Darwin). Doesn’t Darwin’s linking model effectively do this separation (though I don’t know about randomization) unconditionally, with the whole “atom” thing? IME the linker does tree shaking effectively without per-function sections, and I have trouble seeing how that would work otherwise.
- pjc50 6y ago> JIT is normalizing creating new executable code at run-time shrug the alternative is to be iOS, where programming languages are banned. The simple and now widespread mitigation is "W xor X": enforcing that pages must either be data or code, but if something is supposed to be executable it must not be writable. So JITs must build their code and then call the operating system to change the page into executable. But even this isn't entirely sufficient in situations where "ROP" (return oriented programming) is possible; this relies on finding "gadgets" at the tail end of functions that contain a wide enough selection of instructions to do what you want, and then overwriting the stack with a big pile of return addresses. The chain of returns then executes each gadget in sequence. An advanced mitigation against that is stack "canaries", such as https://arxiv.org/pdf/1909.05747.pdf https://arxiv.org/pdf/1909.05747.pdf
- fwsgonzo 6y agoThanks, I was aware of canaries actually, and I've been using XO (execute-only) which makes code unreadable, which is a better version of what you call W xor X in my own emulation. Code should simply not be readable, as it never contains any data that I have seen. It might have been the case in the hand-written assembly days when people put data and constants close to the code, but I recently read that GCC considers it a bug if there is data in the code segment. I realize I am taking some minor flack for my questions, but that is how you get people to answer. You have to be a little bit controversial. So, As far as JITs go, I've been thinking about how Rust just took something C++ people took for granted: You can't have certain safeties because it would cost you performance, and prove it completely wrong. But it required moving completely away from C and rethinking things from the ground up. So, who is to say you can't do the same with dynamic languages? I mean, why do we even have JIT to begin with? It's largely for these highly dynamic JS, Python-like languages, which are extremely popular because of it. Is there really a good reason to believe that these types of languages will never be compiled to native code? Even if only at near-native speeds?
- johncolanduoni 6y agoIn principle there’s no reason why you can’t profile the code ahead of time with a representative benchmark of the application and perform most of the important optimizations that a JIT performs, and then save that generated code. In fact V8’s snapshot mechanism effectively does that with the Chromium APIs. However you’re losing one of the significant benefits of these languages (that is pretty much crucial in the JS case): the ability for them to be written independently of the particular runtime and architecture of the execution environment. Code generated for JS (just as an example) has to be intimately aware of the runtime’s native bindings and GC implementation to be as efficient as it is in modern browser engines. You would also lose the ability to software sandbox untrusted code. Basically, the JIT platform for which exploits are the most valuable by a large margin wouldn’t be able to use this solution, and the other JIT platforms aren’t often used in ways that make this kind of issue relevant in practice.
- Karliss 6y agoFrom what I understand BSD has a feature which verifies calling address of syscall and blocks if it doesn't comes from libc thus achieving similar result to your suggestion with randomization.
- fuzzybear3965 6y ago> just by making the executable code execute-only? What else would I do with executable code? Also, what does it mean to make an exploit "hard"? Hard to write an exploit (offensive) or hard to mitigate (defensive)?
- fwsgonzo 6y agohttps://linuxplumbersconf.org/event/4/contributions/283/attachments/357/588/Touch_but_dont_look__Running_the_kernel_in_execute_only_memory-presented.pdf https://linuxplumbersconf.org/event/4/contributions/283/atta... That presentation has some info on XO