In a ret2libc attack, I understand that the return address can be overwritten with the address of the system command, which takes a command string as an argument. In this case, shouldn't the address of the command string come directly after the address of system()? However,most tutorials I have seen follow the format : address of system().address of exit().address of command string.

Why is it mandatory to include the address of exit()? Will the attack still work if exit() is excluded?

  • You can always try excluding the exit() statement and try out? ;)
    – Limit
    Commented Sep 12, 2016 at 21:46
  • I tried excluding the address of exit() and placed the address of the command string directly after the address of system() but it doesn't work anymore... Commented Sep 12, 2016 at 21:50

2 Answers 2


The ret2libc (and return oriented programming (ROP)) technique relies on overwriting the stack to create a new stack frame that calls the system function. This wikipedia article explains stack frames in great detail: https://en.wikipedia.org/wiki/Call_stack#Stack_and_frame_pointers

The stack frame dictates the order the function call and parameters are written:

function address return address parameters

So in your example you want to call system() with cmdstring and return to the exit() function when the system() call returns. So you write the following stack frame:

system_addr exit_addr cmdstring_addr

If you remove the exit address you change the stack frame to the following:

system_addr cmdstring_addr existingdataonstack_aka_junk

So now you're calling system() with a junk address argument and trying to return into your command string once the function completes. Which is why it fails. You can replace the exit() address with other data such as 0x41414141 which will cause a segfault once the system() call completes. Or you could replace it with the address of a pop pop ret location and then write more stack frames underneath. This is now a ROP payload. Here is a basic example of how that would look on the stack:

system_addr poppopret_addr cmdstring_addr printf_addr exit_addr addr_of_string_to_printf

This would allow you to print something like You got hacked before exiting.

  • In the wikipedia article the order of operations seems to be: arguments, return address, local variables. For return-to-libc, why is the order not then system_addr, cmdstring_addr, exit_addr?
    – rookie
    Commented Mar 9, 2021 at 14:40
  • @rookie When returning from the vulnerable function, system_addr' looks like a return address, and the two others are arguments. Control is transferred to system()` by popping the stack, and now exit_addr looks like the return address and cmdstring_addr looks like an argument.
    – b0fh
    Commented Dec 6, 2023 at 10:25

The reason exit() is included is to terminate the program gracefully. If you don't have exit() at the end of your chain the program will either continue to run weirdly or most likely terminate with a segmentation fault. It's not mandatory to include a call to exit(). Ideally you will construct your exploit in such a way that afterwards the program continues to run as usual so nobody gets suspicious because their service isn't running anymore.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .