In the following 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, "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("Access Denied\n");
}
}
if I run something such as "AAAAAAAAAAAAAAAAAAAA", somehow something is overflowed and it causes the program to run as access granted. I'm confused because when I ran the gdb debugger, auth_flag was before password_buffer in the memory and it was never overflowed.
EDIT: I understand that the text doesn't fit into the buffer but I am experimenting with buffer overflows and how to exploit them in a controlled manner. Yes, we could make the array bigger but that's not the point of this
I was wondering if someone would be able to tell me why this is happening/what is being overflowed to cause this.
AAAAAAAAAAAAAAAAAAAA has size 20. You are then using strcpy to copy it to an char array of size 16. Try increasing your password_buffer size.
To avoid overflows, the size of the array pointed by destination
shall be long enough to contain the same C string as source (including
the terminating null character), and should not overlap in memory with
source.
http://www.cplusplus.com/reference/cstring/strcpy/
Because you are copying the input into a buffer that is too small (and for no reason). Your method could be implemented (without overflow) like
int check_authentication(const char *password) {
size_t len = strlen(password);
return strncmp(password, "brillig", len) == 0 ||
strncmp(password, "outgrabe", len) == 0;
}
I understand it overflows the buffet, but why would it cause access granted? (auth_flag >0)
Because auth_flag is the next int in memory after char password_buffer[16];. With some compilers (and operating systems), if you overflow password_buffer, there is a high probability that you modify auth_flag.
The memory is stored in the stack in a consecutive manner,
so there's a char[16] and an int that indicates whether the authentication passed successfully.
When you pass in a pointer to a buffer and copies it without boundary checking the local buffer, you risking overflowing your buffer and rewriting your stack variables.
When you input 'A' * 20, the first 16 'A' went into the buffer, and the remaining 4 'A's went into the int (usually sizeof(int) is 4 bytes).
So now, your int is not 0, it's:
auth = 0x41414141
since the ASCII code of 'A' is 0x41.
There's a really great article related to this -
http://insecure.org/stf/smashstack.html
It lays out the basics of stack overflows and gets a little more advanced later on.
Related
I have a simple code that suppose to read length sized and fetch data from character device in Linux C.
This is my code and errno is set to 9. I made sure the file exists and it does. And able to read it with cat /dev/mychardev-0 but why bad file descriptor error at time of read. I am getting this line passed int fd=open("/dev/mychardev-0",O_RDONLY); if(fd<0)
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
int main()
{
int fd=open("/dev/mychardev-0",O_RDONLY);
if(fd<0)
{
printf("fd %d\n",fd);
exit(0);
}
int length=1024000;
int x=0;
char buffer[length-1];
while(x<length)
{
int valread=read(fd,&buffer[x],length);
if(valread==0)
{
printf("zero bytes read\n");
break;
}
if(valread<0)
{
printf("read return negative %d %d\n",valread,errno);
break;
}
x=x+valread;
}
if(x>0)
{
printf("%x",buffer);
}
else
{
printf("ops no read\n");
}
return 0;
}
char buffer[length-1];
int valread=read(fd,&buffer[x],length);
That is a very bad idea. You allocate enough space for length - 1 bytes then attempt to read length bytes into that space.
If the read gets the largest possible size, you will have a buffer overflow, well into undefined behaviour territory(1).
And, on top of that, you attempt to read length bytes even though you may have already read some in earlier iterations of the loop. You should be:
Using the correct length for the buffer; and
Adjusting the length down based on what you've already read.
And, though this may not be a problem, one meg is quite a lot of data to put on the stack (which is often limited in size by default). If that is the case, you may be better of using malloc to get a dynamic buffer, such as:
char *buffer = malloc(length);
// make sure buffer != NULL, then use it.
free(buffer);
So, something like (untested but you should hopefully get the idea):
int x = 0, length = 1024000; // with malloc if needed.
char buffer[length];
while (x < length) {
int valread = read(fd, &buffer[x], length - x);
if (valread == 0) {
printf("zero bytes read\n");
break;
}
else if (valread < 0) {
printf("read return negative %d %d\n",valread,errno);
break;
}
x += valread;
}
(1) An example of what may happen is that overflowing the buffer will affect other variables on the stack in certain ways.
For example, it may corrupt x which would mean your next read could go to some arbitrary place in memory possibly corrupting other things as a result.
Or it could corrupt fd which would make the next read likely to fail with an invalid file descriptor.You can check the actual behaviour by simply printing out those two values before any use. Given the fact you mention "bad file descriptor error", that's possibly the most likely scenario here.
But, to be honest, the best solution is probably just to avoid undefined behaviour (though it may still be educational for you to find out the effects).
I'm needing to modify the return address so I can call print_good function. I need an offset value, and a hex value to store to do so. The offset that I calculated from using obj is 0xcd, which was gotten 0x40074e(the puts function which I believe prints out try again) minus 400686(The address of the print_good function that I want to call). The hex value that I believe is correct is 400686, which is the address of the print_good value. Putting the offset value I got and the hex value I got doesn't give the correct answer.
I'm pretty sure the hex value is 0x400686, but correct me if I'm wrong. The offset value I believe could be off.
I'm given the code and the executable. I need to find the password which is an offset value and a hex value. I got the addresses using objdump and looking at its code.
If you want to try it yourself to help me, here's the file to download the executable:
The executable:
https://www.mediafire.com/file/disui6pwcb55ing/Ch3_07_CanaryBypass
The c code:
https://www.mediafire.com/file/yu3j3dxg476qy8c/Ch3_07_CanaryBypass.c
The c Code written here:
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#define USERDEF 30
char msg[] =
"Stack canaries and non-executable stacks make stack exploitation difficult. Such\n"
"techniques, however, do not protect against return-oriented programming where\n"
"only the return addresses on the stack are targeted. In this level, you control\n"
"a single write into a vulnerable buffer in the function prompt_user. Overflow\n"
"the buffer to modify the return address beyond the stack canary so that it\n"
"points to a function of your choice. The level will prompt you with an offset\n"
"(in decimal) from the beginning of the buffer that will be written into followed\n"
"by a hexadecimal value that will be written there (e.g. scanf(\"%d %x\");).\n"
"The program will then write the hexadecimal value to a location computed\n"
"using the offset. To determine how close you are, examine the pointer\n"
"being used to write into the stack and how far away it is from the value\n"
"of $rsp when the retq instruction at the end of prompt_user is reached.\n\n";
void print_good() {
printf("Good Job.\n");
exit(0);
}
void print_msg() {
printf("%s",msg);
}
void prompt_user() {
char buffer[30];
int offset;
char *user_addr;
char **over_addr;
printf("Enter the password: ");
scanf("%d %lx", &offset, (unsigned long *) &user_addr);
over_addr = (char **) (buffer + offset);
*over_addr = user_addr;
}
int main(int argc, char *argv[]) {
print_msg();
prompt_user();
printf("Try again.\n");
return 0;
}
I wonder why the two values of int don't validate the if condition even if it is true. printf shows both of them are equal.
Is buffer overflow able to affect the behavior of if conditions,corrupting other code sections behavior.
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main(void) {
srand(time(NULL));
char instring[2]; // when this increases somehow I get the right behavior
int inint;
int guess;
guess = rand() % 127;
inint = ~guess;
printf("%i\n", guess); //testing with printf()
while (guess != inint) {
printf("Guess Number\r\n");
gets(instring);
inint = atoi(instring);
printf("%i\n", inint);
if (inint > guess) {
printf("%i\n", inint);
puts("too high");
} else if (guess > inint) {
puts("too low");
} else {
puts("right");
}
}
return 0;
}
The problem is indeed here.
char instring[2];
Now let's think about this line.
gets(instring);
Let's say you type 10 and hit enter. What will go into instring is three bytes.
1
0
A terminating null.
instring can only hold two bytes, but gets will shove (at least) three in anyway. That extra byte will overflow into adjacent memory corrupting some other variable's memory causing some bizarre bug.
And that's why making instring large enough to hold the result from gets fixes the program.
To avoid this when working with strings, use functions which limit themselves to the memory available. In this case fgets.
fgets(instring, sizeof(instring), stdin);
That will limit itself to only reading as much as it can fit into instring.
In general, don't get stingy with memory to read input. A common practice is to allocate one large buffer for reading input, 1024 is good, and reuse that buffer just for reading input. The data is copied out of it to more appropriately sized memory, which atoi effectively does for you.
I'm doing some exercises regarding buffer overflows and I am currently stumped as how to proceed further with one of them. This is the program code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/syscall.h>
void reverb(char *msg, unsigned int len) {
unsigned char length = (unsigned char) len;
char buffer[250] = "Printed: ";
strcat(buffer + 9, msg);
if ((length > 75) || (length < 15)) {
fprintf(stderr, "Error: invalid string length");
exit(1);
}
else {
fprintf(stdout, "%s\n", buffer);
}
}
int main(int argc, char **argv) {
//argument check
if (argc != 2) {
fprintf(stderr, "Invalid arguments!\n");
return 1;
}
reverb(argv[1], strlen(argv[1]));
return 0;
}
So basically as obvious as it is, this program should just re-print the argument you gave to it. I obviously have to exploit one of the functions used, and I suspect the main culprit here is strcat. However, I'm faced with the issue of the length variable when I want to get my stack smashing done.
To be able to cause a segfault and successfully find a point for the overflow to happen, I need to pass an argument with a length of around 255+ (not sure on the current number right now but it's somewhere around that), which is not doable with my 75 char limit. Using gdb and setting a break point right after strcat, I am able to find the buffer's location in the memory (kinda easy, just the area filled with 0x41 since I spammed it with A's). length's location was kinda trickier, but here's the issue - it's located BEFORE the buffer, meaning I couldn't even overwrite it if I wanted. But, I somehow still need to overwrite it to get into the else branch, I think. And I've been stuck at that point, not seeing a way to proceed properly.
If the string is long - say 256 chars, the Length variable will be wrong. Use the suggestions in the comments to catch it.
The crash: the string copy will then copy all 256 chars onto the end of the string in buffer, which only can hold 250 bytes.
Move the strcpy() into the else.
Additional notes:
I ran this in my debugger (VS2015)
When passing in the 256 byte string and overrunning “buffer”, the variable “length” got clobbered, and its value changes from 0 to 69 (some character in my string). This caused the 75 char limit to fail and the buffer to be printed.
What follows are abbreviated just to keep this question short (no check for null, etc.).
program1.c
main()
{
char *aString = calloc(10, sizeof(char));
printf("Enter string: ");
scanf("%s", aString);
printf("You typed in %s\n", aString);
}
program2.c
main()
{
char aString[10];
printf("Enter string: ");
scanf("%s", aString);
printf("You typed in %s\n", aString);
}
program1.c will let me enter characters seemingly forever. I've entered 2000+ characters and the program will execute without error, despite the fact that this is "undefined behavior".
program2.c will let me enter more than 10 characters, but if get close to like 30 or 40 characters, it will give me a segmentation fault.
Now my limited understanding from class and other tutorials tells me that both of these programs are doing the same thing under the hood --- setting aside a piece of memory intended to be an array of chars of length 10. But it seems that program2.c's implementation provides some degree of safety. Or is the segmentation fault error completely random when you exceed the granted memory space, and I just happen to be getting it with program2.c just because that's the mood my computer is in right now?
What is the difference between program1.c and program2.c, and which is the "safer" method of entering a string? I realize there are other methods which may be even better, but I'm curious about the comparison between just these two.
Although neither program is safe, the likely reason you are seeing the behavior is that program B allocates the array on the stack, and as soon as you get out of bounds you are overwriting other useful things like the stack frame of the scanf call.
Whereas program A allocates heap memory, and since you are not doing anything else in this toy program, the memory you are writing into is unused.
In any real program, both are equally unsafe.
Note: This out-of-bounds behavior is undefined by the C standard, and the compiler could theoretically be doing anything. But in most common real-world compilers, the above is most likely what actually happens.
The two are not the same under the hood.
program1 is calling calloc to allocate memory from the heap.
program2 has been compiled to reserve additional space on the stack when the function is called.
Both programs are exploitable because you are not checking any bounds when you call scanf(). He is free to write as many bytes as he wishes to either buffer. The solution here is scanf("%9s", aString), which tells scanf to only write up to 9+1 bytes.
What and where are the stack and heap?
Assuming a typical modern operating system, your program 1 does not crash because calloc had to request an entire page (4096 bytes of RAM, usually) from the OS to satisfy the request for 10 bytes. If you feed that program sufficiently many characters, it will crash. However, writing even one byte more than the overtly requested size (10 bytes) is forbidden, and has an excellent chance of corrupting the internal data structure used to keep track of "heap" allocations. It is probable that if you added another malloc or free call to this program, after the scanf, it would crash inside that malloc or free. By way of illustration, consider this program:
#include <stdlib.h>
#include <string.h>
int main(void)
{
char *p = malloc(23);
memcpy(p, "abcdefghijklmnopqrstuvwx", 25);
char *q = malloc(1);
return 0;
}
➽
$ MALLOC_CHECK_=1 ./a.out
*** Error in `./a.out': malloc: top chunk is corrupt: 0x0000000001bc4020 ***
(On this system, copying only 24 bytes does not crash. Do not rely on this information.)
Program 2, meanwhile, is probably crashing not because the scanf call wrote all the way to unmapped memory (which, for similar reasons, would require far more bytes of input) but because data on the stack is very densely packed and it clobbered something critical, e.g. the address to which main should return.
In a program that does anything even a little more complicated than your examples, both "techniques" are equally dangerous -- both heap and stack overflows can and have lead to catastrophic security holes.
You explicitly asked for a comparison between your two unsafe techniques, but for the benefit of future readers I am going to describe two much better techniques for reading strings from standard input. If your C library includes it, the best option is getline, which (in a simple program like this) would be used like so:
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
char *line = 0;
size_t n = 0;
ssize_t r;
fputs("Enter a string: ", stdout);
fflush(stdout);
r = getline(&line, &n, stdin);
if (r == -1) {
perror("getline");
return 1;
}
if (r > 0 && line[r-1] == '\n')
line[r-1] = '\0';
printf("You entered %s\n", line);
free(line);
return 0;
}
If you don't have getline, and you need to read an arbitrarily long string from the user, your best option is to implement getline yourself (gnulib has an implementation you can borrow, if your code can be released under the GPL). But an acceptable alternative in many cases is to place an upper limit on input length, at which point you can use fgets:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_LINE_LEN 81
int main(void)
{
char *line = malloc(MAX_LINE_LEN);
size_t n;
fputs("Enter a string: ", stdout);
fflush(stdout);
if (!fgets(line, MAX_LINE_LEN, stdin)) {
perror("fgets");
return 1;
}
n = strlen(line);
if (line[n] != '\n') {
fprintf(stderr, "string too long - %u characters max\n", MAX_LINE_LEN);
return 1;
}
line[n] = '\0';
printf("You entered %s\n", line);
free(line);
return 0;
}
Notes:
sizeof(char) == 1 by definition; therefore, sizeof(char) should never appear in well-written code. If you want to use calloc to allocate a prezeroed array of characters, write calloc(1, nchars).
Never use scanf, fscanf, or sscanf.
Do not confuse fgets with gets. fgets is safe if used correctly; it is impossible to use gets safely.