Buffer Overflows – An introduction with SLMail

Hey everyone!

This is the longest post I have ever written. It is going to get quite technical too (for what I assume are the people that read this. I could be completely wrong). I hope that you are with us at the end, as this is a really fun attack.

To me, buffer overflows are the most like “movie hacking” that there is. That bit at the beginning of Hackers where Dade and Kate are fighting for control of a TV station, in order to become the one who invents Netflix. It’s just like that.

The setup that I am using is a couple of virtual machines. One with Kali Linux running on it, and the other using Windows 7 Ultimate edition. If you want to follow along at home, please feel free, just beware that you will get a couple of different results with different versions of Windows.

The program we are going to be exploiting is an old one called SLMail. It’s an email server program. The exploit is a classic as far as buffer overflows go, and I have seen it used for teaching this exploit in several places. Great for making one more tutorial about!

It might also be useful at this point, early on, to tell you what a buffer overflow is. Essentially it is where a program will crash, usually because someone sent it a command it was not expecting. When developers make these programs, often they will think “Who would ever send a 4000 character password?”

We would.

And we will.

The reason they are under these constrains when developing software is a result of the C programming language. It says that you have to declare how long a buffer is going to be. The buffer that will hold your variable, like a Password. The Username is also a variable that is put into a buffer. In fact loads of things are put into buffers, all the time.

These buffers, containing the variables that we send (like a password) are then put onto something called The Stack. At the risk of getting into the weeds here about what that is, think of it like a stack of trays at a restaurant or a cafeteria. Trays can only be put on the top of the stack, and they can only be taken from the top. The last tray to be put onto the stack will be the first one off.

Now we can think of these trays as functions in a program. When a computer wants to do something, like say print a variable, it will put the print function along with anything else it needs to do the job onto the top of the stack. It will then work it’s way down the stack, trying to do each of the functions.

The computer will get to the print function, and print the stuff we want pop that function/tray off the top of the stack, and then move onto whatever was next in line.

Buffer overflows happen because we give the tray a larger thing than it was expecting. The trays, or buffers, are of a fixed maximum size. That means that when we send a super long password, the computer will put that into a buffer, put it onto the stack, and then try to execute it. Whatever we put in that super long buffer will then bleed over into the next tray.

We will be overwriting the next tray with whatever is in our password. The computer will then get to that thing it expects to be a function, and find out that it is something it doesn’t understand at all. It will then crash.

What we are going to do is to try and force the program to crash, and then get it to do what we want it to do instead of whatever was next in the stack. We will do this by adding a “legit” function to the end of our password. When the computer goes through the first tray, it will just see a long password, although we will have secretly over-written the tray underneath us.

We are going to try to get the computer to dial back a connection to us, so that we can have a look around it. It is possible to do anything though, from opening the calculator program (what the cool kids call ‘popping calc’) to getting the computer to sing the national anthem of whichever repressive regime you desire.

If you are following along at home, then you can download the program by visiting this link and clicking on the Vulnerable App icon:

https://www.exploit-db.com/exploits/638/

Just a word of warning though. If you install this on your network, and people on the internet can see it, you will very likely get hacked. Probably in under an hour. So don’t do that. Put your VM’s on a private network with each other with no connection to the internet.

With that out of the way, let’s get our hands dirty.

First off I installed the SLMail program on my Windows 7 machine using all defaults, just click Next the whole time. Then started the service as administrator. Depending on your version of Windows, you may also have to turn off the Firewall.

To double check that it was working OK, I made a quick netcat connection to it, using the command:
nc 192.168.97.130 110
I was greeted with a login prompt, where I could enter “USER ‘username'” (enter), then “PASS ‘password'” (enter). So far so good. This is a POP3 connection (port 110), and not a SMTP connection (port25). Let’s automate that with a small python script, which will connect and show the responses, then disconnect.

This is a basic skeleton (It is after-all, Halloween soon) of our exploit. It is given an ip address and a port, and an opportunity to receive data. Then we send it “USER legitness”, wait for the response, then send it “PASS 2legit2quit”. Pretty simple.

Now that we have out python script that can connect to the service, let’s try to crash it.

