Today, we’re going to introduce how to write a buffer overflow for CVE-2004-2271. CVE-2004-2271 is a buffer overflow condition in MiniShare 1.4.1 and earlier which allows remote attackers to execute arbitrary code via a long HTTP GET request.

If you want to follow along, you can download the vulnerable application from my github:

We’ll be doing this with a Windows 10 and seeing if things work. Should be fun! You can download a copy of Windows 10 here.

We’ll put them both on a virtual network together with no public internet access within VMWare Fusion and then install Minishare, Immunity Debugger and add the Mona script to Immunity. Lastly, we’ll disable our firewall so that we can work on the exploit directly The setup details are outside of scope for this discussion. With that said, please shut off DEP, in case it’s on!

Disabling DEP on Windows 10

bcdedit.exe /set {current} nx AlwaysOff

Let’s dig in, this should be fun!

1. Fuzzing

The first thing that we need to do is figure out where the vulnerability occurs. We know that this has to do with GET requests to a server, so let’s create a tool in Python which will send progressively longer GET requests to the MiniShare 1.4.1 server. To do this, we’ll build the following Python script:

So what does this do? Great question! The way this script works is it starts off by asking the user for some arguments. Specifically what host is the service we are going to fuzz running on (rhost) and what port is the service listening on (rport)? This occurs in the function get_args, as seen below:

Next, we print out a basic banner. This isn’t necessary, but is useful for seeing what configuration we’ve passed to the program. The banner function just puts together a basic text based setup displaying the information. This occurs in the banner function, as seen below:

With that out of the way, we begin the actual fuzzing process by calling the fuzz method, giving it our remote host and remote port. Let’s take a look at this function before breaking down what’s happening.

So first, we set our initial length to 100. We’ll use this when we actually create our buffer that we send to the program. The reason we choose 100 though is so that we can take small steps so that when we crash the application, we know pretty accurately how large of a buffer we need.

With the buffer put together, we use the socket library to connect to the remote host on the specified port. We tell the user how large of a buffer we’re going to send so they can keep track of progress and make sure it’s working as they expect, then we send the actual buffer. Next, we sleep and close our connection to the host.

With our payload sent, we increment the length another 100 bytes, and then try again. In the event that we can’t connect to the host, we’ll trigger an error and tell the user when we had crashed. Since we incremented the buffer size at the end of the last attempt, we’re going to need to remove those added 100 when we print out the length.

Pretty simple function. Basically it’s building the following type of request:

GET AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
HTTP/1.1

Nothing special or crazy there. So lets start MiniShare 1.4.1, attach Immunity Debugger to it and then run the fuzzer and see what happens. With the fuzzer running we see it crash:

The fuzzer successfully crashed the MiniShare application

Not only have we seen it crash, but we’ve seen that our A’s have overwritten the EIP register, so theoretically, we have enough to take control of the program’s execution. Let’s replicate this and hone it.

2. Skeleton Exploit

Now that we have a crash that works, we want to try to replicate the crash and make sure we can crash it anytime we want. The key to doing this is to try to use a buffer that is the same size as when the fuzzer caused the application to crash. In our case, that’s roughly 1800 bytes.

So to create our skeleton exploit, we need to modify a few things. Specifically, we’re going to change our fuzz function into an exploit function and remove the loop.

With a successful crash, we now can focus on weaponizing our exploit and setting up some type of control. We are overwriting the EIP register, and can see some of our buffer went into ESP. If we’re lucky, this will be enough space for us to put our full shellcode that we want to use during the exploitation.

But before we worry about that, how can we figure out where EIP is in our 1800 byte buffer? We could count I guess, maybe even write our own custom string, but lucky for us the Metasploit Framework has a great tool to help us identify the location. This tool, is pattern_create.rb and pattern_offset.rb.

3. Gain control of EIP

These tools can be used to build a unique string of any length (that I’ve tested) and then identify which location in the pattern we found. Let’s build a string the length of our buffer:

With this, we can now update our skeleton exploit to locate EIP. We’ll add a new global variable named PATTERN and then in our build_buffer function replace the ‘A’*length with PATTERN. This leaves us with:

Perfect, supposedly our EIP register starts after byte 1788. We never want to trust this blindly though. Let’s verify this. To do that, we’ll remove our pattern (it took up a lot of space in the file anyway) and create two new global variables:

This way, we’re filling our up the buffer up until EIP with A’s (\x41), then we put B’s (\x42) over EIP, and then fill any remaining space with C’s. Wonderful! Let’s check if our update works. Updated code is:

Not only do we now know we have control of EIP, we also seem to have control of ESP. This could be nice and easy. So next we need to know if there are any characters we can’t use in our payload.

4. Identifying Bad Characters

In this case, we already know of a few characters that we can’t use:

\x00 — Null byte

\x0a — Line feed / New Line

\x0d — Carriage Return

Why can’t we use these characters? Glad you asked. So if we take a look back at the vulnerability, we’re making an HTTP request. This means that we’re going to terminate the request with \x0A\x0D\x0A\x0D. If we’re not careful, this could appear earlier than we want in our payload. Thus, if we remove it, we can hopefully safely use the characters. Why are we removing a null byte? Well…it just causes problems more often than not, so I prefer to remove it if I can. But are there any other bad characters? Well, we have to check. To do this, we’ll add a new function:

Now, we’re going to start going over the buffer size we’ve started with, so it’s important for us to watch and make sure that we aren’t losing our control. Sometimes, when the buffer size changes too much, a different crash that isn’t exploitable will occur.

With that, we’ll restart minishare and set a breakpoint at our new EIP target address. Run our new exploit and see if we trigger the breakpoint (meaning things worked as expected).

Nothing. $#!7. Looking at it again we can see the problem, we don’t actually have a memory address we can use. The \x00 in the address is a null byte which was one of our disallowed characters. So now what do we do?

Author Kevin Kirsche

Kevin is a Principal Security Architect with Verizon. He holds the OSCP, OSWP, OSCE, and SLAE certifications. He is interested in learning more about building exploits and advanced penetration testing concepts.