Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/lib/msf/base/serializer/readable_text.rb
21546 views
1
# -*- coding: binary -*-
2
3
4
module Msf
5
module Serializer
6
7
# This class formats information in a plain-text format that
8
# is meant to be displayed on a console or some other non-GUI
9
# medium.
10
class ReadableText
11
12
#Default number of characters to wrap at.
13
DefaultColumnWrap = 70
14
#Default number of characters to indent.
15
DefaultIndent = 2
16
17
# Returns a formatted string that contains information about
18
# the supplied module instance.
19
#
20
# @param mod [Msf::Module] the module to dump information for.
21
# @param indent [String] the indentation to use.
22
# @return [String] formatted text output of the dump.
23
def self.dump_module(mod, indent = " ")
24
case mod.type
25
when Msf::MODULE_PAYLOAD
26
return dump_payload_module(mod, indent)
27
when Msf::MODULE_NOP
28
return dump_basic_module(mod, indent)
29
when Msf::MODULE_ENCODER
30
return dump_basic_module(mod, indent)
31
when Msf::MODULE_EXPLOIT
32
return dump_exploit_module(mod, indent)
33
when Msf::MODULE_AUX
34
return dump_auxiliary_module(mod, indent)
35
when Msf::MODULE_POST
36
return dump_post_module(mod, indent)
37
when Msf::MODULE_EVASION
38
return dump_evasion_module(mod, indent)
39
else
40
return dump_generic_module(mod, indent)
41
end
42
end
43
44
# Dumps an exploit's targets.
45
#
46
# @param mod [Msf::Exploit] the exploit module to dump targets
47
# for.
48
# @param indent [String] the indentation to use (only the length
49
# matters).
50
# @param h [String] the string to display as the table heading.
51
# @return [String] the string form of the table.
52
def self.dump_exploit_targets(mod, indent = '', h = nil)
53
tbl = Rex::Text::Table.new(
54
'Indent' => indent.length,
55
'Header' => h,
56
'Columns' =>
57
[
58
'IsTarget',
59
'Id',
60
'Name',
61
],
62
'SortIndex' => 1,
63
'ColProps' => {
64
'IsTarget' => {
65
'Stylers' => [Msf::Ui::Console::TablePrint::RowIndicatorStyler.new],
66
'ColumnStylers' => [Msf::Ui::Console::TablePrint::OmitColumnHeader.new],
67
'Width' => 2
68
}
69
}
70
)
71
72
mod.targets.each_with_index do |target, idx|
73
is_target = mod.target == target
74
75
tbl << [is_target, idx.to_s, target.name || 'All' ]
76
end
77
78
tbl.to_s + "\n"
79
end
80
81
def self.dump_evasion_targets(mod, indent = '', h = nil)
82
tbl = Rex::Text::Table.new(
83
'Indent' => indent.length,
84
'Header' => h,
85
'Columns' =>
86
[
87
'IsTarget',
88
'Id',
89
'Name',
90
],
91
'SortIndex' => 1,
92
'ColProps' => {
93
'IsTarget' => {
94
'Stylers' => [Msf::Ui::Console::TablePrint::RowIndicatorStyler.new],
95
'ColumnStylers' => [Msf::Ui::Console::TablePrint::OmitColumnHeader.new],
96
'Width' => 2
97
}
98
}
99
)
100
101
mod.targets.each_with_index do |target, idx|
102
is_target = mod.target == target
103
104
tbl << [is_target, idx.to_s, target.name || 'All' ]
105
end
106
107
tbl.to_s + "\n"
108
end
109
110
# Dumps the exploit's selected target
111
#
112
# @param mod [Msf::Exploit] the exploit module.
113
# @param indent [String] the indentation to use (only the length
114
# matters)
115
# @param h [String] the string to display as the table heading.
116
# @return [String] the string form of the table.
117
def self.dump_exploit_target(mod, indent = '', h = nil)
118
tbl = Rex::Text::Table.new(
119
'Indent' => indent.length,
120
'Header' => h,
121
'Columns' =>
122
[
123
'Id',
124
'Name',
125
])
126
127
tbl << [ mod.target_index, mod.target.name || 'All' ]
128
129
tbl.to_s + "\n"
130
end
131
132
# Dumps the evasion module's selected target
133
#
134
# @param mod [Msf::Evasion] The evasion module.
135
# @param indent [String] The indentation to use (only the length matters)
136
# @param h [String] The string to display as the table heading.
137
# @return [String] The strong form of the table.
138
def self.dump_evasion_target(mod, indent = '', h = nil)
139
tbl = Rex::Text::Table.new(
140
'Indent' => indent.length,
141
'Header' => h,
142
'Columns' =>
143
[
144
'Id',
145
'Name',
146
])
147
148
tbl << [ mod.target_index, mod.target.name || 'All' ]
149
150
tbl.to_s + "\n"
151
end
152
153
# Dumps a module's actions
154
#
155
# @param mod [Msf::Module] the module.
156
# @param indent [String] the indentation to use (only the length
157
# matters)
158
# @param h [String] the string to display as the table heading.
159
# @return [String] the string form of the table.
160
def self.dump_module_actions(mod, indent = '', h = nil)
161
tbl = Rex::Text::Table.new(
162
'Indent' => indent.length,
163
'Header' => h,
164
'Columns' =>
165
[
166
'ActionEnabled',
167
'Name',
168
'Description'
169
],
170
'SortIndex' => 1,
171
'ColProps' => {
172
'ActionEnabled' => {
173
'Stylers' => [Msf::Ui::Console::TablePrint::RowIndicatorStyler.new],
174
'ColumnStylers' => [Msf::Ui::Console::TablePrint::OmitColumnHeader.new],
175
'Width' => 2
176
}
177
}
178
)
179
180
mod.actions.each_with_index { |target, idx|
181
action_enabled = mod.action == target
182
183
tbl << [ action_enabled, target.name || 'All' , target.description || '' ]
184
}
185
186
tbl.to_s + "\n"
187
end
188
189
# Dumps the module's selected action
190
#
191
# @param mod [Msf::Module] the module.
192
# @param indent [String] the indentation to use (only the length
193
# matters)
194
# @param h [String] the string to display as the table heading.
195
# @return [String] the string form of the table.
196
def self.dump_module_action(mod, indent = '', h = nil)
197
tbl = Rex::Text::Table.new(
198
'Indent' => indent.length,
199
'Header' => h,
200
'Columns' =>
201
[
202
'Name',
203
'Description',
204
])
205
206
tbl << [ mod.action.name || 'All', mod.action.description || '' ]
207
208
tbl.to_s + "\n"
209
end
210
211
# Dumps the table of payloads that are compatible with the supplied
212
# exploit.
213
#
214
# @param exploit [Msf::Exploit] the exploit module.
215
# @param indent [String] the indentation to use (only the length
216
# matters)
217
# @param h [String] the string to display as the table heading.
218
# @return [String] the string form of the table.
219
def self.dump_compatible_payloads(exploit, indent = '', h = nil)
220
tbl = Rex::Text::Table.new(
221
'Indent' => indent.length,
222
'Header' => h,
223
'Columns' =>
224
[
225
'Name',
226
'Description',
227
])
228
229
exploit.compatible_payloads.each { |entry|
230
tbl << [ entry[0], entry[1].new.description ]
231
}
232
233
tbl.to_s + "\n"
234
end
235
236
def self.dump_traits(mod, indent=' ')
237
output = ''
238
239
unless mod.side_effects.empty?
240
output << "Module side effects:\n"
241
mod.side_effects.each { |side_effect|
242
output << indent + side_effect + "\n"
243
}
244
output << "\n"
245
end
246
247
unless mod.stability.empty?
248
output << "Module stability:\n"
249
mod.stability.each { |stability|
250
output << indent + stability + "\n"
251
}
252
output << "\n"
253
end
254
255
unless mod.reliability.empty?
256
output << "Module reliability:\n"
257
mod.reliability.each { |reliability|
258
output << indent + reliability + "\n"
259
}
260
output << "\n"
261
end
262
263
output
264
end
265
266
# Dumps information about an exploit module.
267
#
268
# @param mod [Msf::Exploit] the exploit module.
269
# @param indent [String] the indentation to use.
270
# @return [String] the string form of the information.
271
def self.dump_exploit_module(mod, indent = '')
272
output = "\n"
273
output << " Name: #{mod.name}\n"
274
output << " Module: #{mod.fullname}\n"
275
output << " Platform: #{mod.platform_to_s}\n"
276
output << " Arch: #{mod.arch_to_s}\n"
277
output << " Privileged: " + (mod.privileged? ? "Yes" : "No") + "\n"
278
output << " License: #{mod.license}\n"
279
output << " Rank: #{mod.rank_to_s.capitalize}\n"
280
output << " Disclosed: #{mod.disclosure_date}\n" if mod.disclosure_date
281
output << "\n"
282
283
# Authors
284
output << "Provided by:\n"
285
mod.each_author { |author|
286
output << indent + author.to_s + "\n"
287
}
288
output << "\n"
289
290
output << dump_traits(mod)
291
292
# Targets
293
output << "Available targets:\n"
294
output << dump_exploit_targets(mod, indent)
295
296
# Check
297
output << "Check supported:\n"
298
output << "#{indent}#{mod.has_check? ? 'Yes' : 'No'}\n\n"
299
300
# Options
301
if (mod.options.has_options?)
302
output << "Basic options:\n"
303
output << dump_options(mod, indent)
304
output << "\n"
305
end
306
307
# Payload information
308
if (mod.payload_info.length)
309
output << "Payload information:\n"
310
if (mod.payload_space)
311
output << indent + "Space: " + mod.payload_space.to_s + "\n"
312
end
313
if (mod.payload_badchars)
314
output << indent + "Avoid: " + mod.payload_badchars.length.to_s + " characters\n"
315
end
316
output << "\n"
317
end
318
319
# Description
320
output << dump_description(mod, indent)
321
322
# References
323
output << dump_references(mod, indent)
324
325
# Notes
326
output << dump_notes(mod, indent)
327
328
return output
329
330
end
331
332
# Dumps information about an auxiliary module.
333
#
334
# @param mod [Msf::Auxiliary] the auxiliary module.
335
# @param indent [String] the indentation to use.
336
# @return [String] the string form of the information.
337
def self.dump_auxiliary_module(mod, indent = '')
338
output = "\n"
339
output << " Name: #{mod.name}\n"
340
output << " Module: #{mod.fullname}\n"
341
output << " License: #{mod.license}\n"
342
output << " Rank: #{mod.rank_to_s.capitalize}\n"
343
output << " Disclosed: #{mod.disclosure_date}\n" if mod.disclosure_date
344
output << "\n"
345
346
# Authors
347
output << "Provided by:\n"
348
mod.each_author { |author|
349
output << indent + author.to_s + "\n"
350
}
351
output << "\n"
352
353
output << dump_traits(mod)
354
355
# Actions
356
if mod.actions.any?
357
output << "Available actions:\n"
358
output << dump_module_actions(mod)
359
end
360
361
# Check
362
has_check = mod.has_check?
363
output << "Check supported:\n"
364
output << "#{indent}#{has_check ? 'Yes' : 'No'}\n\n"
365
366
# Options
367
if (mod.options.has_options?)
368
output << "Basic options:\n"
369
output << dump_options(mod, indent)
370
output << "\n"
371
end
372
373
# Description
374
output << dump_description(mod, indent)
375
376
# References
377
output << dump_references(mod, indent)
378
379
# Notes
380
output << dump_notes(mod, indent)
381
382
return output
383
end
384
385
# Dumps information about a post module.
386
#
387
# @param mod [Msf::Post] the post module.
388
# @param indent [String] the indentation to use.
389
# @return [String] the string form of the information.
390
def self.dump_post_module(mod, indent = '')
391
output = "\n"
392
output << " Name: #{mod.name}\n"
393
output << " Module: #{mod.fullname}\n"
394
output << " Platform: #{mod.platform_to_s}\n"
395
output << " Arch: #{mod.arch_to_s}\n"
396
output << " Rank: #{mod.rank_to_s.capitalize}\n"
397
output << " Disclosed: #{mod.disclosure_date}\n" if mod.disclosure_date
398
output << "\n"
399
400
# Authors
401
output << "Provided by:\n"
402
mod.each_author.each do |author|
403
output << indent + author.to_s + "\n"
404
end
405
output << "\n"
406
407
output << dump_traits(mod)
408
409
# Compatible session types
410
if mod.session_types
411
output << "Compatible session types:\n"
412
mod.session_types.sort.each do |type|
413
output << indent + type.capitalize + "\n"
414
end
415
output << "\n"
416
end
417
418
# Actions
419
if mod.actions.any?
420
output << "Available actions:\n"
421
output << dump_module_actions(mod)
422
end
423
424
# Options
425
if (mod.options.has_options?)
426
output << "Basic options:\n"
427
output << dump_options(mod, indent)
428
output << "\n"
429
end
430
431
# Description
432
output << dump_description(mod, indent)
433
434
# References
435
output << dump_references(mod, indent)
436
437
# Notes
438
output << dump_notes(mod, indent)
439
440
return output
441
end
442
443
# Dumps information about an evasion module.
444
#
445
# @param mod [Msf::Evasion] The evasion module instance.
446
# @param indent [String] The indentation to use.
447
# @return [String] The string form of the information
448
def self.dump_evasion_module(mod, indent = '')
449
output = "\n"
450
output << " Name: #{mod.name}\n"
451
output << " Module: #{mod.fullname}\n"
452
output << " Platform: #{mod.platform_to_s}\n"
453
output << " Arch: #{mod.arch_to_s}\n"
454
output << " Privileged: " + (mod.privileged? ? "Yes" : "No") + "\n"
455
output << " License: #{mod.license}\n"
456
output << " Rank: #{mod.rank_to_s.capitalize}\n"
457
output << " Disclosed: #{mod.disclosure_date}\n" if mod.disclosure_date
458
output << "\n"
459
460
# Authors
461
output << "Provided by:\n"
462
mod.each_author { |author|
463
output << indent + author.to_s + "\n"
464
}
465
output << "\n"
466
467
# Check
468
output << "Check supported:\n"
469
output << "#{indent}#{mod.has_check? ? 'Yes' : 'No'}\n\n"
470
471
# Options
472
if (mod.options.has_options?)
473
output << "Basic options:\n"
474
output << dump_options(mod, indent)
475
output << "\n"
476
end
477
478
# Description
479
output << dump_description(mod, indent)
480
481
# References
482
output << dump_references(mod, indent)
483
484
return output
485
end
486
487
# Dumps information about a payload module.
488
#
489
# @param mod [Msf::Payload] the payload module.
490
# @param indent [String] the indentation to use.
491
# @return [String] the string form of the information.
492
def self.dump_payload_module(mod, indent = '')
493
# General
494
output = "\n"
495
output << " Name: #{mod.name}\n"
496
output << " Module: #{mod.fullname}\n"
497
output << " Platform: #{mod.platform_to_s}\n"
498
output << " Arch: #{mod.arch_to_s}\n"
499
output << "Needs Admin: " + (mod.privileged? ? "Yes" : "No") + "\n"
500
output << " Total size: #{mod.size}\n"
501
output << " Rank: #{mod.rank_to_s.capitalize}\n"
502
output << "\n"
503
504
# Authors
505
output << "Provided by:\n"
506
mod.each_author { |author|
507
output << indent + author.to_s + "\n"
508
}
509
output << "\n"
510
511
# Options
512
if (mod.options.has_options?)
513
output << "Basic options:\n"
514
output << dump_options(mod)
515
output << "\n"
516
end
517
518
# Description
519
output << dump_description(mod, indent)
520
output << "\n"
521
522
return output
523
end
524
525
# Dumps information about a module, just the basics.
526
#
527
# @param mod [Msf::Module] the module.
528
# @param indent [String] the indentation to use.
529
# @return [String] the string form of the information.
530
def self.dump_basic_module(mod, indent = '')
531
# General
532
output = "\n"
533
output << " Name: #{mod.name}\n"
534
output << " Module: #{mod.fullname}\n"
535
output << " Platform: #{mod.platform_to_s}\n"
536
output << " Arch: #{mod.arch_to_s}\n"
537
output << " Rank: #{mod.rank_to_s.capitalize}\n"
538
output << "\n"
539
540
# Authors
541
output << "Provided by:\n"
542
mod.each_author { |author|
543
output << indent + author.to_s + "\n"
544
}
545
output << "\n"
546
547
output << dump_traits(mod)
548
549
# Description
550
output << dump_description(mod, indent)
551
552
output << dump_references(mod, indent)
553
554
output << "\n"
555
556
return output
557
558
end
559
560
#No current use
561
def self.dump_generic_module(mod, indent = '')
562
end
563
564
# Dumps the list of options associated with the
565
# supplied module.
566
#
567
# @param mod [Msf::Module] the module.
568
# @param indent [String] the indentation to use.
569
# @param missing [Boolean] dump only empty required options.
570
# @return [String] the string form of the information.
571
def self.dump_options(mod, indent = '', missing = false, advanced: false, evasion: false)
572
filtered_options = mod.options.select { |_name, opt| opt.advanced? == advanced && opt.evasion? == evasion }
573
574
option_groups = mod.options.groups.values.select { |group| group.option_names.any? { |name| filtered_options.keys.include?(name) } }
575
options_by_group = option_groups.map do |group|
576
[group, group.option_names.map { |name| filtered_options[name] }.compact]
577
end.to_h
578
grouped_option_names = option_groups.flat_map(&:option_names)
579
remaining_options = filtered_options.reject { |_name, option| grouped_option_names.include?(option.name) }
580
options_grouped_by_conditions = remaining_options.values.group_by(&:conditions)
581
582
option_tables = []
583
584
sort_by_empty_then_lexicographical = proc do |(conditions_a, _options_a), (conditions_b, _options_b)|
585
next -1 if conditions_a.empty?
586
next 1 if conditions_b.empty?
587
conditions_a.to_s <=> conditions_b.to_s
588
end
589
590
options_grouped_by_conditions.sort(&sort_by_empty_then_lexicographical).each do |conditions, options|
591
tbl = options_table(missing, mod, options, indent)
592
593
next if tbl.rows.empty?
594
595
if conditions.any?
596
option_tables << "#{indent}When #{Msf::OptCondition.format_conditions(mod, options.first)}:\n\n#{tbl}"
597
else
598
option_tables << tbl.to_s
599
end
600
end
601
602
options_by_group.each do |group, options|
603
tbl = options_table(missing, mod, options, indent)
604
option_tables << "#{indent}#{group.description}:\n\n#{tbl}"
605
end
606
607
result = option_tables.join("\n\n")
608
result
609
end
610
611
# Creates the table for the given module options
612
#
613
# @param missing [Boolean] dump only empty required options.
614
# @param mod [Msf::Module] the module.
615
# @param options [Array<Msf::OptBase>] The options to be added to the table
616
# @param indent [String] the indentation to use.
617
#
618
# @return [String] the string form of the table.
619
def self.options_table(missing, mod, options, indent)
620
tbl = Rex::Text::Table.new(
621
'Indent' => indent.length,
622
'Columns' =>
623
[
624
'Name',
625
'Current Setting',
626
'Required',
627
'Description'
628
]
629
)
630
options.sort_by(&:name).each do |opt|
631
name = opt.name
632
if mod.datastore.is_a?(Msf::DataStore)
633
val = mod.datastore[name]
634
else
635
val = mod.datastore[name].nil? ? opt.default : mod.datastore[name]
636
end
637
next if (missing && opt.valid?(val))
638
639
desc = opt.desc.dup
640
641
# Hint at RPORT proto by regexing mixins
642
if name == 'RPORT' && opt.kind_of?(Msf::OptPort)
643
mod.class.included_modules.each do |m|
644
case m.name
645
when /tcp/i, /HttpClient$/
646
desc << ' (TCP)'
647
break
648
when /udp/i
649
desc << ' (UDP)'
650
break
651
end
652
end
653
end
654
655
tbl << [name, opt.display_value(val), opt.required? ? "yes" : "no", desc]
656
end
657
tbl
658
end
659
660
# Dumps the advanced options associated with the supplied module.
661
#
662
# @param mod [Msf::Module] the module.
663
# @param indent [String] the indentation to use.
664
# @return [String] the string form of the information.
665
def self.dump_advanced_options(mod, indent = '')
666
return dump_options(mod, indent, advanced: true)
667
end
668
669
# Dumps the evasion options associated with the supplied module.
670
#
671
# @param mod [Msf::Module] the module.
672
# @param indent [String] the indentation to use.
673
# @return [String] the string form of the information.
674
def self.dump_evasion_options(mod, indent = '')
675
return dump_options(mod, indent, evasion: true)
676
end
677
678
# Dumps the references associated with the supplied module.
679
#
680
# @param mod [Msf::Module] the module.
681
# @param indent [String] the indentation to use.
682
# @return [String] the string form of the information.
683
def self.dump_references(mod, indent = '')
684
output = ''
685
686
if (mod.respond_to?(:references) && mod.references && mod.references.length > 0)
687
output << "References:\n"
688
689
mod.references.each do |ref|
690
case ref.ctx_id
691
when 'LOGO', 'SOUNDTRACK'
692
output << indent + ref.to_s + "\n"
693
Rex::Compat.open_browser(ref.ctx_val) if Rex::Compat.getenv('FUEL_THE_HYPE_MACHINE')
694
else
695
output << indent + ref.to_s + "\n"
696
end
697
end
698
699
output << "\n"
700
end
701
702
output
703
end
704
705
# Dumps the notes associated with the supplied module.
706
#
707
# @param mod [Msf::Module] the module.
708
# @param indent [String] the indentation to use.
709
# @return [String] the string form of the information.
710
def self.dump_notes(mod, indent = '')
711
output = ''
712
713
mod.notes.each do |name, val|
714
next unless val.present?
715
716
case name
717
when 'AKA'
718
output << "Also known as:\n"
719
val.each { |aka| output << "#{indent}#{aka}\n" }
720
when 'NOCVE'
721
output << "CVE not available for the following reason:\n" \
722
"#{indent}#{val}\n"
723
when 'RelatedModules'
724
output << "Related modules:\n"
725
val.each { |related| output << "#{indent}#{related}\n" }
726
when 'Stability', 'SideEffects', 'Reliability'
727
# Handled by dump_traits
728
next
729
else
730
output << "#{name}:\n"
731
732
case val
733
when Array
734
val.each { |v| output << "#{indent}#{v}\n" }
735
when Hash
736
val.each { |k, v| output << "#{indent}#{k}: #{v}\n" }
737
else
738
# Display the raw note
739
output << "#{indent}#{val}\n"
740
end
741
end
742
743
output << "\n"
744
end
745
746
output
747
end
748
749
# Dumps the contents of a datastore.
750
#
751
# @param name [String] displayed as the table header.
752
# @param ds [Msf::DataStore] the DataStore to dump.
753
# @param indent [Integer] the indentation size.
754
# @param col [Integer] the column width.
755
# @return [String] the formatted DataStore contents.
756
def self.dump_datastore(name, ds, indent = DefaultIndent, col = DefaultColumnWrap)
757
tbl = Rex::Text::Table.new(
758
'Indent' => indent,
759
'Header' => name,
760
'Columns' =>
761
[
762
'Name',
763
'Value'
764
])
765
766
ds.keys.sort.each { |k|
767
tbl << [ k, (ds[k] != nil) ? ds[k].to_s : '' ]
768
}
769
770
return ds.length > 0 ? tbl.to_s : "#{tbl.header_to_s}No entries in data store.\n"
771
end
772
773
# Dumps the list of sessions.
774
#
775
# @param framework [Msf::Framework] the framework to dump.
776
# @param opts [Hash] the options to dump with.
777
# @option opts :verbose [Boolean] gives more information if set to
778
# true.
779
# @option opts :indent [Integer] set the indentation amount.
780
# @return [String] the formatted list of sessions.
781
def self.dump_sessions(framework, opts={})
782
output = ""
783
verbose = opts[:verbose] || false
784
sessions = opts[:sessions] || framework.sessions
785
show_active = opts[:show_active] || false
786
show_inactive = opts[:show_inactive] || false
787
# if show_active and show_inactive are false the caller didn't
788
# specify either flag; default to displaying active sessions
789
show_active = true if !(show_active || show_inactive)
790
show_extended = opts[:show_extended] || false
791
indent = opts[:indent] || DefaultIndent
792
793
return dump_sessions_verbose(framework, opts) if verbose
794
795
if show_active
796
columns = []
797
columns << 'Id'
798
columns << 'Name'
799
columns << 'Type'
800
columns << 'Checkin?' if show_extended
801
columns << 'Enc?' if show_extended
802
columns << 'Local URI' if show_extended
803
columns << 'Information'
804
columns << 'Connection'
805
806
tbl = Rex::Text::Table.new(
807
'Header' => "Active sessions",
808
'Columns' => columns,
809
'Indent' => indent)
810
811
sessions.each do |session_id, session|
812
row = create_msf_session_row(session, show_extended)
813
tbl << row
814
end
815
816
output << (tbl.rows.count > 0 ? tbl.to_s : "#{tbl.header_to_s}No active sessions.\n")
817
end
818
819
if show_inactive
820
output << "\n" if show_active
821
822
columns = []
823
columns << 'Closed'
824
columns << 'Opened'
825
columns << 'Reason Closed'
826
columns << 'Type'
827
columns << 'Address'
828
829
tbl = Rex::Text::Table.new(
830
'Header' => "Inactive sessions",
831
'Columns' => columns,
832
'Indent' => indent,
833
'SortIndex' => 1)
834
835
if framework.db.active
836
framework.db.sessions.each do |session|
837
unless session.closed_at.nil?
838
row = create_mdm_session_row(session, show_extended)
839
tbl << row
840
end
841
end
842
end
843
844
output << (tbl.rows.count > 0 ? tbl.to_s : "#{tbl.header_to_s}No inactive sessions.\n")
845
end
846
847
# return formatted listing of sessions
848
output
849
end
850
851
# Creates a table row that represents the specified session.
852
#
853
# @param session [Msf::Session] session used to create a table row.
854
# @param show_extended [Boolean] Indicates if extended information will be included in the row.
855
# @return [Array] table row of session data.
856
def self.create_msf_session_row(session, show_extended)
857
row = []
858
row << session.sid.to_s
859
row << session.sname.to_s
860
row << session.type.to_s
861
if session.respond_to?(:session_type)
862
row[-1] << " #{session.session_type}"
863
elsif session.respond_to?(:platform)
864
row[-1] << " #{session.platform}"
865
end
866
867
if show_extended
868
if session.respond_to?(:last_checkin) && session.last_checkin
869
row << "#{(Time.now.to_i - session.last_checkin.to_i)}s ago"
870
else
871
row << '?'
872
end
873
874
if session.respond_to?(:tlv_enc_key) && session.tlv_enc_key && session.tlv_enc_key[:key]
875
row << 'Y'
876
else
877
row << 'N'
878
end
879
880
if session.exploit_datastore && session.exploit_datastore.has_key?('LURI') && !session.exploit_datastore['LURI'].empty?
881
row << "(#{session.exploit_datastore['LURI']})"
882
else
883
row << '?'
884
end
885
end
886
887
sinfo = session.info.to_s
888
sinfo = sinfo.gsub(/[\r\n\t]+/, ' ')
889
# Arbitrarily cut info at 80 columns
890
if sinfo.length > 80
891
sinfo = "#{sinfo[0,77]}..."
892
end
893
row << sinfo
894
895
row << "#{session.tunnel_to_s} (#{session.session_host})"
896
897
# return complete row
898
row
899
end
900
901
# Creates a table row that represents the specified session.
902
#
903
# @param session [Mdm::Session] session used to create a table row.
904
# @param show_extended [Boolean] Indicates if extended information will be included in the row.
905
# @return [Array] table row of session data.
906
def self.create_mdm_session_row(session, show_extended)
907
row = []
908
row << session.closed_at.to_s
909
row << session.opened_at.to_s
910
row << session.close_reason
911
row << session.stype
912
if session.respond_to?(:platform) && !session.platform.nil?
913
row[-1] << " #{session.platform}"
914
end
915
row << (!session.host.nil? ? session.host.address : nil)
916
917
# return complete row
918
row
919
end
920
921
# Dumps the list of active sessions in verbose mode
922
#
923
# @param framework [Msf::Framework] the framework to dump.
924
# @param opts [Hash] the options to dump with.
925
# @return [String] the formatted list of sessions.
926
def self.dump_sessions_verbose(framework, opts={})
927
out = "Active sessions\n" +
928
"===============\n\n"
929
930
if framework.sessions.length == 0
931
out << "No active sessions.\n"
932
return out
933
end
934
935
sessions = opts[:sessions] || framework.sessions
936
937
sessions.each do |session_id, session|
938
sess_info = session.info.to_s
939
sess_id = session.sid.to_s
940
sess_name = session.sname.to_s
941
sess_tunnel = session.tunnel_to_s + " (#{session.session_host})"
942
sess_via = session.via_exploit.to_s
943
sess_type = session.type.to_s
944
sess_uuid = session.payload_uuid.to_s
945
sess_luri = session.exploit_datastore['LURI'] || "" if session.exploit_datastore
946
sess_enc = 'No'
947
if session.respond_to?(:tlv_enc_key) && session.tlv_enc_key && session.tlv_enc_key[:key]
948
sess_enc = "Yes (AES-#{session.tlv_enc_key[:key].length * 8}-CBC)"
949
end
950
951
sess_checkin = "<none>"
952
sess_registration = "No"
953
954
if session.respond_to?(:platform) && session.platform
955
sess_type << " #{session.platform}"
956
end
957
958
if session.respond_to?(:last_checkin) && session.last_checkin
959
sess_checkin = "#{(Time.now.to_i - session.last_checkin.to_i)}s ago @ #{session.last_checkin.to_s}"
960
end
961
962
if !session.payload_uuid.nil? && session.payload_uuid.registered
963
sess_registration = "Yes"
964
if session.payload_uuid.name
965
sess_registration << " - Name=\"#{session.payload_uuid.name}\""
966
end
967
end
968
969
out << " Session ID: #{sess_id}\n"
970
out << " Name: #{sess_name}\n"
971
out << " Type: #{sess_type}\n"
972
out << " Info: #{sess_info}\n"
973
out << " Tunnel: #{sess_tunnel}\n"
974
out << " Via: #{sess_via}\n"
975
out << " Encrypted: #{sess_enc}\n"
976
out << " UUID: #{sess_uuid}\n"
977
out << " CheckIn: #{sess_checkin}\n"
978
out << " Registered: #{sess_registration}\n"
979
unless (sess_luri || '').empty?
980
out << " LURI: #{sess_luri}\n"
981
end
982
983
out << "\n"
984
end
985
986
out << "\n"
987
return out
988
end
989
990
# Dumps the list of running jobs.
991
#
992
# @param framework [Msf::Framework] the framework.
993
# @param verbose [Boolean] if true, also prints the payload, LPORT, URIPATH
994
# and start time, if they exist, for each job.
995
# @param indent [Integer] the indentation amount.
996
# @param col [Integer] the column wrap width.
997
# @return [String] the formatted list of running jobs.
998
def self.dump_jobs(framework, verbose = false, indent = DefaultIndent, col = DefaultColumnWrap)
999
columns = [ 'Id', 'Name', "Payload", "Payload opts"]
1000
1001
if (verbose)
1002
columns += [ "URIPATH", "Start Time", "Handler opts", "Persist" ]
1003
end
1004
1005
tbl = Rex::Text::Table.new(
1006
'Indent' => indent,
1007
'Header' => "Jobs",
1008
'Columns' => columns
1009
)
1010
1011
# Get the persistent job info.
1012
if verbose
1013
begin
1014
persist_list = JSON.parse(File.read(Msf::Config.persist_file))
1015
rescue Errno::ENOENT, JSON::ParserError
1016
persist_list = []
1017
end
1018
end
1019
1020
# jobs are stored as a hash with the keys being a numeric String job_id.
1021
framework.jobs.keys.sort_by(&:to_i).each do |job_id|
1022
# Job context is stored as an Array with the 0th element being
1023
# the running module. If that module is an exploit, ctx will also
1024
# contain its payload.
1025
exploit_mod, _payload_mod = framework.jobs[job_id].ctx
1026
row = []
1027
row[0] = job_id
1028
row[1] = framework.jobs[job_id].name
1029
1030
pinst = exploit_mod.respond_to?(:payload_instance) ? exploit_mod.payload_instance : nil
1031
payload_uri = ''
1032
1033
if pinst.nil?
1034
row[2] = ""
1035
row[3] = ""
1036
else
1037
row[2] = pinst.refname
1038
row[3] = ""
1039
if pinst.respond_to?(:payload_uri)
1040
payload_uri = pinst.payload_uri.strip
1041
row[3] << payload_uri
1042
end
1043
if pinst.respond_to?(:luri)
1044
row[3] << pinst.luri
1045
end
1046
if pinst.respond_to?(:comm_string)
1047
via = pinst.comm_string
1048
if via
1049
row[3] << " #{via}"
1050
end
1051
end
1052
end
1053
1054
if verbose
1055
uripath = exploit_mod.get_resource if exploit_mod.respond_to?(:get_resource)
1056
uripath ||= exploit_mod.datastore['URIPATH']
1057
row[4] = uripath
1058
row[5] = framework.jobs[job_id].start_time
1059
row[6] = ''
1060
row[7] = 'false'
1061
1062
if pinst.respond_to?(:listener_uri)
1063
listener_uri = pinst.listener_uri.strip
1064
row[6] = listener_uri unless listener_uri == payload_uri
1065
end
1066
1067
persist_list.each do |e|
1068
handler_ctx = framework.jobs[job_id.to_s].ctx[1]
1069
if handler_ctx && handler_ctx.respond_to?(:datastore)
1070
row[7] = 'true' if e['mod_options']['Options'] == handler_ctx.datastore.to_h
1071
end
1072
end
1073
1074
end
1075
tbl << row
1076
end
1077
1078
return framework.jobs.keys.length > 0 ? tbl.to_s : "#{tbl.header_to_s}No active jobs.\n"
1079
end
1080
1081
# Dumps the module description
1082
#
1083
# @param mod [Msf::Module] the module.
1084
# @param indent [String] the indentation string
1085
# @return [String] the string description
1086
def self.dump_description(mod, indent)
1087
description = mod.description
1088
1089
output = "Description:\n"
1090
output << word_wrap_description(description, indent)
1091
output << "\n\n"
1092
end
1093
1094
# @param str [String] the string to wrap.
1095
# @param indent [String] the indentation string
1096
# @return [String] the wrapped string.
1097
def self.word_wrap_description(str, indent = '')
1098
return '' if str.blank?
1099
1100
str_lines = str.strip.lines(chomp: true)
1101
# Calculate the preceding whitespace length of each line
1102
smallest_preceding_whitespace = nil
1103
str_lines[1..].to_a.each do |line|
1104
preceding_whitespace = line[/^\s+/]
1105
if preceding_whitespace && (smallest_preceding_whitespace.nil? || preceding_whitespace.length < smallest_preceding_whitespace)
1106
smallest_preceding_whitespace = preceding_whitespace.length
1107
end
1108
end
1109
1110
# Normalize any existing left-most whitespace on each line; Ignoring the first line which won't have any preceding whitespace
1111
result = str_lines.map.with_index do |line, index|
1112
next if line.blank?
1113
1114
"#{indent}#{index == 0 || smallest_preceding_whitespace.nil? ? line : line[smallest_preceding_whitespace..]}"
1115
end.join("\n")
1116
1117
result
1118
end
1119
end
1120
1121
end end
1122
1123