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/auxiliary/admin/mysql/mysql_enum.rb
Views: 11623
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
6
class MetasploitModule < Msf::Auxiliary
7
include Msf::Auxiliary::Report
8
include Msf::Exploit::Remote::MYSQL
9
include Msf::OptionalSession::MySQL
10
11
def initialize(info = {})
12
super(update_info(info,
13
'Name' => 'MySQL Enumeration Module',
14
'Description' => %q{
15
This module allows for simple enumeration of MySQL Database Server
16
provided proper credentials to connect remotely.
17
},
18
'Author' => [ 'Carlos Perez <carlos_perez[at]darkoperator.com>' ],
19
'License' => MSF_LICENSE,
20
'References' =>
21
[
22
[ 'URL', 'https://cisecurity.org/benchmarks.html' ]
23
]
24
))
25
26
end
27
28
def report_cred(opts)
29
service_data = {
30
address: opts[:ip],
31
port: opts[:port],
32
service_name: opts[:service_name],
33
protocol: 'tcp',
34
workspace_id: myworkspace_id
35
}
36
37
credential_data = {
38
origin_type: :service,
39
module_fullname: fullname,
40
username: opts[:user],
41
private_data: opts[:password],
42
private_type: :nonreplayable_hash,
43
jtr_format: 'mysql,mysql-sha1'
44
}.merge(service_data)
45
46
login_data = {
47
core: create_credential(credential_data),
48
status: Metasploit::Model::Login::Status::UNTRIED,
49
proof: opts[:proof]
50
}.merge(service_data)
51
52
create_credential_login(login_data)
53
end
54
55
def run
56
# If we have a session make use of it
57
if session
58
print_status("Using existing session #{session.sid}")
59
self.mysql_conn = session.client
60
self.sock = session.client.socket
61
else
62
# otherwise fallback to attempting to login
63
return unless mysql_login_datastore
64
end
65
66
print_status("Running MySQL Enumerator...")
67
print_status("Enumerating Parameters")
68
#-------------------------------------------------------
69
# getting all variables
70
vparm = {}
71
res = mysql_query("show variables") || []
72
res.each do |row|
73
# print_status(" | #{row.join(" | ")} |")
74
vparm[row[0]] = row[1]
75
end
76
77
#-------------------------------------------------------
78
# MySQL Version
79
print_status("\tMySQL Version: #{vparm["version"]}")
80
print_status("\tCompiled for the following OS: #{vparm["version_compile_os"]}")
81
print_status("\tArchitecture: #{vparm["version_compile_machine"]}")
82
print_status("\tServer Hostname: #{vparm["hostname"]}")
83
print_status("\tData Directory: #{vparm["datadir"]}")
84
85
if vparm["log"] == "OFF"
86
print_status("\tLogging of queries and logins: OFF")
87
else
88
print_status("\tLogging of queries and logins: ON")
89
print_status("\tLog Files Location: #{vparm["log_bin"]}")
90
end
91
92
print_status("\tOld Password Hashing Algorithm #{vparm["old_passwords"]}")
93
print_status("\tLoading of local files: #{vparm["local_infile"]}")
94
print_status("\tDeny logins with old Pre-4.1 Passwords: #{vparm["secure_auth"]}")
95
print_status("\tSkipping of GRANT TABLE: #{vparm["skip_grant_tables"]}") if vparm["skip_grant_tables"]
96
print_status("\tAllow Use of symlinks for Database Files: #{vparm["have_symlink"]}")
97
print_status("\tAllow Table Merge: #{vparm["have_merge_engine"]}")
98
print_status("\tRestrict DB Enumeration by Privilege: #{vparm["safe_show_database"]}") if vparm["safe_show_database"]
99
100
if vparm["have_openssl"] == "YES"
101
print_status("\tSSL Connections: Enabled")
102
print_status("\tSSL CA Certificate: #{vparm["ssl_ca"]}")
103
print_status("\tSSL Key: #{vparm["ssl_key"]}")
104
print_status("\tSSL Certificate: #{vparm["ssl_cert"]}")
105
else
106
print_status("\tSSL Connection: #{vparm["have_openssl"]}")
107
end
108
109
#-------------------------------------------------------
110
# Database selection
111
query = "use mysql"
112
mysql_query(query)
113
114
# Starting from MySQL 5.7, the 'password' column was changed to 'authentication_string'.
115
if vparm['version'][0..2].to_f > 5.6
116
password_field = 'authentication_string'
117
else
118
password_field = 'password'
119
end
120
121
# Account Enumeration
122
# Enumerate all accounts with their password hashes
123
print_status("Enumerating Accounts:")
124
query = "select user, host, #{password_field} from mysql.user"
125
res = mysql_query(query)
126
if res and res.size > 0
127
print_status("\tList of Accounts with Password Hashes:")
128
res.each do |row|
129
print_good("\t\tUser: #{row[0]} Host: #{row[1]} Password Hash: #{row[2]}")
130
report_cred(
131
ip: mysql_conn.peerhost,
132
port: mysql_conn.peerport,
133
user: row[0],
134
password: row[2],
135
service_name: 'mysql',
136
proof: row.inspect
137
)
138
end
139
end
140
# Only list accounts that can log in with SSL if SSL is enabled
141
if vparm["have_openssl"] == "YES"
142
query = %Q|select user, host, ssl_type from mysql.user where
143
(ssl_type = 'ANY') or
144
(ssl_type = 'X509') or
145
(ssl_type = 'SPECIFIED')|
146
res = mysql_query(query)
147
if res.size > 0
148
print_status("\tThe following users can login using SSL:")
149
res.each do |row|
150
print_status("\t\tUser: #{row[0]} Host: #{row[1]} SSLType: #{row[2]}")
151
end
152
end
153
end
154
query = "select user, host from mysql.user where Grant_priv = 'Y'"
155
res = mysql_query(query)
156
if res and res.size > 0
157
print_status("\tThe following users have GRANT Privilege:")
158
res.each do |row|
159
print_status("\t\tUser: #{row[0]} Host: #{row[1]}")
160
end
161
end
162
163
query = "select user, host from mysql.user where Create_user_priv = 'Y'"
164
res = mysql_query(query)
165
if res and res.size > 0
166
print_status("\tThe following users have CREATE USER Privilege:")
167
res.each do |row|
168
print_status("\t\tUser: #{row[0]} Host: #{row[1]}")
169
end
170
end
171
query = "select user, host from mysql.user where Reload_priv = 'Y'"
172
res = mysql_query(query)
173
if res and res.size > 0
174
print_status("\tThe following users have RELOAD Privilege:")
175
res.each do |row|
176
print_status("\t\tUser: #{row[0]} Host: #{row[1]}")
177
end
178
end
179
query = "select user, host from mysql.user where Shutdown_priv = 'Y'"
180
res = mysql_query(query)
181
if res and res.size > 0
182
print_status("\tThe following users have SHUTDOWN Privilege:")
183
res.each do |row|
184
print_status("\t\tUser: #{row[0]} Host: #{row[1]}")
185
end
186
end
187
query = "select user, host from mysql.user where Super_priv = 'Y'"
188
res = mysql_query(query)
189
if res and res.size > 0
190
print_status("\tThe following users have SUPER Privilege:")
191
res.each do |row|
192
print_status("\t\tUser: #{row[0]} Host: #{row[1]}")
193
end
194
end
195
query = "select user, host from mysql.user where FILE_priv = 'Y'"
196
res = mysql_query(query)
197
if res and res.size > 0
198
print_status("\tThe following users have FILE Privilege:")
199
res.each do |row|
200
print_status("\t\tUser: #{row[0]} Host: #{row[1]}")
201
end
202
end
203
query = "select user, host from mysql.user where Process_priv = 'Y'"
204
res = mysql_query(query)
205
if res and res.size > 0
206
print_status("\tThe following users have PROCESS Privilege:")
207
res.each do |row|
208
print_status("\t\tUser: #{row[0]} Host: #{row[1]}")
209
end
210
end
211
queryinmysql = %Q| select user, host
212
from mysql.user where
213
(Select_priv = 'Y') or
214
(Insert_priv = 'Y') or
215
(Update_priv = 'Y') or
216
(Delete_priv = 'Y') or
217
(Create_priv = 'Y') or
218
(Drop_priv = 'Y')|
219
res = mysql_query(queryinmysql)
220
if res and res.size > 0
221
print_status("\tThe following accounts have privileges to the mysql database:")
222
res.each do |row|
223
print_status("\t\tUser: #{row[0]} Host: #{row[1]}")
224
end
225
end
226
227
228
# Anonymous Account Check
229
queryanom = "select user, host from mysql.user where user = ''"
230
res = mysql_query(queryanom)
231
if res and res.size > 0
232
print_status("\tAnonymous Accounts are Present:")
233
res.each do |row|
234
print_status("\t\tUser: #{row[0]} Host: #{row[1]}")
235
end
236
end
237
238
# Blank Password Check
239
queryblankpass = "select user, host, #{password_field} from mysql.user where length(#{password_field}) = 0 or #{password_field} is null"
240
res = mysql_query(queryblankpass)
241
if res and res.size > 0
242
print_status("\tThe following accounts have empty passwords:")
243
res.each do |row|
244
print_status("\t\tUser: #{row[0]} Host: #{row[1]}")
245
end
246
end
247
248
# Wildcard host
249
querywildcrd = 'select user, host from mysql.user where host = "%"'
250
res = mysql_query(querywildcrd)
251
if res and res.size > 0
252
print_status("\tThe following accounts are not restricted by source:")
253
res.each do |row|
254
print_status("\t\tUser: #{row[0]} Host: #{row[1]}")
255
end
256
end
257
258
mysql_logoff unless session
259
end
260
end
261
262