This step is called ‘fuzzing’. Fuzzing is where you send unexpected or random inputs to a computer program. The wikipedia article covers it quite well if you are interested in learning more.
If you are a software or games tester who is reading this that does not know what it is (I didn’t for probably the first 12 years of testing) then I would highly recommend reading more. In fact create python scripts to connect to services, and chuck random stuff at them, just to see how well they perform.
We are going to cheat a little bit, by knowing which field in the login process to fuzz, the password field. If you were to fuzz an application without knowing that it would cause a crash, you could be fuzzing for months before finding something. You could also be fuzzing for only a couple of minutes.
So let’s start by sending a bunch of “A” characters as our password. We will then close the session and re-try, gradually increasing the length of the password.
We are going to change our python program a bit so that it starts off sending a password of just 100 “A” characters, then increases it by 200 each time, until it crashes.

This code is pretty similar to the one in the OSCP course-ware, if you are coming from there.

We set this code off running and note that it crashes somewhere in the range of 2700 bytes. That means that if we send a password of 2700 letters, we can overwrite the tray underneath ours in the stack. Or at least we think we can. At this point we will need some additional visibility into what is actually happening inside the SLMail program.
On our Windows machine, we can also run a program called Immunity Debugger. This debugger will essentially hook into the program and tell us what is happening under the hood. It can give us all sorts of information, like where things are crashing, what inputs the program receives. Debuggers are SUPER useful. The font always seems to be made for ants though.
Immunity is a free program, that you can get from Immunity Inc, right here:
We are also going to use a plugin for Immunity called Mona.py. You may have already guessed that it is written in python. You can get Mona.py here:
As we crashed our SLMail program, we have to restart the service using the awesome Traffic Lights UI. Then we can start Immunity, also as administrator. Now that Immunity is up, we can ‘attach’ ourselves to a running service. Click File >> Attach and then select the SLMail program. We then need to press the F9 button once or twice, so that the program is no longer in a paused state, which it enters by default when we attach to it.
Once it is all up and running it looks like this.
1
Pretty hacker-ey, right?
I also changed my exploit code to the following, in order to figure out where exactly the code is crashing.

Just so we are clear on why I did this, we already established that the program will crash when we send it something that is 2700 characters long. We also know that it won’t crash if we send it something 2500 characters long. This way we split the difference and add it to the buffer we know won’t crash to get us halfway there.

This means that if we see only “A” characters in the debugger when the program crashes, it will be in the range 2500-2600, but if we see “B” characters, it will be in the range 2600-2700.

Let’s send off this, and watch what happens in the debugger. This is what it looks like in the top left window. The registers are special variables the computer uses when it is dealing with the stack.

2
EIP and ESP are the only two we will need to care about here. EIP is called the Instruction Pointer, and ESP is called the Stack Pointer. As 42 is the hex character code for “B”, we can see that the EIP has been over-written with our super long password, and at the ESP there is ASCII “BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB”. This puts us 100% in our 2600-2700 range.
Progress!
Our next goal is going to be to figure out exactly how long the password needs to be. We want to be able to control the EIP register which pretty much points to the next instruction the computer is going to do.
If we can give the computer a different instruction, we can control the flow of execution of the program, making it do anything we want. Like buying us ice-cream.
So let’s modify the buffer line in our code:

We also need to close Immunity, restart SLMail and then restart and re-attach Immunity to it, and un-pause the program. Running our new python code, we see exactly the same crash!

As we see all those “B”‘s again, it means that our crash is somewhere in the range of 2600-2650. If you right click on the ESP in the top right window, you can select “Follow in dump”, which will make the bottom left window show you what it looks like in the memory of the crashed program.

There are a-lot of 43’s, 42’s and 41’s in there. These are the ASCII representations of the A/B/C characters.

So from this point on, it is pretty much a matter of repetition to keep dividing the area we have left, adding it to one side, and then re-trying until we find the exact spot where the program crashes in our password length. If you want to go through that, then do not read the next sentence.

We are going to cheat. It’s 2606.

I say cheat, I did it earlier, and it took around 9 tries, which would be super boring to read about. So let’s not, and just accept that it is there.

So now we know where exactly the program crashes, it is time to outline exactly what we are going to do next.

