2

I'm currently practicing buffer overflows, but there is one thing I don't understand yet. I have read this similar question. I understand that if you don't know the address of ESP you must look for an address that executes JMP ESP to jump to the injected shellcode.

But do you really don't know the address of ESP? You do know the address of EIP, because you detected the point where EIP is overwritten or do you only know the offset of the EIP from your injected buffer? If so, then even no address is known to an attacker (ESP included ofcourse) and the attacker has to work with offsets only?

The picture below I've made shows the position right when the program has popped the return address.

Stack

Can't you just do any of these actions:

  • Inject JMP ESP directly into the address of EIP, why do you need to find an address that performs this call?
  • Add +4 to the return address as the shellcode comes right after the return address

I've read that the return address must point to another address that does JMP ESP is necessary, because of ASLR and a possible different depth of the call stack. I don't understand what is meant with the latter, does somebody have an example? Isn't the shellcode always right after the overwritten EIP?

And if ASLR is not enabled, do you then still have to find an address with JMP ESP?

EDIT:

My main question is: Why does the state of the stack at a moment in time influence the stack pointer? I've read that this makes it hard for the attacker to predict the stack addresses. But if you have a program that you start up again and again, then the same amount of variables and procedures are executed, so the stack size always will be the same.

After some talking with a colleague, he thinks they mean with that the situation of a Apache Webserver that handles requests and responses and performs a lot of actions. When you fire your buffer overflow exploit you don't know where in the execution the stack is. This opposed to having a program on your own pc that you can start over in the same manner again and again. Is this assumption correctly?

With all protections disabled; can you know the location of the stack in memory? Is the bottom of the stack a fixed address?

All the examples of exercises made me confusing, because that is not the real-world scenario. In the debugger you CAN see the stack addresses being modified, but you can't when remotely exploiting a buffer overflow. That's the reason why I thought you know the EIP and ESP address while exploiting, but that is only when you are using a debugger.

4
  • The stack pointer can vary at a fixed point in execution because some functions in the call chain might use varying amounts of stack (alloca in C, for example). There can also be different code paths that execute the vulnerable code, which can result in very different environments (including wildly different stack pointer values). – Extrarius Jul 26 '20 at 15:47
  • @Extrarius How does the use of alloca interrupt the overall stack layout? If a program uses two functions that call alloca with a specified size, then that will be the same upon every execution, right? Or do you mean that the code could use alloca with a variable size depending on some external parameters that could be different upon each execution? For the latter: let's say you perform a BOF in a query param of a webserver, then there is (probably) one handler/code path that handles it. Do you have examples of different code paths running the vulnerable code, as I can't think of any now :). – Maikkeyy Jul 26 '20 at 16:25
  • Yes, I meant calling alloca with a variable (whether it depends on user input, program state, or something else). As for multiple code paths, an http server might have different paths to the query parser depending on the type of request (get, post, head, connect, etc), for example. If only a "GET handler" calls the query parser, you have one path, but if there is a separate "POST handler" that calls it also, you now have (at least) two possible stack setups inside the query parser. In more complicated scenarios, the path taken might not be directly controlled by the input. – Extrarius Jul 26 '20 at 16:48
  • @Extrarius that made it a lot clearer with the examples, thanks! I've added them to the list of things that can affect the stack layout in my own answer to this question. If somebody knows another situation that lacks in the list, let me know :) – Maikkeyy Jul 26 '20 at 16:59
3

