C: buffer overflow, changing passed variables - c

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.

Related

simple read operating not reading any thing from char device linux

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).

Buffer Overflow to Modify the Return Address

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;
}

Why is this Overflow Occuring?

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.

Saving an integer into an array of strings using sprintf()

I'm having trouble storing an integer into an array of strings using sprintf(). I am trying to create a new argv list to pass into my child process. I have 'curr' storing the correct value since I've tested in in GDB. My code is as follows:
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h> /* for pid_t */
int main(int argc, char *argv[]){
static char *argv2[] = {"./datagen", "10", "outputfile", "SIGUSR1"};
pid_t pid = fork();
int curr = getpid();
sprintf(argv2[4], "%s", curr);
if(pid == 0)
{
printf("You are in the child process.\n");
}
else{
printf("You are in the parent process. Process ID is %d\n", getpid());
}
return;
}
After exhaustively searching around for a clear answer, I have yet to find anything. Ideally, the 4th slot of argv2 will store the process id as a string. However, I am getting a segmentation fault 11. If anyone could shed some light on this issue I would be eternally grateful.
Thank you!!
You can't do that
static char *argv2[] = {"./datagen", "10", "outputfile", "SIGUSR1"};
is a declaration of an array of char pointers, which are pointing to string literals, and further more to four string literals only, you can't extend it nor modifiy the strings.
What you need is
char argv2[10][100] = {"./datagen", "10", "outputfile", "SIGUSR1"};
assuming that you want 10 strings of maximum length 100, which you can obviously change.
Also, the format specifier for integers is "%d" so you have another mistake, having said all that you can now
sprintf(argv2[4], "%d", curr);
and I would suggest the snprintf() function, since it will avoid buffer overflow problems,
snprintf(argv2[4], sizeof(argv[4]), "%d", curr);
chux comment is correct if you want to have control on whether the specified length of the string was enough, you should check the return value of snprintf(), in case there wasn't sufficient space to write all the source string into the destination it will be truncated, if snprintf() returns a value larger or equal to the requested maximum, it means that the string was truncated, so a simple check like
if (snprintf(argv2[4], sizeof(argv[4]), "%d", curr) >= sizeof(argv[4]))
doSomething_TheString_Was_Truncated();
although, for 100 characters and the "%d" that will not happen, but I firmly believe that people must write safe code as a habit, rather than only checking possible problems, check for every thing that can conceptually go wrong, no matter how unlikely. Because sometimes there will be situations where an unexpected thing will happen.
Note: as chux pointed out again, snprintf() will return a negative in case of an error, you can check for that separately, to check if there was an error.
Simple steps:
1) Determine array size information from existing argv, argc arguments.
int i, len=0, lenKeep=21;//initialize large enough to contain pid integer
for(i=0;i<argc;i++)
{
len = strlen(argv[i])
if(lenKeep<len)lenKeep = len;
}
2) use that size information to create a new string array, argv2, with additional elements if necessary. (argv2 will be an array of strings, create sufficient space.)
char argv2[argc+1][lenKeep+1];
//argc+1 allows for additional array element
//lenKeep+1 provides space for all existing content
3) add new information to the string array in the normal way.
sprintf(argv2[argc], "%d", curr); //argv2 array contains argc + 1 elements
//so now argc is a valid index value
sprintf(argv2[4], "%s", curr); breaks the array, which has only 4 elements.
Even if it has more elements, you are writing to a string literal which is Undefined Behaviour.
Try using %d instead of %s, it should anyways be saved as a C string.

strange read buffer

here i meet a strange problem about c read function in linux.
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
int main(int argc, char** argv){
int fd=open("a.c",O_RDONLY);
if(fd==-1){
fprintf(stderr,"%s\n",strerror(errno));
}
char buf[10];
if(read(fd,buf,9)==-1){
fprintf(stderr,"%s\n",strerror(errno));
}else{
printf("%s\n",buf);
}
}
i think the buf should be initialize to zero, so the first 9 char read to buffer and the last one is '\0' and it like a string. but the resule is odd, below is a.c file and the result of this program,
a.c
1234567890abcd
result
1234567893øþzôo`
seems this string is out of buffer, I can't figure out what happened, can anyone help me?
thanks.
When you print a character array without ending '\0', printf will print all characters till it finds '\0' in the memory. In this case, looks like '1234567893øþzôo` is followed by '\0'. Note that printf does not know the size of 'buf' array, hence it will print even those characters present after the end of buf array.
As you have suggested it is better to either set entire buffer to 0 or add '\0' explicitly at the end (as shown in below code).
buf[9] = '\0';
You said "i think the buf should be initialize to zero". The compiler does not do this automatically for you, so you will need to do it yourself if that is what you want:
char buf[10];
memset(buf, 0, sizeof(buf));
Before the buffer is initialized, you have no guarantees on what its contents will be.
ISTM your buffer is not zero-terminated, since you only read 9 characters. Change the last part of your code:
if(read(fd,buf,9)==-1){
fprintf(stderr,"%s\n",strerror(errno));
}else{
/* add this */
buf[9] = '\0';
printf("%s\n",buf);
}
}
What happens if you add that?
You should initialize buf to all 0.

Resources