Hack Like a Pro: How to Build Your Own Exploits, Part 2 (Writing a Simple Buffer Overflow in C)

How to Build Your Own Exploits, Part 2 (Writing a Simple Buffer Overflow in C)

Welcome back, my amateur hackers!

Over the course of the next year, we will be developing our own zero-day exploits. In my first article in this series, I introduced you to buffer overflows, which are the source of some of the most lethal exploits, particularly the "remote code execution," so we are focusing our exploit development here on a buffer overflow.

Developing your own exploits requires considerable knowledge and skill, so this series will toggle between providing you background material and information and labs to test and expand your skills.

In this tutorial, we will build a simple buffer overflow to demonstrate how a buffer overflow can work. We will build a short, simple program in C, compile it, run it successfully, and then attempt to overflow its buffer and get our own code to run. Although it is far from a sophisticated exploit, I think it demonstrates well what we are trying to achieve in developing our buffer overflow exploit.

Step 1: Open Up Leafpad

First, fire up Kali and open a text editor for entering our code. In my case, I'll be using Leafpad, but you can use any text editor you want. To open Leafpad, go to Applications -> Accessories -> Leafpad.

Step 2: Write the Code

I have developed a small bit of code that will enable us to overflow a memory buffer and run our own commands on the system. It is not meant to be used as an exploit, but rather to simply demonstrate the principle of buffer overflows that we will be building into our zero-day exploit.

Now, enter the following code as shown below.

In the screenshot below, I have highlighted our two variables we will be using in this code;

char *place
char *systemcommand

We have declared them below both "char" or character type variables.

Next, we have allocated memory for each variable using the malloc (memory allocation) command.

After the memory allocation, we have two "printf" statements that print the memory locations of the two variables. The third printf below then calculates the number of bytes between the two memory locations of our variables.

The fourth printf then asks the user "Where is the best place to learn hacking on the web?" followed by the "gets" function that puts the users response into the variable "place."

This is then followed by another printf function that prints the statement "The best place to learn hacking on the web is" followed by the user's response. Of course, the user will respond with "Null Byte"... what else?

Finally, the last line executes whatever is in the "systemcommand" variable. If the variable is empty, then no command is executed.

Finally, let's save this file as bufferoverflow.c.

Step 3: Compile

The next step is to compile our new program. Compiling is the process of converting our source code, in this case C, into machine code. It's required whenever we write code in a compiled language, unlike say, Python, that is an interpreted language (interpreted languages are converted to machine language on the fly at run-time, line by line, and are thereby slower).

We need to use the GNU C Compiler (gcc) on the file bufferoverflow.c and output (-o) the compiled to a new file named "bufferoverflow" or whatever you choose to call it.

kali> gcc bufferoverflow.c -o bufferoverflow

This may produce a few errors, but you can largely ignore those.

Step 4: Run the Program

Now, let's run our little "bufferoverflow" program.

kali > ./bufferoverflow

Notice that it first responded with the memory location of our variable "place," and then the memory location of our variable "systemcommand," and third it calculates that there are 16 bytes between these two memory locations. It then prompts the user for "the best place on the web to learn hacking" and, of course, the user responds "Null Byte." Finally, our little program responds with the obvious truth "The best place to learn hacking is Null Byte."

Step 5: Overflow the Buffer

Now, let's run this program and try to overflow the memory area for the variable "place" into the memory area for "systemcommand." If we can overflow that memory area into the variable "systemcommand," we should be able to execute any system command on the system. For instance, we might be able to execute a command shell or display the contents of the /etc/shadow file.

We know from the third printf statement, the space between the "place" variable and the "systemcommand" variable is 16 bytes. This means that if we enter more than 16 ASCII characters (each ASCII character is one byte) when prompted, starting with the 17th character, whatever we input will move into the next variable, in this case, "systemcommand."

Let's now enter the following when prompted:

kali> nnnnnnnnnnnnnnnncat /etc/shadow

When we do this, the first 16 characters will go into the "place" variable and the 17th character and everything after will overflow into the "systemcommand" variable. The final line of our code will then execute the system command variable.

Notice that that we have been able to overflow the "place" variable into the "systemcommand" variable and have been able to "cat" the contents of the /etc/shadow file thereby showing us all the users and their hashed passwords. Congratulations! You have successfully overflowed the buffer and run your own code.

Over the course of this year, 2015, we will work step by step toward developing our own zero-day exploit in this series, so keep coming back, my novice hackers!

Just updated your iPhone? You'll find new emoji, enhanced security, podcast transcripts, Apple Cash virtual numbers, and other useful features. There are even new additions hidden within Safari. Find out what's new and changed on your iPhone with the iOS 17.4 update.

Cover image via Shutterstock

51 Comments

The third printf got truncated. The end should read "systemcommand -place);"

Wow, This can't be that simple! Lol
Great tutorial.

I'm curious to learn more. I guess one of the next difficulty would be to find out how many bytes you have to fill in order to reach the command part of the memory.

