It is often shown that non-executable data segemnts are possible to bypass through return-to-libc attacks. It's evident on /bin/sh
but is it also possible to invoke a remote shell?
-
I believe the general term for this kind of attack is Return Oriented Programming.– this.joshAug 2, 2011 at 22:51
3 Answers
The universal parts of an exploit are often called "shell code" because they usually do exactly that: Start a shell process and connect stdin/stdout to a network socket.
This paper may be interesting:
To demonstrate, we perform an over the network brute force return-to-libc attack against a pre-forking concurrent server to gain remote access to a shell.
-
+1, that paper is a really good, detailed explanation, I like it.– user2213Aug 17, 2011 at 16:14
-
2
Sure it is possible! What about a call to system()
? The stack itself may not be executable but it still contains the arguments and thus anything can be done as long as the exploited process runs with the necessary privileges.
I think, if I've got this right, you're asking:
"What is the difference between a return to libc attack vs a standard buffer overflow and is an exploit involving invoking a remote shell still possible?"
The difference is subtle. First let's take a look at the stack. Avid's going to just love this piece of code:
int insecure_function(char* source)
{
char buffer[200];
memcpy(buffer, source, strlen(source));
Which is clearly vulnerable to a buffer overflow. The stack for this function should look like this:
Low Address High Address
char buffer --------------------------------- | ret address | char* source
some data 200 sizeof(char)'s long | 0x134543344 | 0x4374234294
Now, a traditional buffer overflow allows you to do this:
Low Address High Address
char buffer --------------------------------- | ret address | char* source
Shell code here ---------- then some padding --- 0x999999999 | 0x4374234294
/\ |
|| |
--------- < the address points to here < -------------
That's a really simple example - you overwrite the return address to point at your stack. In practice that's quite hard, so you end up using a NOP Sled which is equivalent to "aim in the right place".
Of course, all of this is defeated if that stack area is non-executable because the processor won't execute those instructions. Excellent!
Well not quite. We can't get shellcode of our own into memory to execute, but luckily there are lots of libraries in code that will do pretty much any system call. Technically, we could pick any function we liked, completely, but the most obvious choice is libc
since it provides all sorts of functions. Like opening shells. So in this case, what we do is provide our own stack frame and return address to that function:
Low Address High Address
char buffer --------------------------------- | ret address | char* source
Arguments - libc uses these! | 0x011010101 | 0x4374234294
|
< off to libc we go ----------------------
When we get to libc, the stack frame is set up with the arguments said function needs, so we can do anything the function we've chosen can do, e.g. launch an instance of /bin/sh
with it's IO redirected wherever we like.
This is clearly quite tough and depends on lots of things. How big is the buffer? Does it get manipulated post buffer overflow (overwriting your arguments)? Do you care about the target process (the one whose stack you just clobbered) staying up, or not? Lot's of issues practically, but theoretically it can be done.