[why can't you] inject JMP ESP directly into the address of EIP, why do you need to find an address that performs this call?

First of all, EIP is a register in x86. It itself does not have an address pointing to it. What you mean to say is more along the lines of "the value on the stack that will be popped into EIP at time of return". This is important, because you can't just "inject" or put instructions into EIP; it only holds a 4-byte address pointing to the memory address of the instruction to be executed. If you tried to put the opcodes directly in EIP, you'd get a segfault since invalid memory would be accessed.

You might need the jmp ESP or a similar gadget in this case in order to redirect execution to an address you cannot (easily) guess, assuming ASLR is enabled.

[why can't you] add +4 to the return address as the shellcode comes right after the return address

If PIE is disabled (which is a fair assumption for an old fashioned exploit like this), then you are able to know the addresses of all the program's instructions. So you know where the program normally returns, but you don't know the stack address where that value is stored. So no, you can't add 4 to an address you don't know unless you are able to leak it somehow.

Isn't the shellcode always right after the overwritten EIP?

Not necessarily, it depends what is on the stack, or the shellcode could end up in the heap depending how the program is written. Technically, it would be before since it would be lower addresses.

And if ASLR is not enabled, do you then still have to find an address with JMP ESP?

No, if there is no ASLR or you are able to leak/guess/bruteforce where the stack is, you can use that address directly such that it ends up in EIP.

As far as call stack depth goes, maybe I'm not understanding the question, but I can't think of any impacts from that.

4
  • Thanks, your answer makes it a lot more clear. So the summary of the story is you can see the addresses stored IN the stack, but not the stack adresses that CONTAIN these addresses? Does the same apply when loading the executable in Immunity Debugger? You can see the stack adresses there too, right? I think they meant with call stack depth the amount of items on the stack? Like what you said that it depends what is on the stack. But apart from the fact if it is smart to place it right before the EIP you always can, right (if no heap is used)? – Maikkeyy Jul 16 '20 at 22:16
  • Well, with a debugger, you can see everything in the process's memory, and naturally all addresses. The stack layout is going to mostly depend on how the program was compiled, but in a simple function, that is a fairly reasonable assumption yes. – multithr3at3d Jul 16 '20 at 23:35
  • Everywhere I read that the amount of data loaded in the stack changes upon every execution or that memory may move around a bit during execution, but nobody tells WHY :). The amount of data on the stack is written in code, so why would that change upon execution (assuming no stack protection features are enabled)? You can open up a debugger and read the address ESP points too, but according to the other posts that address can change every execution? I will accept your answer after this, thank you! I'm clearly missing something :) – Maikkeyy Jul 17 '20 at 6:06
  • If you run a program the same way with the debugger attached 1000 times, you should see the same stack layout every time (unless something really weird is being done...). Naturally, the address of the stack and addresses on it will differ, but things will be at the same relative offset. – multithr3at3d Jul 17 '20 at 19:51
0

For myself and other people who want a brief overview of the things discussed I will answer my own question for the sake of clarity.

The confusion arose when I read that the address ESP points to (after overflowing EIP) can’t be used to effectively jump to the shellcode as the address changes dynamically. At the same time, I was working with the debugger to exploit a buffer overflow with all protections disabled, just seeing all the addresses and them not changing. So why is the ESP then a dynamic value?

Well, there are actually two (exploitable) situations:

  • Locally, where you always run and restart an application in the same way
  • Remotely, where the application is already running and you not knowing in what state it is

Think of a program that reads configuration files out of a directory and if you have that program locally you KNOW how much config files are stored in the local directory. But you don’t know how much config files are stored on the remote machine, thus putting the stack in a different state or call stack depth. You don’t know how far the application is in reading the files, so the top of the stack (ESP) could be different than what it is on your local computer. I think another example is an Apache webserver of which you don’t how far it is with handling requests.

So my question was: “But do you really don't know the address of ESP?” and yes you do if you test it locally, but it is not reliable remotely. All this goes for the situation where no protection is enabled. This brings me to another point, namely the situation where protections have been put in place.

ASLR

This protection also ensures that you cannot rely on the address you see in the debugger to return to after overflowing, because the addresses are frequently randomized. Gdb disables ASLR by default, so you could make your exploit work there, but it is not representative for real-world scenarios. Even running the vulnerable program out of the debugger results in your exploit not working, because the hardcoded address where the program must return to doesn't exist anymore as all the addresses are randomized upon each execution.

So this is even a problem locally and of course remotely.

So, to summarize: the following situations affect the stack layout in a target application:

  • State/call stack depth

  • Variable memory allocations within functions based on external parameters

  • Different code paths that make use of the vulnerable code

  • Protections based on memory-randomization

Conclusion

This all boils down to the reliability of your crafted exploit AFTER debugging. So utilizing a gadget like “jmp esp” is necessary, because you don’t have to jump to an absolute address which you can’t predict easily (call stack depth/state, aslr), but instead it jumps to the correct address on behalf of you.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.