views:

194

answers:

6

I read many articles about unsafe functions like strcpy, memcpy, etc. which may lead to security problems when processing external data, like the content of a file or data coming from sockets. This may sound stupid, but I wrote a vulnerable program but I did not manage to "hack" it.

I understand the problem of buffer overflow. Take this example code:

int main() {
   char buffer[1];
   int var = 0;

   scan("%s", &buffer);
   printf("var = 0x%x\n", var);
   return 0;
}

When I execute the program and type "abcde", the program outputs 0x65646362 which is "edcb" in hexadecimal + little-endian. However I read that you could modify the eip value that was pushed on the stack in order to make the program execute some unwanted code (eg. right before a call to the system() function).

However the function's assembly starts like this:

push %ebp
mov %ebp, %esp
and $0xfffffff0, %esp
sub $0x20, %esp

Since the value of %esp is random at the start of the function and because of this "and", there seems to be no reliable way to write a precise value into the pushed eip value.

Moreover, I read that it was possible to execute the code you wrote in the buffer (here the buffer is only 1 byte long, but in reality it would be large enough to store some code) but what value would you give to eip in order to do so (considering the location of the buffer is random)?

So why are developpers so worried about security problems (except that the program could crash) ? Do you have an example of a vulnerable program and how to "hack" it to execute unwanted code? I tried this on linux, is Windows less safe?

+8  A: 

Read the excellent article by Aleph One: Smashing the Stack for Fun and Profit.

Moron
Correct me if I'm wrong but, as seminal as it was, I don't remember this article dealing with any stack protection mechanisms.
torak
@torak: No it does not. I don't believe that is OP's problem though, and the question seems more basic than dealing with OS/hardware safeguards. This article is a very good starting point.
Moron
@Moron: Well, given that the OP specifically mentioned that the `and $0xfffffff0, %esp` instruction renders the offset to EIP variable it would seem that they grasp the basic overflow mechanism.
torak
@torak: The main question seems to be the fact the esp is not known to the attacker. Doing an `and` with 0xfffffff0 does not randomize the esp or make it any harder (and if fact makes it easier because we can now align our return address within the shellcode:-)). All that `and` seems to be doing is to align the stack frame boundary. So it still seems like a basic question to me and I believe that article (especially the examples) addresses that.
Moron
I just read the article, thanks. I didn't know the jmp-call tip to get the address of the buffer, but the article is a bit obsolete when it comes to overwriting the value of EIP.
Tomaka17
@Moron: It doesn't randomise ESP, it just varies the number of bytes between the first byte of `buffer` and the address at which the return EIP is stored. However, I just realised that the only possible values for this extra offset are 0, 4, 8 or 12. These are all multiples of four bytes, the address size. So repetition of the value to write into return EIP is sufficient.
torak
The value of ESP is random at the start of the function, and the "and" opcode comes after pushing EIP so the offset between EIP and the buffer you're overflowing is random
Tomaka17
Oh, nevermind then
Tomaka17
@torak: Exactly! That is why I said it made it easier, in fact! And as you said, you might have to repeat the return address to be sure of overwriting the EIP. This is basic exploit technique :-)
Moron
For the record, that and is there for performance reasons (to keep stack frames (arguments and local variables, really) with an alignment that performs well.
ninjalj
@ninjalj: Yes. Agreed.
Moron
+4  A: 

Well for one thing, don't under estimate the hazards associated with being able to unreliably place a value inside EIP. If an exploit works one in 16 times, and the service it is attacking automatically restarts, like many web applications, then an attacker that fails when trying to get access can always try, try again.

Also in a lot of cases the value of ESP is less random than you think. For starters on a 32-bit system it is nearly always a multiple of four. That means that the extra padding offered by the and $0xfffffff0, %esp instruction will be either 0, 4, 8 or 12 bytes. That means that it is possible to just repeat the value that is to be written into the return EIP four times to cover all possible offsets to the address of return EIP.

There are actually much more aggressive stack protection / buffer overflow detection mechanisms around. However, there are ways and means around even these.

Also, for an example of where this sort of thing can be dangerous, consider if the value of var was important to you logic as in the following toy example.

int main() {
  char buffer[1];
  int var = 0;

  var = SecurityCheck();

  scan("%s", &buffer);
  if (var != 0)
    GrantAccess();
  else
    DenyAccess()
}
torak
I'd also mention nop sleds. In short, you can put a lot of NOPs before your shellcode, so guessing where is the "start" of your shellcode gets easier.
ninjalj
Closely related to this example is the technique of corrupting a structure like `struct user_account {char name[16]; int group; int permissions;};` by supplying a `name` that is too long in order to overwrite the `group` and `permissions` members. The key is being able to predict what is located in memory in the bytes following an array.
bta
+2  A: 

A classic example of an actual exploit based on buffer overruns is the Morris Worm of 1988.

David Gelhar
+1: I was thinking about this exact same example!
Moron
Actually, the Morris worm exploited several vulnerabilities, one of which was a buffer overflow in fingerd.
ninjalj
@ninjalj: Yes, that seems accurate.
Moron
+4  A: 

Further you don't have to overwrite EIP with a pointer to something in your string. For example you could overwrite it with a pointer to system() and overwrite the next word with a pointer to /bin/sh at a fixed location in the program image.

Edit: Note that system uses the PATH (actually it runs the command via a shell), so "sh" would be just as good; thus, any English word ending in "sh" at the end of a string provides the argument you need.

R..
A: 

Here's a windows version and tutorial:

http://www.codeproject.com/KB/winsdk/CodeInject.aspx

The general case I was always warned about was:

printf( string );

Because the user can provide a "%n" in there, which allows you to insert anything you want into memory. All you need to do is find the memory offset for a system call, pass a few "%n"'s and junk characters, and thus insert the memory address onto the stack where the return vector would normally be. Voila -- insert any code you like.

eruciform
That's called a format string vulnerability, not a buffer overflow :)
ninjalj
@ninjali: meh, they're kissin' cousins. this one overflows the stack buffer. :-)
eruciform
+1  A: 

As mentioned in other answers, absolute reliability is not always essential for the attack to succeed. Applications that restart automatically are an example. Locally exploitable buffer overflows on suid programs would be another. And there's the NOP sled technique to increase chances of successful exploitation, put a lot of NOPs before your shellcode so you have a much better chance to correctly guess the "start" of your shellcode.

There are many more techniques for increasing the reliability of attacks. On Windows, back in the day, many exploits overwrote the return address with the address of a "jmp %esp" located somewhere in the program (trampoline).

"Insecure programming by example" had a nice trick for Linux. Clean your environment and put your shellcode in an environment variable. Back in the day, this would lead to a predictable address near the top of the stack.

And there are also variants like return-into-libc and return-oriented programming.

There was even an article on Phrack on how to exploit 1-byte stack overflows (meaning the buffer was overrun by only one byte) (btw, 1-byte heap overflows are also exploitable in the vast majority of cases, barring protections).

To sum up, it's not that developers are paranoid, there are lots of ways to exploit even the strangest cases, and remember:

  • A program is of good quality when it does what it is supposed to do.
  • A program is secure when it does what it is supposed to do, and nothing more.
ninjalj