We want the computer to run some code of our own (in this case, dial back a connection). For this we need something called “shellcode”. Shellcode is called that because it is usually written to send a command shell back to an attacker (exactly what we are going to do). Our shellcode will be written in machine code. That means it will be written in a way that the Stack Pointer and the Instruction Pointer understand and can execute.

We also have to tell the computer exactly where this shellcode is going to be. We are going to put it in our password buffer that we send. The computer will get to the maximum length of the password, 2606, then look at the EIP to see what it should do next.

As we control the EIP, and have written over everything underneath the stack, we can tell the computer to jump to the ESP register. When it jumps there, it will start to execute the commands at the top of the stack. Those commands will be everything we send immediately after the 2610’th byte.

The reason for this is that we send 2606 byte’s of Password, 4 bytes of EIP, and then however many extra bytes containing our instructions to the computer (shellcode).

We can test out part of this, by changing our buffer in the following way.

The ret_addr is for “return address” which is the technical name for what we want to control.

Try this out in Immunity. When it crashes, right click the ESP and “Follow in Dump”. If you look closely, you can see we have spelled out the word DEAD. That is because 44 is “D”, 45 is “E”, 41 is “A” and at the end is another “D”.

At this point you should probably give yourself  a high-five, because you earned it!

So let’s make ourselves some shellcode. For this we will use a program installed in Kali Linux called msfvenom. You can find out what the options for it are by just typing in the command. Or you could just look at this screenshot below.

3

We obviously want to use a payload, but if you try just “msfvenom -l payloads” you will get all 600 or so of them, which are not so easy to sort through. We know we want windows, and the type of connection I have been talking about so far is a reverse shell. Let’s pipe the output through grep to find which one we need.

4

If we use the payload options command on this payload, we can see what information we need to supply for the shell to dial back to us. Obviously we are going to need an IP address for our attacking machine, and a port that we are listening on.

5

Trying this out though, you might make this sound. “Bleuurgh”

6

Or perhaps “What even is that?”

The problem is, that we need to encode it, for inclusion in our python script. There are some other problems with it too.

Now we have to deal with something called “Bad characters”, and I don’t mean the type of person you meet at a kebab stand at 4:30am. Bad characters for us, are those in the ASCII range (remember that “A” is 41 etc) that will stop our code from working.

There could be any number of reasons why a character is bad. Bad in this sense is also only bad for our exploit. When we send this exploit code, in the python script we end each line with “\r\n” for Carriage Return and Newline. These are both ASCII characters. We can see all the different types of ASCII characters here, along with the hex number of them under “Hx”

http://www.asciitable.com/

We can see there that “Carriage Return” has a hex value of D, and “Newline” has a value of A. These are perhaps a bit misleading, as we have already spoken about “A” and “D” having hex values of 41 and 44 respectively.

You can see “A” and “D” further along the table in that link. Carriage return can be better expressed in our python script as “\x0D” and Newline as “\x0A”. We know that these are going to be bad characters when it comes to sending stuff to the SLMail program, as it will instantly interpret these as ending the password field by pressing return.

If we then type out all the ASCII expressed characters from “\x00”, “\x01”, “\x02” etc, all the way to “\xFF” and send it to the SLMail program, we can examine the crash in the buffer and see where the sequence stops.

We can introduce a new variable called “bad_chars” with all the ASCII characters from “\x00” to “\xFF” immediately after our ret_addr. Doing this the first time, we see that almost nothing is there. It stops directly after “\x00”.

This is not as bad as it sounds, as the “\x00”, or null byte character is used in C to determine where the end of a buffer is. If we remove this one and try again, we can see that when the program crashes, there are no more characters missing. Our bad characters are “\x00”, “\x0A” and “\x0D”.

That means that we can go back to our shell code generating, and tell the program not to include any of these characters when it spits out the shell code. We can also tell it to spit out the shell code in a format that will better fit our python program.

7

Let’s just run through these command line flags to make sure we understand what is being done. The “-f c” will output the shellcode formatted for C. the “-a x86” will make sure it is for 32 bit operating systems, not 64bit. The “–platform windows” is probably pretty self-explanitory, and the “-b “\x00\x0A\x0D” is to tell the program our bad characters and not to use them. The “-e x86/shikata_ga_nai” is an encoder. This will transform the shellcode, obfuscating it somewhat from anti-virus, and allowing us to remove the bad characters,