Damo:

You are right, it is not that simple. This was designed to demonstrate the principle of buffer overflows. I wrote the C in a way that it could be overflowed easily. Things aren't that simple in the real world.

As we progress through this series, I will show how to do this in the real world.

OTW

Hi there ! Thanks for the tutorial !
I just wanted to add a tip for lazy guys like me :

When changing the code, you might have a different spacing value between the two variables (32 instead of 16 or let's say x bytes), don't bother typing x times "n" (or any other character) and then type your favorite command (cat /etc/shadow here). Just use space character (keep pressing space bar) to get over your spacing value you have then type your command. Could be useful when you don't know the actual spacing value !

that is a pretty nice tip! Thanks for that :D

Nice tutorial thanks.But i can't understand why it doesnt work on other Linux systems like Ubuntu?

Hi,

I did this on Kubuntu and it works just fine for me. What goes wrong for you ?

There should be no reason this doesn't work on other Linux systems as far as I know, it's just compiling a c program and inputting via the command line. What errors are you getting? Also I apologise to cracker if I seem rude, but as otw said it's not a script, it's a pretty important distinction, this is a program with direct access to memory which is why this overflow can happen, in a scripting language there is bounds checking done for you by the interpreter. I believe this to be true, my apologies if not i am extremely tired from revision, unfortunately have lots of software engineering slides to read instead i'm sat looking at null byte again lol

Ah sorry Brian, just seen your post explaining it.

I've figured out why it wasnt working.That's because my memory space was 32 bytes.Now when i type 32 letters before cat command it works perfectly.But i'm wondering why the space is different?Maybe because of 64 bit system ?

Here is my source code :

http://pastebin.com/2rDSBD9C

place is a char which takes 8 bits in most systems arbitrary of the register size . Please paste the output of the following program, in order to further debug this strange behavior.

#include <stdio.h>
#include <limits.h>

int main(void)
{
printf("Number of bits in a Char: %d\n " , CHAR_BIT);
printf("Minimum value of a Char: %d\n " , CHAR_MIN);
printf("Maximum Value in a Char: %d\n " , CHAR_MAX);
return 0;
}_

This is my first post in this great arena of knowledge. Seriously hats off to the initiative. I would strongly recommend anyone who is interested in computers not only hacking or tweaking, because in order to tweak a system we must first know the system. And this place defines every concept of computer science very minutely. All in a a great experience for an open minded learner.

Looking forward to follow this forum of knowledge very closely.

Thank you for sharing.

P.S I don't know why my underscore was omitted when I posted it the first time around. Please guide me to a tutorial on the text editor which the site uses. For now, lesson learned we need to use double underscore inorder to post a underscore in this editor.

Sorry, should have posted it as reply to Mr. Cyber's Post above. Pardon me if I caused any confusion.

Hi there,
I also have a spacing value of 32, here is the code and result :

Then with the overflow :

I typed a lots a whitespaces then "echo still Nullbyte !" and we see as expected that the systemcommand variable is filled with whitespaces (discarded by the shell) and my command which is executed by the shell as we wanted. Nothing new, just the illustration of the whitespaces trick.

Edit: off topic, but there is an error in my code: my main should be declared as int if I return 0. Or as void without returning anything.

Hi.Thank you for your assistance.Here is the output of above program on my system :

Number of bits in a Char: 8
Minimum value of a Char: -128
Maximum Value in a Char: 127

Same here for the code RND provided. And we can check for the size of "place" and "systemcommand" variables with the function sizeof() which gives me 8 bits in both cases. So I'm not sure why I have a 32 spacing value...

Is there anyway to get access to the shadow file, so that I can simulate the overflow like in the example above?

Thanos:

Welcome to Null Byte!

I'm not sure what you are asking. Are you asking whether there are techniques to get to the /etc/shadow file like this buffer overflow? The answer is yes. There are many ways, but obviously the easiest and most direct would be to escalate privileges to root.

OTW

I guess what I'm trying to say is where did you get the shadow file?

We overflowed the memory area and executed the command "cat /etc/shadow".

Thanks, I was able to find the shadow file and buffer over flow it. My c program was on the desktop instead of in the root where the shadow file is located.

Thanos :

It doesn't matter where the actual program is located,in the example above we are accessing the file directly via buffer overflow with root privileges.If you try this without root privileges you will see "Permission denied" error.

How to get that working when executing as not-root?

Asdf:

This demonstration only works if you logged in as root as only root can access /etc/shadow. You could change the command to "cat /etc/passwd" and it will work for any user.

this is now really getting exciting...you know you're whetting the appetite of some of us :P
just keep it flowing Master OTW!

Question: Why do I need to overflow the buffer to run a system command? If I can execute a C Program in a system, I can can make all sort of system commands.

I suppose there is something more to this, which I am failing to notice , or may not have been revealed to this point. Hoping, someone would clarify my stupid question.

Thanks.

Rnd:

This is meant to demonstrate the principle of buffer overflows. The C program represents any app on the system that might be vulnerable to buffer overflows. If you can overflow the buffer in an app, you can remotely execute any system command.

OTW

Hey OTW

Thanks a lot for this great series. I've recently read a book called "Hacking: The Art of Exploitation", which talks about buffer overflows in depth. The problem with the book is that it was written over 6 years ago, and it doesn't account for ASLR, x64 architecture, and many other fixes.

Can you please make an article about the best resources (books, blogs, etc) for learning about exploits, namely buffer overflows ??

I truly thank you for all this great information.

Shellcoders Handbook is really good.

If the BO isn't working, that's probably because of ASLR protection mechanism in latest OSes, Windows, Linux and OS X. ASLR is designed to protect from BO exploitation. So before compiling the vulnerable C code, one has to disable ASLR and other protection mechanism.

When can we expect the next post in this series?

I have eight different series running, but I'll get back to this one soon.

Thank you very much, very interested

Ok so my question is that you allocated 128th block to systemcommand and 10th block to place... then why is the space between then 16 bytes instead of 118?? And why others are also getting 32 bytes??

Some help me?? I really want the answer to the question ;);)

