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/spec/lib/msf/base/serializer/readable_text_spec.rb
Views: 11765
1
# -*- coding:binary -*-
2
3
require 'spec_helper'
4
require 'rex/text'
5
6
RSpec.describe Msf::Serializer::ReadableText do
7
# The described_class API takes a mix of strings and whitespace character counts
8
let(:indent_string) { '' }
9
let(:indent_length) { indent_string.length }
10
11
let(:default_module_options) do
12
[
13
Msf::Opt::RHOSTS,
14
Msf::Opt::RPORT(3000),
15
Msf::OptString.new(
16
'foo',
17
[true, 'Foo option', 'bar']
18
),
19
Msf::OptString.new(
20
'fizz',
21
[true, 'fizz option', 'buzz']
22
),
23
Msf::OptString.new(
24
'baz',
25
[true, 'baz option', 'qux']
26
),
27
Msf::OptString.new(
28
'OptionWithModuleDefault',
29
[true, 'option with module default', true]
30
),
31
Msf::OptFloat.new('FloatValue', [false, 'A FloatValue ', 3.5]),
32
Msf::OptString.new(
33
'NewOptionName',
34
[true, 'An option with a new name. Aliases ensure the old and new names are synchronized', 'default_value'],
35
aliases: ['OLD_OPTION_NAME']
36
),
37
Msf::OptString.new(
38
'SMBUser',
39
[true, 'The SMB username'],
40
fallbacks: ['username']
41
),
42
Msf::OptString.new(
43
'SMBDomain',
44
[true, 'The SMB username', 'WORKGROUP'],
45
aliases: ['WindowsDomain'],
46
fallbacks: ['domain']
47
)
48
]
49
end
50
51
let(:default_advanced_module_options) do
52
[
53
Msf::OptEnum.new('DigestAlgorithm', [ true, 'The digest algorithm to use', 'SHA256', %w[SHA1 SHA256] ])
54
]
55
end
56
57
let(:default_evasion_module_options) do
58
[
59
Msf::OptInt.new('EVASION_TEST_OPTION', [ true, 'The evasion test option'])
60
]
61
end
62
63
let(:module_options) { default_module_options }
64
let(:advanced_module_options) { default_advanced_module_options }
65
let(:evasion_module_options) { default_evasion_module_options }
66
67
# (see Msf::Exploit::Remote::Kerberos::ServiceAuthenticator::Options#kerberos_auth_options)
68
def kerberos_auth_options(protocol:, auth_methods:)
69
mixin = Class.new.extend(Msf::Exploit::Remote::Kerberos::ServiceAuthenticator::Options)
70
mixin.kerberos_auth_options(protocol: protocol, auth_methods: auth_methods)
71
end
72
73
let(:aux_mod) do
74
mod_klass = Class.new(Msf::Auxiliary) do
75
def initialize
76
super(
77
'Name' => 'mock module',
78
'Description' => 'mock module',
79
'Author' => ['Unknown'],
80
'License' => MSF_LICENSE,
81
'DefaultOptions' => {
82
'OptionWithModuleDefault' => false,
83
'foo' => 'foo_from_module',
84
'baz' => 'baz_from_module'
85
},
86
)
87
end
88
end
89
90
mod = mod_klass.new
91
mod.send(:register_options, module_options)
92
mod.send(:register_advanced_options, advanced_module_options)
93
mod.send(:register_evasion_options, evasion_module_options)
94
mock_framework = instance_double(::Msf::Framework, datastore: Msf::DataStore.new)
95
allow(mod).to receive(:framework).and_return(mock_framework)
96
mod
97
end
98
99
let(:aux_mod_with_set_options) do
100
mod = aux_mod.replicant
101
mod.framework.datastore['RHOSTS'] = '192.0.2.2'
102
mod.framework.datastore['FloatValue'] = 5
103
mod.framework.datastore['foo'] = 'foo_from_framework'
104
mod.datastore['foo'] = 'new_value'
105
mod.datastore.unset('foo')
106
mod.datastore['OLD_OPTION_NAME'] = nil
107
mod.datastore['username'] = 'username'
108
mod.datastore['fizz'] = 'new_fizz'
109
mod
110
end
111
112
before(:each) do
113
allow(Rex::Text::Table).to receive(:wrapped_tables?).and_return(true)
114
end
115
116
describe '.dump_datastore' do
117
context 'when the datastore is empty' do
118
it 'returns the datastore as a table' do
119
expect(described_class.dump_datastore('Table name', Msf::DataStore.new, indent_length)).to match_table <<~TABLE
120
Table name
121
==========
122
123
No entries in data store.
124
TABLE
125
end
126
end
127
128
context 'when the datastore has values' do
129
it 'returns the datastore as a table' do
130
expect(described_class.dump_datastore('Table name', aux_mod_with_set_options.datastore, indent_length)).to match_table <<~TABLE
131
Table name
132
==========
133
134
Name Value
135
---- -----
136
DigestAlgorithm SHA256
137
EVASION_TEST_OPTION
138
FloatValue 5
139
NewOptionName
140
OptionWithModuleDefault false
141
RHOSTS 192.0.2.2
142
RPORT 3000
143
SMBDomain WORKGROUP
144
SMBUser username
145
VERBOSE false
146
WORKSPACE
147
baz baz_from_module
148
fizz new_fizz
149
foo foo_from_framework
150
username username
151
TABLE
152
end
153
end
154
end
155
156
describe '.dump_options' do
157
context 'when missing is false' do
158
it 'returns the options as a table' do
159
expect(described_class.dump_options(aux_mod_with_set_options, indent_string, false)).to match_table <<~TABLE
160
Name Current Setting Required Description
161
---- --------------- -------- -----------
162
FloatValue 5 no A FloatValue
163
NewOptionName yes An option with a new name. Aliases ensure the old and new names are synchronized
164
OptionWithModuleDefault false yes option with module default
165
RHOSTS 192.0.2.2 yes The target host(s), see https://docs.metasploit.com/docs/using-metasploit/basics/using-metasploit.html
166
RPORT 3000 yes The target port
167
SMBDomain WORKGROUP yes The SMB username
168
SMBUser username yes The SMB username
169
baz baz_from_module yes baz option
170
fizz new_fizz yes fizz option
171
foo foo_from_framework yes Foo option
172
TABLE
173
end
174
end
175
176
context 'when missing is true' do
177
it 'returns the options as a table' do
178
expect(described_class.dump_options(aux_mod_with_set_options, indent_string, true)).to match_table <<~TABLE
179
Name Current Setting Required Description
180
---- --------------- -------- -----------
181
NewOptionName yes An option with a new name. Aliases ensure the old and new names are synchronized
182
TABLE
183
end
184
end
185
186
context 'when some options are grouped' do
187
let(:group_name) { 'group_name' }
188
let(:group_description) { 'Used for example reasons' }
189
let(:option_names) { %w[DigestAlgorithm RHOSTS SMBUser SMBDomain] }
190
let(:group) { Msf::OptionGroup.new(name: group_name, description: group_description, option_names: option_names) }
191
let(:aux_mod_with_grouped_options) do
192
mod = aux_mod_with_set_options.replicant
193
mod.options.add_group(group)
194
mod
195
end
196
197
it 'should return the grouped options separate to the rest of the options' do
198
expect(described_class.dump_options(aux_mod_with_grouped_options, indent_string, false)).to match_table <<~TABLE
199
Name Current Setting Required Description
200
---- --------------- -------- -----------
201
FloatValue 5 no A FloatValue
202
NewOptionName yes An option with a new name. Aliases ensure the old and new names are synchronized
203
OptionWithModuleDefault false yes option with module default
204
RPORT 3000 yes The target port
205
baz baz_from_module yes baz option
206
fizz new_fizz yes fizz option
207
foo foo_from_framework yes Foo option
208
209
210
#{group_description}:
211
212
Name Current Setting Required Description
213
---- --------------- -------- -----------
214
RHOSTS 192.0.2.2 yes The target host(s), see https://docs.metasploit.com/docs/using-metasploit/basics/using-metasploit.html
215
SMBDomain WORKGROUP yes The SMB username
216
SMBUser username yes The SMB username
217
TABLE
218
end
219
end
220
221
context 'when there are multiple options groups' do
222
let(:group_name_1) { 'group_name_1' }
223
let(:group_description_1) { 'Used for example reasons_1' }
224
let(:option_names_1) {['RHOSTS']}
225
let(:group_name_2) { 'group_name_2' }
226
let(:group_description_2) { 'Used for example reasons_2' }
227
let(:option_names_2) { %w[SMBUser SMBDomain] }
228
229
let(:group_1) { Msf::OptionGroup.new(name: group_name_1, description: group_description_1, option_names: option_names_1) }
230
let(:group_2) { Msf::OptionGroup.new(name: group_name_2, description: group_description_2, option_names: option_names_2) }
231
232
let(:aux_mod_with_grouped_options) do
233
mod = aux_mod_with_set_options.replicant
234
mod.options.add_group(group_1)
235
mod.options.add_group(group_2)
236
mod
237
end
238
239
it 'should return the grouped options separate to the rest of the options' do
240
expect(described_class.dump_options(aux_mod_with_grouped_options, indent_string, false)).to match_table <<~TABLE
241
Name Current Setting Required Description
242
---- --------------- -------- -----------
243
FloatValue 5 no A FloatValue
244
NewOptionName yes An option with a new name. Aliases ensure the old and new names are synchronized
245
OptionWithModuleDefault false yes option with module default
246
RPORT 3000 yes The target port
247
baz baz_from_module yes baz option
248
fizz new_fizz yes fizz option
249
foo foo_from_framework yes Foo option
250
251
252
#{group_description_1}:
253
254
Name Current Setting Required Description
255
---- --------------- -------- -----------
256
RHOSTS 192.0.2.2 yes The target host(s), see https://docs.metasploit.com/docs/using-metasploit/basics/using-metasploit.html
257
258
259
#{group_description_2}:
260
261
Name Current Setting Required Description
262
---- --------------- -------- -----------
263
SMBDomain WORKGROUP yes The SMB username
264
SMBUser username yes The SMB username
265
TABLE
266
end
267
end
268
end
269
270
describe '.dump_advanced_options' do
271
context 'when kerberos options are present' do
272
let(:advanced_module_options) do
273
[
274
*default_advanced_module_options,
275
*kerberos_auth_options(protocol: 'Winrm', auth_methods: Msf::Exploit::Remote::AuthOption::WINRM_OPTIONS),
276
]
277
end
278
279
it 'returns the options as a table' do
280
expect(described_class.dump_advanced_options(aux_mod_with_set_options, indent_string)).to match_table <<~TABLE
281
Name Current Setting Required Description
282
---- --------------- -------- -----------
283
DigestAlgorithm SHA256 yes The digest algorithm to use (Accepted: SHA1, SHA256)
284
VERBOSE false no Enable detailed status messages
285
WORKSPACE no Specify the workspace for this module
286
Winrm::Auth auto yes The Authentication mechanism to use (Accepted: auto, ntlm, kerberos, plaintext)
287
288
289
When Winrm::Auth is kerberos:
290
291
Name Current Setting Required Description
292
---- --------------- -------- -----------
293
DomainControllerRhost no The resolvable rhost for the Domain Controller
294
Winrm::Krb5Ccname no The ccache file to use for kerberos authentication
295
Winrm::KrbOfferedEncryptionTypes AES256,AES128,RC4-HMAC,DES-CBC-MD5,DES3-CBC-SHA1 yes Kerberos encryption types to offer
296
Winrm::Rhostname no The rhostname which is required for kerberos - the SPN
297
TABLE
298
end
299
end
300
301
context 'when some options are grouped' do
302
let(:group_name) { 'group_name' }
303
let(:group_description) { 'Used for example reasons' }
304
let(:option_names) { %w[DigestAlgorithm RHOSTS SMBUser SMBDomain] }
305
let(:group) { Msf::OptionGroup.new(name: group_name, description: group_description, option_names: option_names) }
306
let(:aux_mod_with_grouped_options) do
307
mod = aux_mod_with_set_options.replicant
308
mod.options.add_group(group)
309
mod
310
end
311
312
it 'should return the grouped options separate to the rest of the options' do
313
expect(described_class.dump_advanced_options(aux_mod_with_grouped_options, indent_string)).to match_table <<~TABLE
314
Name Current Setting Required Description
315
---- --------------- -------- -----------
316
VERBOSE false no Enable detailed status messages
317
WORKSPACE no Specify the workspace for this module
318
319
320
#{group_description}:
321
322
Name Current Setting Required Description
323
---- --------------- -------- -----------
324
DigestAlgorithm SHA256 yes The digest algorithm to use (Accepted: SHA1, SHA256)
325
326
TABLE
327
end
328
end
329
end
330
331
describe '.dump_evasion_options' do
332
context 'when kerberos options are present' do
333
let(:evasion_module_options) do
334
[
335
*default_evasion_module_options,
336
*kerberos_auth_options(protocol: 'Winrm', auth_methods: Msf::Exploit::Remote::AuthOption::WINRM_OPTIONS),
337
]
338
end
339
340
it 'returns the options as a table' do
341
expect(described_class.dump_evasion_options(aux_mod_with_set_options, indent_string)).to match_table <<~TABLE
342
Name Current Setting Required Description
343
---- --------------- -------- -----------
344
EVASION_TEST_OPTION yes The evasion test option
345
Winrm::Auth auto yes The Authentication mechanism to use (Accepted: auto, ntlm, kerberos, plaintext)
346
347
348
When Winrm::Auth is kerberos:
349
350
Name Current Setting Required Description
351
---- --------------- -------- -----------
352
DomainControllerRhost no The resolvable rhost for the Domain Controller
353
Winrm::Krb5Ccname no The ccache file to use for kerberos authentication
354
Winrm::KrbOfferedEncryptionTypes AES256,AES128,RC4-HMAC,DES-CBC-MD5,DES3-CBC-SHA1 yes Kerberos encryption types to offer
355
Winrm::Rhostname no The rhostname which is required for kerberos - the SPN
356
TABLE
357
end
358
end
359
end
360
361
describe '.dump_description' do
362
context 'when the module description is nil' do
363
it 'dumps the module description' do
364
mod = instance_double(
365
Msf::Module,
366
description: nil
367
)
368
369
result = described_class.dump_description(mod, ' ')
370
expect(result).to match_table <<~TABLE
371
Description:
372
373
TABLE
374
end
375
end
376
377
context 'when the module description has no whitespace' do
378
it 'dumps the module description' do
379
mod = instance_double(
380
Msf::Module,
381
description: 'this is a module description'
382
)
383
384
result = described_class.dump_description(mod, ' ')
385
expect(result).to match_table <<~TABLE
386
Description:
387
this is a module description
388
TABLE
389
end
390
end
391
392
context 'when the module description is a single line' do
393
it 'dumps the module description' do
394
mod = instance_double(
395
Msf::Module,
396
description: %q{ This is a description; with module details etc. }
397
)
398
399
result = described_class.dump_description(mod, ' ')
400
expect(result).to match_table <<~TABLE
401
Description:
402
This is a description; with module details etc.
403
404
TABLE
405
end
406
end
407
408
context 'when the first line has less preceding whitespace than the subsequent lines' do
409
it 'dumps the module description' do
410
mod = instance_double(
411
Msf::Module,
412
description: 'Listen for a connection. First, the port will need to be knocked from
413
the IP defined in KHOST. This IP will work as an authentication method
414
(you can spoof it with tools like hping). After that you could get your
415
shellcode from any IP. The socket will appear as "closed," thus helping to
416
hide the shellcode',
417
)
418
419
result = described_class.dump_description(mod, ' ')
420
expect(result).to match_table <<~TABLE
421
Description:
422
Listen for a connection. First, the port will need to be knocked from
423
the IP defined in KHOST. This IP will work as an authentication method
424
(you can spoof it with tools like hping). After that you could get your
425
shellcode from any IP. The socket will appear as "closed," thus helping to
426
hide the shellcode
427
TABLE
428
end
429
end
430
431
context 'when the first line has more whitespace than the subsequent lines' do
432
it 'dumps the module description' do
433
mod = instance_double(
434
Msf::Module,
435
description: %q{
436
Login credentials to the Motorola WR850G router with
437
firmware v4.03 can be obtained via a simple GET request
438
if issued while the administrator is logged in. A lot
439
more information is available through this request, but
440
you can get it all and more after logging in.
441
},
442
)
443
444
result = described_class.dump_description(mod, ' ')
445
expect(result).to match_table <<~TABLE
446
Description:
447
Login credentials to the Motorola WR850G router with
448
firmware v4.03 can be obtained via a simple GET request
449
if issued while the administrator is logged in. A lot
450
more information is available through this request, but
451
you can get it all and more after logging in.
452
TABLE
453
end
454
end
455
456
context 'when there are two blank lines in a row' do
457
it 'dumps the module description' do
458
mod = instance_double(
459
Msf::Module,
460
description: "Run a meterpreter server in Android.\n\nTunnel communication over HTTP"
461
)
462
463
result = described_class.dump_description(mod, ' ')
464
expect(result).to match_table <<~TABLE
465
Description:
466
Run a meterpreter server in Android.
467
468
Tunnel communication over HTTP
469
TABLE
470
end
471
end
472
473
context 'when the module description spans multiple lines' do
474
it 'dumps the module description' do
475
mod = instance_double(
476
Msf::Module,
477
description: %q{
478
This is a description; with module details etc.
479
480
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer quis mattis lacus. Nam nisi diam, commodo id eu.
481
482
This is a list of important items to consider:
483
- Item A
484
- Item B
485
- Item C
486
487
}
488
)
489
490
result = described_class.dump_description(mod, ' ')
491
expect(result).to match_table <<~TABLE
492
Description:
493
This is a description; with module details etc.
494
495
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer quis mattis lacus. Nam nisi diam, commodo id eu.
496
497
This is a list of important items to consider:
498
- Item A
499
- Item B
500
- Item C
501
502
TABLE
503
end
504
end
505
end
506
end
507
508