So now we can put this shellcode into our exploit, and put a bit of meat on those bones. The payload size of 351 bytes is important for us to remember. We sometimes don’t have so much space to work with, so it is important to know where you are in memory.

So now all we need to do is find a way to tell the EIP to jump to the ESP when it receives our code. We can do this by writing a memory address at the EIP. When the computer reads the EIP, it will do whatever it finds at the EIP. We want it to jump to the ESP register, so that is what we should aim for.

This is where Mona.py comes in. With Mona.py you can click at the text entry box at the bottom and enter “!mona modules”. This will bring up a list of all the modules that are loaded with the program when it starts.

8

I have highlighted one there. Remember I said the font was for ants? It is the SLMFC.DLL. The reason it is highlighted is the number of “False” flags it has in those columns. Those are information about how it was compiled. This one was compiled with ASLR and DEP turned off. ASLR and DEP are protections against exactly the kind of attack we are making here. We are relying on the return address being the same everytime the program loads. If ASLR and DEP were turned on, then they would not be the same.

If we click the tiny, tiny “e” button at the top in Immunity, we will bring up a menu of the executable modules. It should look like this.

9

We have our SLMFC.DLL highlighted. Double clicking on this will show us all the code in this DLL. We can then right click and search for a command (Or just use Ctrl + F), such as JMP ESP.

JMP ESP does pretty much what it sounds like.

1

Sadly we get a “Item not found”. Normally within a large DLL such as this, you would find a JMP ESP instruction somewhere.

1

This is a shame, but not the end of us by a long shot. We can also search for the machine code equivalent of this instruction, which is something called “opcode”. Take a look at the wikipedia article for more info.

https://en.wikipedia.org/wiki/Opcode

If you don’t want to, just know that it is a different way of telling the computer the same thing. Jump to that ESP register. The opcode for JMP ESP is “FFE4”. We can search for this in the SLMFC.DLL using Mona.py again.

Down in the bottom text entry field again, we can type in “find -s “\xff\xe4″ -m slmfc.dll”.

1

I hope you set the keyboard languages on your virtual machines properly (unlike me), so that you aren’t looking for the slashes and quotes everywhere.

As we can see, Mona finds quite a few of these strings, but the first one, highlighted below, will be fine for us.

1

So now we have a return address (which has to go in backwards, because for some reason, that was decided as a good idea by the people who came up with the “little endian” nature of these computers ), we have some shell code, everything should be fine and dandy, right? Of course not!

We have to add in some NOP’s NOP’s are “No Operation” commands, which tell the program to do nothing, and then move onto the next command. We need these as our shell code comes with a small de-coder (remember we encoded it with shikata_ga_nai) at the front, and if we don’t give it some space (say 16 NOP’s) then it can trip over its own feet. We don’t want that.

So let’s put it all together. We have

2606 * “A”

+

Return_address

+

16 * “\x90” (90 is the ASCII for a NOP)

+

Shellcode

Phew!

All that is left, is to put it all together and set up a netcat listener on port 443 as we specified earlier in our shellcode. Restart the SLMail application, and re-attach Immunity debugger too.

Start the Python code and if we are very lucky…..1

Hurrah!

We now have a completely functioning exploit! The command shell should work as if you were logged into the Windows machine, and it will be running with the same privileges as the SLMail application was started with (administrator).

I hope you had a LOT more fun reading this than I had typing it ( ~4,000 words and counting, thank you for staying with us this long). I really love this kind of exploit, and plan to seek out other vulnerable programs to practice on, and eventually post about, as it is one of those areas that I have identified where I need improvement.

I don’t know if I will have anything posted the next couple of weeks, as I am going abroad for work, and am unsure what I can pull out of the bag whilst living in a hotel room. Hopefully I will find something though. Perhaps a couple of book reviews, as I have read some really amazing books recently.

I hope you all have a seriously awesome week.

Leave a Reply

Your email address will not be published. Required fields are marked *