CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/lib/msf/base/serializer/readable_text.rb
Views: 11784
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
options_grouped_by_conditions.sort.each do |conditions, options|
585
tbl = options_table(missing, mod, options, indent)
586
587
next if tbl.rows.empty?
588
589
if conditions.any?
590
option_tables << "#{indent}When #{Msf::OptCondition.format_conditions(mod, options.first)}:\n\n#{tbl}"
591
else
592
option_tables << tbl.to_s
593
end
594
end
595
596
options_by_group.each do |group, options|
597
tbl = options_table(missing, mod, options, indent)
598
option_tables << "#{indent}#{group.description}:\n\n#{tbl}"
599
end
600
601
result = option_tables.join("\n\n")
602
result
603
end
604
605
# Creates the table for the given module options
606
#
607
# @param missing [Boolean] dump only empty required options.
608
# @param mod [Msf::Module] the module.
609
# @param options [Array<Msf::OptBase>] The options to be added to the table
610
# @param indent [String] the indentation to use.
611
#
612
# @return [String] the string form of the table.
613
def self.options_table(missing, mod, options, indent)
614
tbl = Rex::Text::Table.new(
615
'Indent' => indent.length,
616
'Columns' =>
617
[
618
'Name',
619
'Current Setting',
620
'Required',
621
'Description'
622
]
623
)
624
options.sort_by(&:name).each do |opt|
625
name = opt.name
626
if mod.datastore.is_a?(Msf::DataStoreWithFallbacks)
627
val = mod.datastore[name]
628
else
629
val = mod.datastore[name].nil? ? opt.default : mod.datastore[name]
630
end
631
next if (missing && opt.valid?(val))
632
633
desc = opt.desc.dup
634
635
# Hint at RPORT proto by regexing mixins
636
if name == 'RPORT' && opt.kind_of?(Msf::OptPort)
637
mod.class.included_modules.each do |m|
638
case m.name
639
when /tcp/i, /HttpClient$/
640
desc << ' (TCP)'
641
break
642
when /udp/i
643
desc << ' (UDP)'
644
break
645
end
646
end
647
end
648
649
tbl << [name, opt.display_value(val), opt.required? ? "yes" : "no", desc]
650
end
651
tbl
652
end
653
654
# Dumps the advanced options associated with the supplied module.
655
#
656
# @param mod [Msf::Module] the module.
657
# @param indent [String] the indentation to use.
658
# @return [String] the string form of the information.
659
def self.dump_advanced_options(mod, indent = '')
660
return dump_options(mod, indent, advanced: true)
661
end
662
663
# Dumps the evasion options associated with the supplied module.
664
#
665
# @param mod [Msf::Module] the module.
666
# @param indent [String] the indentation to use.
667
# @return [String] the string form of the information.
668
def self.dump_evasion_options(mod, indent = '')
669
return dump_options(mod, indent, evasion: true)
670
end
671
672
# Dumps the references associated with the supplied module.
673
#
674
# @param mod [Msf::Module] the module.
675
# @param indent [String] the indentation to use.
676
# @return [String] the string form of the information.
677
def self.dump_references(mod, indent = '')
678
output = ''
679
680
if (mod.respond_to?(:references) && mod.references && mod.references.length > 0)
681
output << "References:\n"
682
683
mod.references.each do |ref|
684
case ref.ctx_id
685
when 'LOGO', 'SOUNDTRACK'
686
output << indent + ref.to_s + "\n"
687
Rex::Compat.open_browser(ref.ctx_val) if Rex::Compat.getenv('FUEL_THE_HYPE_MACHINE')
688
else
689
output << indent + ref.to_s + "\n"
690
end
691
end
692
693
output << "\n"
694
end
695
696
output
697
end
698
699
# Dumps the notes associated with the supplied module.
700
#
701
# @param mod [Msf::Module] the module.
702
# @param indent [String] the indentation to use.
703
# @return [String] the string form of the information.
704
def self.dump_notes(mod, indent = '')
705
output = ''
706
707
mod.notes.each do |name, val|
708
next unless val.present?
709
710
case name
711
when 'AKA'
712
output << "Also known as:\n"
713
val.each { |aka| output << "#{indent}#{aka}\n" }
714
when 'NOCVE'
715
output << "CVE not available for the following reason:\n" \
716
"#{indent}#{val}\n"
717
when 'RelatedModules'
718
output << "Related modules:\n"
719
val.each { |related| output << "#{indent}#{related}\n" }
720
when 'Stability', 'SideEffects', 'Reliability'
721
# Handled by dump_traits
722
next
723
else
724
output << "#{name}:\n"
725
726
case val
727
when Array
728
val.each { |v| output << "#{indent}#{v}\n" }
729
when Hash
730
val.each { |k, v| output << "#{indent}#{k}: #{v}\n" }
731
else
732
# Display the raw note
733
output << "#{indent}#{val}\n"
734
end
735
end
736
737
output << "\n"
738
end
739
740
output
741
end
742
743
# Dumps the contents of a datastore.
744
#
745
# @param name [String] displayed as the table header.
746
# @param ds [Msf::DataStore] the DataStore to dump.
747
# @param indent [Integer] the indentation size.
748
# @param col [Integer] the column width.
749
# @return [String] the formatted DataStore contents.
750
def self.dump_datastore(name, ds, indent = DefaultIndent, col = DefaultColumnWrap)
751
tbl = Rex::Text::Table.new(
752
'Indent' => indent,
753
'Header' => name,
754
'Columns' =>
755
[
756
'Name',
757
'Value'
758
])
759
760
ds.keys.sort.each { |k|
761
tbl << [ k, (ds[k] != nil) ? ds[k].to_s : '' ]
762
}
763
764
return ds.length > 0 ? tbl.to_s : "#{tbl.header_to_s}No entries in data store.\n"
765
end
766
767
# Dumps the list of sessions.
768
#
769
# @param framework [Msf::Framework] the framework to dump.
770
# @param opts [Hash] the options to dump with.
771
# @option opts :verbose [Boolean] gives more information if set to
772
# true.
773
# @option opts :indent [Integer] set the indentation amount.
774
# @return [String] the formatted list of sessions.
775
def self.dump_sessions(framework, opts={})
776
output = ""
777
verbose = opts[:verbose] || false
778
sessions = opts[:sessions] || framework.sessions
779
show_active = opts[:show_active] || false
780
show_inactive = opts[:show_inactive] || false
781
# if show_active and show_inactive are false the caller didn't
782
# specify either flag; default to displaying active sessions
783
show_active = true if !(show_active || show_inactive)
784
show_extended = opts[:show_extended] || false
785
indent = opts[:indent] || DefaultIndent
786
787
return dump_sessions_verbose(framework, opts) if verbose
788
789
if show_active
790
columns = []
791
columns << 'Id'
792
columns << 'Name'
793
columns << 'Type'
794
columns << 'Checkin?' if show_extended
795
columns << 'Enc?' if show_extended
796
columns << 'Local URI' if show_extended
797
columns << 'Information'
798
columns << 'Connection'
799
800
tbl = Rex::Text::Table.new(
801
'Header' => "Active sessions",
802
'Columns' => columns,
803
'Indent' => indent)
804
805
sessions.each do |session_id, session|
806
row = create_msf_session_row(session, show_extended)
807
tbl << row
808
end
809
810
output << (tbl.rows.count > 0 ? tbl.to_s : "#{tbl.header_to_s}No active sessions.\n")
811
end
812
813
if show_inactive
814
output << "\n" if show_active
815
816
columns = []
817
columns << 'Closed'
818
columns << 'Opened'
819
columns << 'Reason Closed'
820
columns << 'Type'
821
columns << 'Address'
822
823
tbl = Rex::Text::Table.new(
824
'Header' => "Inactive sessions",
825
'Columns' => columns,
826
'Indent' => indent,
827
'SortIndex' => 1)
828
829
if framework.db.active
830
framework.db.sessions.each do |session|
831
unless session.closed_at.nil?
832
row = create_mdm_session_row(session, show_extended)
833
tbl << row
834
end
835
end
836
end
837
838
output << (tbl.rows.count > 0 ? tbl.to_s : "#{tbl.header_to_s}No inactive sessions.\n")
839
end
840
841
# return formatted listing of sessions
842
output
843
end
844
845
# Creates a table row that represents the specified session.
846
#
847
# @param session [Msf::Session] session used to create a table row.
848
# @param show_extended [Boolean] Indicates if extended information will be included in the row.
849
# @return [Array] table row of session data.
850
def self.create_msf_session_row(session, show_extended)
851
row = []
852
row << session.sid.to_s
853
row << session.sname.to_s
854
row << session.type.to_s
855
if session.respond_to?(:session_type)
856
row[-1] << " #{session.session_type}"
857
elsif session.respond_to?(:platform)
858
row[-1] << " #{session.platform}"
859
end
860
861
if show_extended
862
if session.respond_to?(:last_checkin) && session.last_checkin
863
row << "#{(Time.now.to_i - session.last_checkin.to_i)}s ago"
864
else
865
row << '?'
866
end
867
868
if session.respond_to?(:tlv_enc_key) && session.tlv_enc_key && session.tlv_enc_key[:key]
869
row << 'Y'
870
else
871
row << 'N'
872
end
873
874
if session.exploit_datastore && session.exploit_datastore.has_key?('LURI') && !session.exploit_datastore['LURI'].empty?
875
row << "(#{session.exploit_datastore['LURI']})"
876
else
877
row << '?'
878
end
879
end
880
881
sinfo = session.info.to_s
882
sinfo = sinfo.gsub(/[\r\n\t]+/, ' ')
883
# Arbitrarily cut info at 80 columns
884
if sinfo.length > 80
885
sinfo = "#{sinfo[0,77]}..."
886
end
887
row << sinfo
888
889
row << "#{session.tunnel_to_s} (#{session.session_host})"
890
891
# return complete row
892
row
893
end
894
895
# Creates a table row that represents the specified session.
896
#
897
# @param session [Mdm::Session] session used to create a table row.
898
# @param show_extended [Boolean] Indicates if extended information will be included in the row.
899
# @return [Array] table row of session data.
900
def self.create_mdm_session_row(session, show_extended)
901
row = []
902
row << session.closed_at.to_s
903
row << session.opened_at.to_s
904
row << session.close_reason
905
row << session.stype
906
if session.respond_to?(:platform) && !session.platform.nil?
907
row[-1] << " #{session.platform}"
908
end
909
row << (!session.host.nil? ? session.host.address : nil)
910
911
# return complete row
912
row
913
end
914
915
# Dumps the list of active sessions in verbose mode
916
#
917
# @param framework [Msf::Framework] the framework to dump.
918
# @param opts [Hash] the options to dump with.
919
# @return [String] the formatted list of sessions.
920
def self.dump_sessions_verbose(framework, opts={})
921
out = "Active sessions\n" +
922
"===============\n\n"
923
924
if framework.sessions.length == 0
925
out << "No active sessions.\n"
926
return out
927
end
928
929
sessions = opts[:sessions] || framework.sessions
930
931
sessions.each do |session_id, session|
932
sess_info = session.info.to_s
933
sess_id = session.sid.to_s
934
sess_name = session.sname.to_s
935
sess_tunnel = session.tunnel_to_s + " (#{session.session_host})"
936
sess_via = session.via_exploit.to_s
937
sess_type = session.type.to_s
938
sess_uuid = session.payload_uuid.to_s
939
sess_luri = session.exploit_datastore['LURI'] || "" if session.exploit_datastore
940
sess_enc = 'No'
941
if session.respond_to?(:tlv_enc_key) && session.tlv_enc_key && session.tlv_enc_key[:key]
942
sess_enc = "Yes (AES-#{session.tlv_enc_key[:key].length * 8}-CBC)"
943
end
944
945
sess_checkin = "<none>"
946
sess_registration = "No"
947
948
if session.respond_to?(:platform) && session.platform
949
sess_type << " #{session.platform}"
950
end
951
952
if session.respond_to?(:last_checkin) && session.last_checkin
953
sess_checkin = "#{(Time.now.to_i - session.last_checkin.to_i)}s ago @ #{session.last_checkin.to_s}"
954
end
955
956
if !session.payload_uuid.nil? && session.payload_uuid.registered
957
sess_registration = "Yes"
958
if session.payload_uuid.name
959
sess_registration << " - Name=\"#{session.payload_uuid.name}\""
960
end
961
end
962
963
out << " Session ID: #{sess_id}\n"
964
out << " Name: #{sess_name}\n"
965
out << " Type: #{sess_type}\n"
966
out << " Info: #{sess_info}\n"
967
out << " Tunnel: #{sess_tunnel}\n"
968
out << " Via: #{sess_via}\n"
969
out << " Encrypted: #{sess_enc}\n"
970
out << " UUID: #{sess_uuid}\n"
971
out << " CheckIn: #{sess_checkin}\n"
972
out << " Registered: #{sess_registration}\n"
973
unless (sess_luri || '').empty?
974
out << " LURI: #{sess_luri}\n"
975
end
976
977
out << "\n"
978
end
979
980
out << "\n"
981
return out
982
end
983
984
# Dumps the list of running jobs.
985
#
986
# @param framework [Msf::Framework] the framework.
987
# @param verbose [Boolean] if true, also prints the payload, LPORT, URIPATH
988
# and start time, if they exist, for each job.
989
# @param indent [Integer] the indentation amount.
990
# @param col [Integer] the column wrap width.
991
# @return [String] the formatted list of running jobs.
992
def self.dump_jobs(framework, verbose = false, indent = DefaultIndent, col = DefaultColumnWrap)
993
columns = [ 'Id', 'Name', "Payload", "Payload opts"]
994
995
if (verbose)
996
columns += [ "URIPATH", "Start Time", "Handler opts", "Persist" ]
997
end
998
999
tbl = Rex::Text::Table.new(
1000
'Indent' => indent,
1001
'Header' => "Jobs",
1002
'Columns' => columns
1003
)
1004
1005
# Get the persistent job info.
1006
if verbose
1007
begin
1008
persist_list = JSON.parse(File.read(Msf::Config.persist_file))
1009
rescue Errno::ENOENT, JSON::ParserError
1010
persist_list = []
1011
end
1012
end
1013
1014
# jobs are stored as a hash with the keys being a numeric String job_id.
1015
framework.jobs.keys.sort_by(&:to_i).each do |job_id|
1016
# Job context is stored as an Array with the 0th element being
1017
# the running module. If that module is an exploit, ctx will also
1018
# contain its payload.
1019
exploit_mod, _payload_mod = framework.jobs[job_id].ctx
1020
row = []
1021
row[0] = job_id
1022
row[1] = framework.jobs[job_id].name
1023
1024
pinst = exploit_mod.respond_to?(:payload_instance) ? exploit_mod.payload_instance : nil
1025
payload_uri = ''
1026
1027
if pinst.nil?
1028
row[2] = ""
1029
row[3] = ""
1030
else
1031
row[2] = pinst.refname
1032
row[3] = ""
1033
if pinst.respond_to?(:payload_uri)
1034
payload_uri = pinst.payload_uri.strip
1035
row[3] << payload_uri
1036
end
1037
if pinst.respond_to?(:luri)
1038
row[3] << pinst.luri
1039
end
1040
if pinst.respond_to?(:comm_string)
1041
via = pinst.comm_string
1042
if via
1043
row[3] << " #{via}"
1044
end
1045
end
1046
end
1047
1048
if verbose
1049
uripath = exploit_mod.get_resource if exploit_mod.respond_to?(:get_resource)
1050
uripath ||= exploit_mod.datastore['URIPATH']
1051
row[4] = uripath
1052
row[5] = framework.jobs[job_id].start_time
1053
row[6] = ''
1054
row[7] = 'false'
1055
1056
if pinst.respond_to?(:listener_uri)
1057
listener_uri = pinst.listener_uri.strip
1058
row[6] = listener_uri unless listener_uri == payload_uri
1059
end
1060
1061
persist_list.each do |e|
1062
handler_ctx = framework.jobs[job_id.to_s].ctx[1]
1063
if handler_ctx && handler_ctx.respond_to?(:datastore)
1064
row[7] = 'true' if e['mod_options']['Options'] == handler_ctx.datastore.to_h
1065
end
1066
end
1067
1068
end
1069
tbl << row
1070
end
1071
1072
return framework.jobs.keys.length > 0 ? tbl.to_s : "#{tbl.header_to_s}No active jobs.\n"
1073
end
1074
1075
# Dumps the module description
1076
#
1077
# @param mod [Msf::Module] the module.
1078
# @param indent [String] the indentation string
1079
# @return [String] the string description
1080
def self.dump_description(mod, indent)
1081
description = mod.description
1082
1083
output = "Description:\n"
1084
output << word_wrap_description(description, indent)
1085
output << "\n\n"
1086
end
1087
1088
# @param str [String] the string to wrap.
1089
# @param indent [String] the indentation string
1090
# @return [String] the wrapped string.
1091
def self.word_wrap_description(str, indent = '')
1092
return '' if str.blank?
1093
1094
str_lines = str.strip.lines(chomp: true)
1095
# Calculate the preceding whitespace length of each line
1096
smallest_preceding_whitespace = nil
1097
str_lines[1..].to_a.each do |line|
1098
preceding_whitespace = line[/^\s+/]
1099
if preceding_whitespace && (smallest_preceding_whitespace.nil? || preceding_whitespace.length < smallest_preceding_whitespace)
1100
smallest_preceding_whitespace = preceding_whitespace.length
1101
end
1102
end
1103
1104
# Normalize any existing left-most whitespace on each line; Ignoring the first line which won't have any preceding whitespace
1105
result = str_lines.map.with_index do |line, index|
1106
next if line.blank?
1107
1108
"#{indent}#{index == 0 || smallest_preceding_whitespace.nil? ? line : line[smallest_preceding_whitespace..]}"
1109
end.join("\n")
1110
1111
result
1112
end
1113
end
1114
1115
end end
1116
1117