The bug is in how ActFax handles "data fields" when remotely sending faxes to be processed by the server.
Finding the Bug
I started looking at ActFax, looking for places I could input data or send data to the server. I came across all the other functions the server had, and wondered why a "fax" server would have these features:
A LPD server? FTP server? RAW server? After reading the help menu, I learned that these were ways that other computers could send unprocessed faxes to the server.
ActFax allows you to compose documents that you want to fax and then transmit them to the server electronically so it can convert them to faxes and send them out. The way it knows who/where/why to send the fax is by the use of data fields, mentioned earlier. Consulting the help menu we get an idea of how this works:
Basically, we start a data field by using "@F" and then the code we want, then our data and the string is terminated again with another "@". That trailing @ will come in handy later....
Here is the help topic that showed me every possible data field the server would accept. There are about 50 of them:
Fuzz Time
With over 50 different fields, a basic fuzzer was in order. However, first I had to decide what protocol to use for my fuzzer. ActFax allows you to use LPD, RAW and FTP to transfer faxes. For the sake of simplicity and after a bit of research, lets go with RAW :) I assigned a arbitrary port to the RAW server to enable it, and I was off. Here is the basic fuzzer http://pastebin.com/sM47zRhU
That fuzzer template was stolen from here: http://www.redteamsecure.com/labs/post/18/build-your-own-ftp-fuzzer
Crash Time
Running the fuzzer yielded interesting results. It looked like there was a crash around 600 bytes or so on the @F506 data field:
Looking at Immunity at the time of the crash, we see:
However, after further inspection, we didn't control SEH or EIP, so exploitation was likely not possible with this buffer setup. As these things typically go, we need to start messing with the buffer a little bit. I decided to double the buffer size and send a single command to the @F506 data field. This one looked a little more promising:
This looks favorable, EIP overwritten and two registers pointing to our buffer.
Next step is to figure out which bytes overwrite EIP and our favorite tool to do this is metasploit's pattern_create tool. Since I usually start with a basic python script, I prefer to just generate this with !mona and stuff it into a variable into my python script:
Adding the pattern to our basic python script results in the following http://pastebin.com/9jssLzwQ
When we run our new script we see EIP overwritten with our cyclical pattern:
As you can see from above, now we have our offset and are well on our way to pwnag3...... or so we think :)
Tight Spaces
Our first problem.... when we dump the contents of ESP, we see our cyclic pattern but then there are a bunch of zeros and then what looks like the beginning of our cyclic pattern:
Based on this we have an interesting situation. ESP points to our buffer but then it's abruptly cutoff. So, it appears we only have a little bit of space to work with so lets figure out how much space this is. If you right click on ESP and "Follow in stack" and then double click at the first address on the stack (blue arrow) we can look at how much space we have. Keep in mind the offset numbers you see are in HEX.
Scrolling down the zeros stop at 22C in HEX, which is 556 in decimal. This means at the time of the crash, from where ESP points to the end of our buffer is 556 bytes.
To confirm this, we'll modify our script to test our theory http://pastebin.com/J037QmVH
This looks good - EIP is overwritten by our "B"s, our "front" variable (C's) is where EAX is pointing and our "A"s are where ESP is pointing. Just as we planned. You might wonder why I'm not talking about JMP EAX here, after all there is a lot more space there to work with. Shouldn't we just jump there?
Sumamama Bitch
As I was recreating this for the blog post, I discovered something interesting. My original research did not show the EAX register pointing to anything useful at all. This is a good lesson, look what happens to our exploit after just a few bytes are changed. As you can see below, here are the 4 byte's I was "missing":
By changing this from 556 to 560 bytes and filling up the remaining space, the register layout changed completely. Now EAX is zero'd out and ESP and ESI point to the beginning of our buffer and nothing is pointing the beginning of our buffer. This is what I originally thought was the only buffer/register layout! Oh well, it was worth the work in the end! In hindsight, I should have messed with the buffer sizes to make sure I didn't over look THIS:
3FC = 1020 in decimal.... that's plenty of space :)
Back to Business
Based on all this information, we know that without doing anything fancy, we only have about 560 (4 more bytes than originally thought) bytes for our shellcode. Normally this is plenty of space for staged shellcode, but the thing that dictates the amount of space that shellcode uses is bad characters. That is our next step, discovering bad chars. However, for the sake of brevity I'll spare you those details. I like to use the !mona bytearray and !mona compare functions to help me with this. You can reference corelan's tutorial here.
Basically all the characters from 00 - 19 were bad....as well as HEX 40 which is "@" and that will effectively terminate the data field in ActFax, which we don't want to do. A character set that satisfies this would be the alpha_mixed encoder but that will likely put us over our 560 controlled bytes.
I decided to keep going down this road for one reason. Even though we only have 560 bytes, we still do have the beginning of our buffer as well.....right after our 560 user controlled bytes. A general buffer structure is looking like this:
buff = 1024bytes + EIP + 560bytes
The 1024 bytes at the beginning would be plenty for the remainder of our shell. So we're shooting for something like this as our final buffer structure:
buff = shellcodechunk2 + filler + EIP + shellcodechunk1
We know that shellcodechunk1 has to be 560 bytes or less, or it will overflow into shellcodechunk2.
The key is to just make sure we're exact on the length of shellcodechunk1 so it can roll right into shellcodechunk2, without screwing up our shell. Lets keep our shells happy. One of the issues with this exploit is that due to so many bad chars, I could only find about 3 valid JMP ESPs and those were in ole32.dll. It's not ideal to use system files that could change with a service pack or patch, but that was my only option. Also, a ROP chain was also out of the question because of the limited characters, so bypassing DEP was also not possible. Lame, but that's the nature of this bug.
A final python script, with metasploit shellcode is here http://pastebin.com/TMH4GjkG
However, like corelanc0d3r said in his training, we need metasploit modules, not shitty python scripts. So the conversion process begins....seemed like it would be very straight forward because my original script worked perfectly.
I'll go through the issues I faced converting this exploit to a MSF module in the next blog post. The end result is this http://www.exploit-db.com/exploits/24467/