Linking and security

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Gigasoft
Member
Member
Posts: 856
Joined: Sat Nov 21, 2009 5:11 pm

Re: Linking and security

Post by Gigasoft »

Just to make it extremely simple and crystal clear: PoC code injection with WNX, that's the mission.
Simply return into memcpy to copy the code somewhere convenient, then to mprotect/VirtualProtect/etc., then to your code. Doesn't mean that DEP is useless, as it does complicate the job of an attacker, especially when combined with ASLR. Or, in a world where page attributes can't be changed (which is not the case with any mainstream OS), create an executable file with the desired content and run it.
User avatar
bzt
Member
Member
Posts: 1584
Joined: Thu Oct 13, 2016 4:55 pm
Contact:

Re: Linking and security

Post by bzt »

nullplan wrote:Then he insults me, then he blames it on my age, and then he says it's not personal.
Ehem, don't even try, that train has gone. If someone else would have said that "security is not measurable", I would had the same reaction. I've said this so many times on this forum, but here it goes again: it doesn't matter to me who says it, what's said matters. If it can be proven then it's correct, if it can't be or if there's at least one example to prove it wrong, then it isn't. That's called the scientific method, and there's absolutely nothing personal about it.

Don't see somebody telling you "you were wrong" as an attack. Specially if there's proof. Think of it as an opportunity to improve and make yourself better!
nullplan wrote:You know damn well what you ask is impossible, bzt.
Of course I know, that's why I'm absolutely certain what I'm saying is true. Glad to see you admitted.
Gigasoft wrote:Simply return into memcpy to copy the code somewhere convenient, then to mprotect/VirtualProtect/etc., then to your code.
Thank you, you've just proven me right. Using mman (your solution to the WNX problem) definitely does not qualify as "denying userspace code to map executable pages altogether" which was the starting point (see here).
Gigasoft wrote:Or, in a world where page attributes can't be changed (which is not the case with any mainstream OS)
Yes, and all mainstream OSes are littered with trojans and viruses. Good point. And we come back to my original lemma: denying userspace to change page attribute means "you can eliminate a big deal of attack vectors at once."
Gigasoft wrote:create an executable file with the desired content and run it.
I don't think that'd qualify as a "code injection" at all.

I consider this conversation over. I don't think there can be anything meaningful added to what's already said. Moving the dynlinker into kernel and denying executable mappings from userspace is indeed a more secure approach.

Cheers,
bzt
kzinti
Member
Member
Posts: 898
Joined: Mon Feb 02, 2015 7:11 pm

Re: Linking and security

Post by kzinti »

bzt wrote:Moving the dynlinker into kernel and denying executable mappings from userspace is indeed a more secure approach.
I came to the opposite conclusion. Adding the dynlinker to the kernel is just growing the attack surface and making your system less secure, not more.
linguofreak
Member
Member
Posts: 510
Joined: Wed Mar 09, 2011 3:55 am

Re: Linking and security

Post by linguofreak »

bzt wrote:
Octocontrabass wrote:
bzt wrote:Then show us a PoC where you inject an arbitrary code and you execute it! Just one little example!
There are some examples here, and links where you can find others.
I know ROP, and that link does not demonstrate "injecting an arbitrary code and then executing it". Just to make it extremely simple and crystal clear: PoC code injection with WNX, that's the mission.
The Wikipedia article on ROP claims that the technique is Turing complete. I think this may be a bit of a strong claim, more accurate is probably that it is *likely* to be Turing complete for a given codebase that is being attacked. Furthermore, as set forth in the next paragraph, I think there's a good argument for viewing ROP as code-injection:

As I see it, any codebase K is has a statistical likelyhood L, when compiled for a given host ISAH, to have a subset that accidentally implements some ad-hoc bytecode interpreter I. L is going to be some increasing function of size (the larger K is, the more likely you can patch together some I), and there is going to be some further likelyhood L_t that I is Turing-complete. L_t will likewise be an increasing function of size. Individual instructions in H machine code form the "microcode" for I, instruction sequences in K implement the instructions for I, and the stack pointer for H is the instruction pointer for I. The ret opcode for H is the fetch microop for I, H instructions that add to or subtract from the stack pointer are I microops used to implement jmp instructions in I, etc. If H enforces W^X, then buffer overflows cannot execute arbitrary H code, but they still can execute arbitrary I code. If I is Turing complete (which is probably the case if K contains any sequence of bytes that constitute a conditional instruction in H followed by a pop, add sp, or sub sp, which would constitute the microcode for a conditional branch in I), then it doesn't really matter if we can't inject H instructions, there will be some sequence of I instructions that will achieve the same result as any given sequence of H instructions. It would probably be overkill, but we could even have the exploit code we want to run already written in C, and then, once we find a suitable I within K, write a C compiler for I (or for real overkill, target GCC to I), and compile our code to I bytecode. It is my view that the fact that ROP has the success it does demonstrates that L_t becomes significant for decently small codebases. This also demonstrates why putting the dynamic linker in the kernel is questionable: if the kernel is vulnerable to buffer overflows, it increases the size, and thus the L_t, for the kernel. There's certainly an argument for putting certain *parts* of the functionality of the dynamic linker in the kernel, and having some way of making sure that the only userspace component that can make syscalls to the relevant kernel components is the dynamic linker, but stuffing the whole linker into the kernel strikes me as likely to do more harm than good.

The sources that Wikipedia cites for the claim that ROP is Turing complete are:

[*] Abadi, M. N.; Budiu, M.; Erlingsson, Ú.; Ligatti, J. (November 2005). "Control-Flow Integrity: Principles, Implementations, and Applications". Proceedings of the 12th ACM conference on Computer and communications security - CCS '05. pp. 340–353. doi:10.1145/1102120.1102165. ISBN 1-59593-226-7.

and

[*] Abadi, M. N.; Budiu, M.; Erlingsson, Ú.; Ligatti, J. (October 2009). "Control-flow integrity principles, implementations, and applications". ACM Transactions on Information and System Security. 13: 1–40. doi:10.1145/1609956.1609960.

The following is cited in the same paragraph and seems to be relevant:

[*] Shacham, H. (October 2007). "The geometry of innocent flesh on the bone: return-into-libc without function calls (on the x86)". Proceedings of the 14th ACM conference on Computer and communications security - CCS '07. pp. 552–561.

Unfortunately, none of them are available online, but I expect that they contain proof-of-concept, Turing-complete exploits, and either an analysis of the statistical likelyhood of finding a Turing-complete set of code fragments, or findings on the results of looking for such fragments in existing code. Combined with my analysis above as to the rationale for viewing ROP as code injection, we then have code injection even if the hardware and OS enforce W^X, because code for I is data for H, so R permission for H is X permission for I, and denying a program either read or write access to its stack won't work well.
User avatar
bzt
Member
Member
Posts: 1584
Joined: Thu Oct 13, 2016 4:55 pm
Contact:

Re: Linking and security

Post by bzt »

kzinti wrote:
bzt wrote:Moving the dynlinker into kernel and denying executable mappings from userspace is indeed a more secure approach.
I came to the opposite conclusion.
You haven't backed up your claim which suggests you are just trolling. But I'm going to answer for the benefit of later post readers.
kzinti wrote:Adding the dynlinker to the kernel is just growing the attack surface and making your system less secure, not more.
Now I'm sure you didn't understand the concept. Denying executable mapping is what makes it more secure. Moving the dynlinker into kernel is just a logical thing to do when you can't change the mappings from userspace. And it is not problematic, because
1) you already must have an ELF (PE) parser in your kernel, which must parse and read the interpreter, so without bound checks there's already an attack surface there
2) adding bound checks is easy, and you should have those with a userspace dynlinker anyway!
3) the only possible attack vector (accidentally modifying kernel space) is extremely easy to avoid with bound checks.

Therefore moving a dynlinker into kernel has no *additional* attack surface and is not less secure, quite the contrary.

Implementing a modular kernel (also needs a dynlinker in the kernel!) where user provided files can be loaded into ring 0 is much more vulnerable, yet I hear nobody complaining about that.

