How To: Security-Oriented C Tutorial 0x0C - Buffer Overflows Exposed!

Security-Oriented C Tutorial 0x0C - Buffer Overflows Exposed!

Security-Oriented C Tutorial 0x0C - Buffer Overflows Exposed!

Welcome finally, to a tutorial on buffer overflows! At last we have reached an exciting part of this series where I will dedicate the entire article on explaining and exploiting the notorious vulnerability. Grab some popcorn, sit back and enjoy the show.

What Is a Buffer Overflow?

A buffer overflow occurs when a buffer's contents spill into the data of the memory around it and essentially overwriting the affected addresses. You can imagine yourself pouring a glass of water. When the glass cannot hold any more, it will overflow and spill everywhere onto the table and floor. From tutorial 0x08 on memory, we observed the data in the char array and the other data around it. If we allocated more items into the array, eventually, the data of the array will begin to overwrite data in front of it.

Requesting User Input

Remember the previous tutorial on user input? Let's see what would happen if we gave the first example more than just 20 characters...?

Example Code

Image via wonderhowto.com

As we can see clearly, the string array has been allocated 21 bytes of space for 20 characters and 1 extra for the null terminator. Let's bring up terminal, run the program and try to give it more data than it can hold...

Compiling and Running

What the heck happened? It gave me a segmentation fault! Not just that, it tore up my num variable too! Let's run a memory analysis for further examination.

Memory Analysis

Wow! Look at all of that damage! The hexadecimal "0x41" represents the character "A" and look at all of the data around it! It has been completely overrun and it is absolute chaos! I located our num variable and look what happened to it! Utterly destroyed... What a disaster! Yet somehow satisfying to look at... So much power... Such evil... Muahahahahaha! * cough *... Pardon me... Anyway! Let's see what happens when we continue running the program...

So there we get our seg fault because what happened was that when main tried to execute its return, it returned into an unknown memory location and said, "Where am I? There's nothing here!" The return location is actually stored on the stack but when we overflowed our buffer, it had overwritten it. A read access error occurred in the memory location 0x41414141 which caused the program to crash. I will clarify the exact details of the return in another tutorial. For now, just understand that the return failed.

Let's try it on the second example with the gets function.

Example Code

Image via wonderhowto.com

Yep! Same result, of course. I will not go into the memory analysis for this example because it is basically the same thing. If you wish to do it yourselves, please, be my guest!

Gets - Deprecated Function

Okay, I'll admit that I did do some editing to cut a part out when going over the gets function. Here it is, see it, know it, understand it and keep it engraved in your brain:

The reason I showed this function is because I wanted you to know the consequences of unsafe functions like this. Never EVER under ANY circumstance should you use the gets function to read strings. If you do, I will find you and I will hunt you down!

The solution to gets? It's a function called fgets. Whenever you are reading a string for input, you should always use fgets because one of its required parameters is that you must specify a length limit. When that limit is reached, it will ignore everything else that is trying to get in.

Demonstration of a Buffer Overflow

Alright, I'm going to write up a piece of code and I will demonstrate an example of what could happen if buffer overflows are exploited. Of course, this code won't be sophisticated - it should be simple enough for you guys to understand and I will explain the code, so don't worry!

Note: The following is not the only thing you can do with buffer overflows as you can see with a few articles which already exist that detail into other methods of exploitation. I will cover those methods in other tutorials once we have gone over the necessary content.

Example Code

What we have here is a sort of login system. There is a set password and to be able to successfully spawn the shell, the the user must type in the password and have it match. When this program is run, main calls the function login (you can see the function defined). It prompts for an input from the user with scanf. Once the user input has been placed into the userInput array, it will proceed to compare it with the password using the strcmp function. If the two match, it will set the authorized variable to 1, otherwise it will print a string to inform the user that the password failed to match. The login function then returns the value of authorized back into the main function. Main will pick up where it left off at where it called login and compare the returned value to 1. If the condition is satisfied, it will spawn a shell.

Can you figure out a way to run the shell without knowing the password?

Getting Our Hands Dirty - The Exploitation Process