Think in terms of bytes(8 bits) and the distance between the start of each of the blocks.

But 118 doesnt divide by 8... and why are some getting 16 bytes and others 32??

The starting points are of each block are 128 blocks apart. When you divide by 8, you get 16 bytes.

Ook!! But why are some people getting 32 bytes??

Different operating systems.

Because operating systems allocate system memory, and compiler configurations, architectures and what not, your program won't compile exactly as OT's did. Your memory address's will be slightly different, but all the same principles still apply.

Hello OTW sodacni here

I tested this in Kali Linux, var place got a 16 byte block of memory, in windows got 24 bytes block of its different OS but why 24 and not 32?

And in future guides of Hack Like a Pro: How to Build Your Own Exploits, will you make any advanced guide on how to take advantage of programing an malicious program, i think you are more focus on reverse engineering programs that are already made and find buffer vulns, what im saying is, something like a legit C program made by us(Social atack, make the victim think its legit) that at some point we use deprecated and vuln function like gets() and instead of a system command use our own exploit/malicious piece of code.

...or you could simply take one of the thousand free open source ssh / ftp / http servers out of sourceforge, and remove a buffer check in a pre-auth command. Modifying just a few lines of code may lead to BoF as well. It's a nice exercise if you want to practice.

My Point is, make it, we cant wait for 3rd party exploits to come out. And there are situations that is better make our own exploits than adapt code form others.

OTW, loving this series. Thank you for all the time you put into these.

It's probably worth mentioning for the benefit of other commenters here that your results won't necessarily be the same as the test run presented here. In my test run, I actually saw that my system (Windows 10 x64) allocated the space for systemcommand at a location in front of the space allocated for the place variable. In this case, the 3rd call to printf shows that the difference between place and systemcommand is a negative number, making it impossible to overflow place into systemcommand. If you run it a few times, you will eventually find an ideal situation to demonstrate the overflow.

To respond to NOPES about the 24 bytes of space, as I noticed when I ran the program, Windows systems obviously do not allocate memory sequentially on the stack. It could also have something to do with the compiler used. I used a MinGW distribution of the g++ compiler, just because that's what my IDE is set up to use. When it comes to memory allocation, you can expect different results on different platforms.

Correcting myself here... In OTW's example, we're not dealing with the stack. In C, malloc allocates memory on the heap, which is never guaranteed to be sequential. Memory allocated on the stack I believe is always sequential.

Why do we have to manually allocate the space for the variables ? When I let the Os handle the memory allocation, the addresses are WAAYYYYYYY too far apart.

Also, you haven't freed the variables. Is it okay ?

Hey, so, i know its quite nice to actually know the amount of bytes an variable occupy in the system for this kind of stuff, but i found pretty dumb to actually have to count and type an X number of characters so i made a little improvment on the code for anyone who wants.

#include<stdio.h>
#include<string.h>

void main() {

char *place;
char *systemcommand;
char *command;

place = (char *)malloc(10);
systemcommand = (char *)malloc(128);
command = (char *)malloc(128);

printf("Memory address of place is: %d\n", place);
printf("Memory address of systemcommand is: %d\n", systemcommand);
printf("The space in memory between place and systemcommand is: %d\n", systemcommand-place);
int space = systemcommand-place;
printf("Type the command: ");
gets(command);

for (int i = 0; i < space; i++) {

place[i] = ' ';

}

for (int i = 0; i < strlen(command); i++) {

place[i + space] = command[i];

}

printf("Place: %s\n", place);

system(systemcommand);

}

While easier, I think the intent was to show how bad data can trigger it with no internal programming to support it.

bro can you make an ios kernel exploitation not like quwerty just the first steps of it

Share Your Thoughts

  • Hot
  • Latest