Note: the dynlinker just parses the format and loads parts of the file into *user* address space (not into kernel space). Unlike kernel modules, it does not execute the loaded code in ring 0 at all! For a dynlinker the executable files are just pure data, never interpreted, eval'd nor executed. It's the scheduler that passes control to execute the linked code (strictly in ring 3) no matter where the dynlinker is located.
linguofreak wrote:I think this may be a bit of a strong claim, more accurate is probably that it is *likely* to be Turing complete for a given codebase that is being attacked. Furthermore, as set forth in the next paragraph, I think there's a good argument for viewing ROP as code-injection
Yes, I don't consider ROP as a code-injection, and being Turing-complete is highly questionable too. But just for the sake of the argument let's say it's a kind of a code-injection.

Even so there's absolutely no difference where the dynlinker is located (kernel or userspace), ROP is completely independent of that. Using a kernel dynlinker does not give more options to ROP (the loaded code will run in userspace, no matter where the dynlinker is). Furthermore, implementing ASLR would prevent ROP equally regardless your dynlinker is in the kernel or in userspace.

Cheers,
bzt
kzinti
Member
Member
Posts: 898
Joined: Mon Feb 02, 2015 7:11 pm

Re: Linking and security

Post by kzinti »

I think your arguments are good and your conclusion logical. But there is no need to be arrogant, insulting and putting me down because I came to a different conclusion than you.

There is also no need to endlessly repost the same arguments over and over. I've read them. I have also read other people's arguments. I also have extensive experience as a programmer and for me, having the dynamic linker outside the kernel makes more sense.

I'm sure you feel the need to have the last word by saying that you didn't insult me (and insult me by doing so, since you clearly insulted me just above). Go ahead. I'll let you have the last word.
linguofreak
Member
Member
Posts: 510
Joined: Wed Mar 09, 2011 3:55 am

Re: Linking and security

Post by linguofreak »

bzt wrote:
Even so there's absolutely no difference where the dynlinker is located (kernel or userspace), ROP is completely independent of that. Using a kernel dynlinker does not give more options to ROP (the loaded code will run in userspace, no matter where the dynlinker is).
That's all true, *unless there is a buffer overflow vulnerability in the kernel itself*. If there's a buffer overflow vulnerability in the kernel, then any chunk of kernel code might be used to provide code fragments for an ROP attack, and having your dynlinker in the kernel makes it all the more likely that the attacker will be able to find the code fragments he needs.
Furthermore, implementing ASLR would prevent ROP equally regardless your dynlinker is in the kernel or in userspace.
True enough, as long as no other vulnerability leaks addresses.
User avatar
bzt
Member
Member
Posts: 1584
Joined: Thu Oct 13, 2016 4:55 pm
Contact:

Re: Linking and security

Post by bzt »

kzinti wrote:But there is no need to be arrogant, insulting and putting me down because I came to a different conclusion than you.
I wasn't arrogant, I did not insult you, and nobody put you down because you came to a different conclusion.

What I wrote was, that you haven't backed up your conclusion. In a civilized discussion it is not enough to state something, you should also say why you think that, and how you came to that conclusion.
linguofreak wrote:That's all true, *unless there is a buffer overflow vulnerability in the kernel itself*.
Having a BO in the kernel is bad, dynlinker in kernel or not, probably there are other syscalls that could be exploited to use that BO. A proper solution is to fix the BO in kernel, and not to find some workaround for the dynlinker.
If you meant the BO could be in the dynlinker, then that means you simply failed to do proper bound checks, and again, the solution is to fix the BO.
linguofreak wrote:If there's a buffer overflow vulnerability in the kernel, then any chunk of kernel code might be used to provide code fragments for an ROP attack, and having your dynlinker in the kernel makes it all the more likely that the attacker will be able to find the code fragments he needs.
I don't see this either. First, a kernel BO would allow to run any arbitrary code, there's no need for ROP (only to exploit the BO maybe). Second, the module loader already has exactly those code fragments as the dynlinker, because it is also a dynlinker in kernel (must have a look up symbol function, a relocation function etc.).

Cheers,
bzt
Post Reply