The following has no tricks or sorcery to it, no "hacking tool" was used and no script kiddie was harmed in the making. This is the real deal, a hands-on and front row seat experience on basic buffer overflow exploitation.

Step 1: Debugging

We first have to know the details about the program we are running, so, we will use GNU's GDB to help us find out where data lies in memory. We begin by setting breakpoints so that we can pause and assess the situation while the program is running.

We have set two breakpoints, one before it requests for an input and one after reading input. We want to analyze the data on the stack so that we know where the userInput and authorized variables lie in relation to each other.

Step 2: Locating Variables on the Stack

We let the program run and it stops at the first breakpoint.

Here, we print out the stack and attempt to find the variables. We can find the address of authorized (p &authorized) and userInput (p &userInput). Fantastic! The authorized variable is located after the userInput address! That means we can overflow the array and "inject" whatever we want into the authorized variable. Not only that but it's located directly adjacent to it! Sweet! Let's continue!

Step 3: Overflowing the Buffer

Okay, so we've calculated the string length we require to fill the data in the userInput array to reach authorized (51). We are then presented to give an input to the program.

We type in whatever 51 characters we want followed by 0x01 which represents the character for the decimal 1. The problem with just entering the number 1 instead is because it will be read as a character and that would give it the value 49, which is incorrect! We need it to be 1 otherwise the condition to spawn the shell would not be satisfied. Printing out the stack after giving the input, we can see that we have successfully "injected" a decimal 1 into the target variable. Continuing the program, we see that the string we gave it was obviously incorrect however, we still satisfied the condition! As a result, we have forced execution to spawn the shell!

Step 4: Owning the Box

50/\/\3 1337 57uff h3r3 0r \/\/h473\/3r...

A Fix for Scanf?

There is a way to prevent this from happening. You can use fgets...
.
.
.

Okay, if you really need to use scanf, you can set a limit to how many characters to read with the string format specifier. All you need is to type in the maximum length in the form of %ns where n is the length (don't forget to account for the null terminator!). For example, scanf (%50s, userInput);

And there you have it. It's not really that nice because you cannot put a variable in place of the max length, unlike fgets, so every time you change the size of your buffer, you will need to manually change your scanf's format specifier.

Conclusion

Take care of your input! Set a maximum size so your buffers don't explode and leak everywhere! That's pretty much it... for now... Hope you've enjoyed this presentation!

dtm.

11 Comments

Hey there, Bengineer!

I'm glad that you enjoyed it! If you want to stick around a bit longer, you might be able to catch some more exploitation methods through the buffer overflow vulnerability or other vulnerabilities which I will introduce later.

dtm.

Nice Tutorial,
Can't wait to learn more reading your C articles.

When I tried to type "^A", it was always "^" and "A", it wasnt "start of a header" so it represented for "415E". Could anyone tell me why?

Alright, I've found it out. Use "Ctrl+V" and "Ctrl+A" instead of "^A".

Hey Silver Doll!

If you need to type in hexadecimal characters in terminal, just press and hold Ctrl + Shift + U followed by the hex numbers.

Thanks Donttrustme, but how do I type NUL character (0x00) in terminal, I try to press Ctrl + Shift + U but if in terminal, it doesnt work, it only work when I type the password in your program.

If you want to print a 0x00 you can try using the command line with a program such as Bash, Perl or Python with echo to pipe into your program. For example:

echo $(perl -e 'print "\x00"') | ./a.out

Ok, thank you for sharing.

Hi,
First of all thank you for your tutorial, it's amazing :)

I just have a problem when I try to analyse the memory. When i try to get the ESP, I got this result : (what does the -6528 mean ?)

(gdb) i r esp -> esp 0xffffe680 -6528
and then when I try to explore the memory, i got this :
(gdb) x/40xw 0xffffe680
0xffffe680: Cannot access memory at address 0xffffe680

Am i doing something wrong ? I'm on KALI linux and the test program is written in C... looks very similar to what you wrote...

I hope someone can help me, I'm sure I am missing something stupid here ;)

Are you sure you used the -m32 flag in the compilation program?
x86 and x64 architecture have different registers name.

Try to use rsp instead of esp

Share Your Thoughts

  • Hot
  • Latest