Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/spec/lib/msf/base/serializer/readable_text_spec.rb
32222 views
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
KrbClockSkew 0s yes Adjust Kerberos client clock by this offset (e.g. 90s, -5m, 1h)
295
Winrm::Krb5Ccname no The ccache file to use for kerberos authentication
296
Winrm::KrbOfferedEncryptionTypes AES256,AES128,RC4-HMAC,DES-CBC-MD5,DES3-CBC-SHA1 yes Kerberos encryption types to offer
297
Winrm::Rhostname no The rhostname which is required for kerberos - the SPN
298
TABLE
299
end
300
end
301
302
context 'when some options are grouped' do
303
let(:group_name) { 'group_name' }
304
let(:group_description) { 'Used for example reasons' }
305
let(:option_names) { %w[DigestAlgorithm RHOSTS SMBUser SMBDomain] }
306
let(:group) { Msf::OptionGroup.new(name: group_name, description: group_description, option_names: option_names) }
307
let(:aux_mod_with_grouped_options) do
308
mod = aux_mod_with_set_options.replicant
309
mod.options.add_group(group)
310
mod
311
end
312
313
it 'should return the grouped options separate to the rest of the options' do
314
expect(described_class.dump_advanced_options(aux_mod_with_grouped_options, indent_string)).to match_table <<~TABLE
315
Name Current Setting Required Description
316
---- --------------- -------- -----------
317
VERBOSE false no Enable detailed status messages
318
WORKSPACE no Specify the workspace for this module
319
320
321
#{group_description}:
322
323
Name Current Setting Required Description
324
---- --------------- -------- -----------
325
DigestAlgorithm SHA256 yes The digest algorithm to use (Accepted: SHA1, SHA256)
326
327
TABLE
328
end
329
end
330
end
331
332
describe '.dump_evasion_options' do
333
context 'when kerberos options are present' do
334
let(:evasion_module_options) do
335
[
336
*default_evasion_module_options,
337
*kerberos_auth_options(protocol: 'Winrm', auth_methods: Msf::Exploit::Remote::AuthOption::WINRM_OPTIONS),
338
]
339
end
340
341
it 'returns the options as a table' do
342
expect(described_class.dump_evasion_options(aux_mod_with_set_options, indent_string)).to match_table <<~TABLE
343
Name Current Setting Required Description
344
---- --------------- -------- -----------
345
EVASION_TEST_OPTION yes The evasion test option
346
Winrm::Auth auto yes The Authentication mechanism to use (Accepted: auto, ntlm, kerberos, plaintext)
347
348
349
When Winrm::Auth is kerberos:
350
351
Name Current Setting Required Description
352
---- --------------- -------- -----------
353
DomainControllerRhost no The resolvable rhost for the Domain Controller
354
KrbClockSkew 0s yes Adjust Kerberos client clock by this offset (e.g. 90s, -5m, 1h)
355
Winrm::Krb5Ccname no The ccache file to use for kerberos authentication
356
Winrm::KrbOfferedEncryptionTypes AES256,AES128,RC4-HMAC,DES-CBC-MD5,DES3-CBC-SHA1 yes Kerberos encryption types to offer
357
Winrm::Rhostname no The rhostname which is required for kerberos - the SPN
358
TABLE
359
end
360
end
361
end
362
363
describe '.dump_description' do
364
context 'when the module description is nil' do
365
it 'dumps the module description' do
366
mod = instance_double(
367
Msf::Module,
368
description: nil
369
)
370
371
result = described_class.dump_description(mod, ' ')
372
expect(result).to match_table <<~TABLE
373
Description:
374
375
TABLE
376
end
377
end
378
379
context 'when the module description has no whitespace' do
380
it 'dumps the module description' do
381
mod = instance_double(
382
Msf::Module,
383
description: 'this is a module description'
384
)
385
386
result = described_class.dump_description(mod, ' ')
387
expect(result).to match_table <<~TABLE
388
Description:
389
this is a module description
390
TABLE
391
end
392
end
393
394
context 'when the module description is a single line' do
395
it 'dumps the module description' do
396
mod = instance_double(
397
Msf::Module,
398
description: %q{ This is a description; with module details etc. }
399
)
400
401
result = described_class.dump_description(mod, ' ')
402
expect(result).to match_table <<~TABLE
403
Description:
404
This is a description; with module details etc.
405
406
TABLE
407
end
408
end
409
410
context 'when the first line has less preceding whitespace than the subsequent lines' do
411
it 'dumps the module description' do
412
mod = instance_double(
413
Msf::Module,
414
description: 'Listen for a connection. First, the port will need to be knocked from
415
the IP defined in KHOST. This IP will work as an authentication method
416
(you can spoof it with tools like hping). After that you could get your
417
shellcode from any IP. The socket will appear as "closed," thus helping to
418
hide the shellcode',
419
)
420
421
result = described_class.dump_description(mod, ' ')
422
expect(result).to match_table <<~TABLE
423
Description:
424
Listen for a connection. First, the port will need to be knocked from
425
the IP defined in KHOST. This IP will work as an authentication method
426
(you can spoof it with tools like hping). After that you could get your
427
shellcode from any IP. The socket will appear as "closed," thus helping to
428
hide the shellcode
429
TABLE
430
end
431
end
432
433
context 'when the first line has more whitespace than the subsequent lines' do
434
it 'dumps the module description' do
435
mod = instance_double(
436
Msf::Module,
437
description: %q{
438
Login credentials to the Motorola WR850G router with
439
firmware v4.03 can be obtained via a simple GET request
440
if issued while the administrator is logged in. A lot
441
more information is available through this request, but
442
you can get it all and more after logging in.
443
},
444
)
445
446
result = described_class.dump_description(mod, ' ')
447
expect(result).to match_table <<~TABLE
448
Description:
449
Login credentials to the Motorola WR850G router with
450
firmware v4.03 can be obtained via a simple GET request
451
if issued while the administrator is logged in. A lot
452
more information is available through this request, but
453
you can get it all and more after logging in.
454
TABLE
455
end
456
end
457
458
context 'when there are two blank lines in a row' do
459
it 'dumps the module description' do
460
mod = instance_double(
461
Msf::Module,
462
description: "Run a meterpreter server in Android.\n\nTunnel communication over HTTP"
463
)
464
465
result = described_class.dump_description(mod, ' ')
466
expect(result).to match_table <<~TABLE
467
Description:
468
Run a meterpreter server in Android.
469
470
Tunnel communication over HTTP
471
TABLE
472
end
473
end
474
475
context 'when the module description spans multiple lines' do
476
it 'dumps the module description' do
477
mod = instance_double(
478
Msf::Module,
479
description: %q{
480
This is a description; with module details etc.
481
482
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer quis mattis lacus. Nam nisi diam, commodo id eu.
483
484
This is a list of important items to consider:
485
- Item A
486
- Item B
487
- Item C
488
489
}
490
)
491
492
result = described_class.dump_description(mod, ' ')
493
expect(result).to match_table <<~TABLE
494
Description:
495
This is a description; with module details etc.
496
497
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer quis mattis lacus. Nam nisi diam, commodo id eu.
498
499
This is a list of important items to consider:
500
- Item A
501
- Item B
502
- Item C
503
504
TABLE
505
end
506
end
507
end
508
end
509
510