2

I have browsed the internet a lot and still could not understand the way it works

No success with this link either: How does a NOP sled work?

Okay, let's say we have a buffer char a[8]; in the function foo() and the stack frame for foo() looks like this (32-bit):

enter image description here

Now, what we want to do, is to overwrite 'return' value, saved on the stack when foo() was called

They say that the problem is, we cannot predict the location of saved 'return' value

But why? For example, if function foo() has the buffer defined prior to all other local variables, like in the picture above, then we always know that we need to fill 8 bytes allocated for buffer, then 4 bytes of saved EBP and then we got 'return'
Alternatively, if there are other local variables defined before the buffer, say 2 ints, then we consider 8 bytes allocated for buffer, then 8 bytes of those 2 local ints, then 4 bytes of saved EBP and then we got 'return'

We always know how far is the saved 'return' from our overflown buffer, don't we ? :/

If we don't, please explain. That's my question 1.

My question 2 is, okay, let's assume that we don't know where the value for 'return' is saved. Then if we have a large array of NOPs coming before the shellcode, we have a high chance of writing NOP (i.e. 0x90) value into location of 'return', hence overwriting the original value
Now, when the function foo() returns, the value of 0x90 is loaded into EIP (instruction pointer) and instead of resuming its normal flow, the program is going to be executed from address 0x90, isn't it?
I think I've misunderstood something here

Please, don't close my question, even though there is a similar question that I've provided the link to, at the very beginning, I guess this question will be far more comprehensive and clearer

Community
  • 1
  • 1
mangusta
  • 3,470
  • 5
  • 24
  • 47

1 Answers1

0

First we assume there is no address space randomization and the stack is executable.

We always know how far is the saved 'return' from our overflown buffer, don't we ? :/

No, we don't know. There is an unspecified number of padding bytes between the local variables and the saved EBP and the return address. This number of bytes may depend on the compiler version.

Then if we have a large array of NOPs coming before the shellcode, we have a high chance of writing NOP (i.e. 0x90) value into location of 'return', hence overwriting the original value

The idea is to have all NOP values after the return address. This way we can overwrite the return address with an "approximate" address of the shellcode in the stack. The exact shellcode address may depend on the OS version but also on program arguments and the size and number of the environment variables because they are in the stack address space.

ouah
  • 142,963
  • 15
  • 272
  • 331
  • So, what happens when 0x90 is loaded into EIP? CPU will jump to address 0x90 and look for the next instruction there, and that will cause some fault because that's not a user space. Am I right? EIP does not contain the instruction itself, it contains the address of next instruction to be executed. – mangusta Aug 04 '13 at 14:30
  • I don't understand how CPU 'slides down' skipping all NOPs until it reaches the shellcode. – mangusta Aug 04 '13 at 14:36
  • @mangusta NOP are not meant to overwrite the return address. If EIP is 0x90909090 you will get a segmentation fault. The return address has to be overwritten with the exact start address of the shellcode but this start address may change on different systems so adding NOP before the shellcode gives you a margin of error. – ouah Aug 04 '13 at 15:00
  • Oh, now I see. Then, I guess there are 3 possible cases to consider, I drew them on this pic (assume that addr_1 is our estimation of shellcode address): http://s018.radikal.ru/i522/1308/37/5c00a8356fb3.png I wonder why all security books I've seen so far, mention only the first case, and never consider the other two? – mangusta Aug 04 '13 at 15:57
  • First case is infeasible if size(NOPs + shellcode) is larger than min(addr. of saved 'return' - &buffer[0]), because either shellcode or NOPs may overwrite saved 'return' value. Second case is a reasonable choice for that kind of situation. In both cases however addr_1 should not contain null value. Third case is meaningless, I guess :) – mangusta Aug 04 '13 at 18:16