CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/lib/msf/core/encoded_payload.rb
Views: 1904
1
# -*- coding: binary -*-
2
3
4
module Msf
5
6
###
7
#
8
# This class wrappers an encoded payload buffer and the means used to create
9
# one.
10
#
11
###
12
class EncodedPayload
13
14
include Framework::Offspring
15
16
#
17
# This method creates an encoded payload instance and returns it to the
18
# caller.
19
#
20
def self.create(pinst, reqs = {})
21
# Create the encoded payload instance
22
p = EncodedPayload.new(pinst.framework, pinst, reqs)
23
24
p.generate(reqs['Raw'])
25
26
return p
27
end
28
29
#
30
# Creates an instance of an EncodedPayload.
31
#
32
def initialize(framework, pinst, reqs)
33
self.framework = framework
34
self.pinst = pinst
35
self.reqs = reqs
36
self.space = reqs['Space']
37
end
38
39
#
40
# This method generates the full encoded payload and returns the encoded
41
# payload buffer.
42
#
43
# @return [String] The encoded payload.
44
def generate(raw = nil)
45
self.raw = raw
46
self.encoded = nil
47
self.nop_sled_size = 0
48
self.nop_sled = nil
49
self.encoder = nil
50
self.nop = nil
51
52
# Increase thread priority as necessary. This is done
53
# to ensure that the encoding and sled generation get
54
# enough time slices from the ruby thread scheduler.
55
priority = Thread.current.priority
56
57
if (priority == 0)
58
Thread.current.priority = 1
59
end
60
61
begin
62
# First, validate
63
pinst.validate()
64
65
# Propagate space information when set
66
unless self.space.nil?
67
# Tell the payload how much space is available
68
pinst.available_space = self.space
69
# Reserve 10% of the available space if encoding is required
70
pinst.available_space -= (self.space * 0.1).ceil if needs_encoding
71
end
72
73
# Generate the raw version of the payload first
74
generate_raw() if self.raw.nil?
75
76
# If encoder is set, it could be an encoders list
77
# The form is "<encoder>:<iteration>, <encoder2>:<iteration>"...
78
unless reqs['Encoder'].blank?
79
encoder_str = reqs['Encoder']
80
encoder_str.scan(/([^:, ]+):?([^,]+)?/).map do |encoder_opt|
81
reqs['Encoder'] = encoder_opt[0]
82
83
self.iterations = (encoder_opt[1] || reqs['Iterations']).to_i
84
self.iterations = 1 if self.iterations < 1
85
86
# Encode the payload with every encoders in the list
87
encode()
88
# Encoded payload is now the raw payload to be encoded by the next encoder
89
self.raw = self.encoded
90
end
91
else
92
self.iterations = reqs['Iterations'].to_i
93
self.iterations = 1 if self.iterations < 1
94
# No specified encoder, let BadChars or ForceEncode do their job
95
encode()
96
end
97
98
# Build the NOP sled
99
generate_sled()
100
101
# Finally, set the complete payload definition
102
self.encoded = (self.nop_sled || '') + self.encoded
103
ensure
104
# Restore the thread priority
105
Thread.current.priority = priority
106
end
107
108
# Return the complete payload
109
return encoded
110
end
111
112
#
113
# Generates the raw payload from the payload instance. This populates the
114
# {#raw} attribute.
115
#
116
# @return [String] The raw, unencoded payload.
117
def generate_raw
118
self.raw = (reqs['Prepend'] || '') + pinst.generate_complete + (reqs['Append'] || '')
119
120
# If an encapsulation routine was supplied, then we should call it so
121
# that we can get the real raw payload.
122
if reqs['EncapsulationRoutine']
123
self.raw = reqs['EncapsulationRoutine'].call(reqs, raw)
124
end
125
end
126
127
#
128
# Scans for a compatible encoder using ranked precedence and populates the
129
# encoded attribute.
130
#
131
def encode
132
# Get the minimum number of nops to use
133
min = (reqs['MinNops'] || 0).to_i
134
min = 0 if reqs['DisableNops']
135
136
# If the exploit needs the payload to be encoded, we need to run the list of
137
# encoders in ranked precedence and try to encode with them.
138
if needs_encoding
139
# Make sure the encoder name from the user has the same String#encoding
140
# as the framework's list of encoder names so we can compare them later.
141
# This is important for when we get input from RPC.
142
if reqs['Encoder']
143
reqs['Encoder'] = reqs['Encoder'].encode(framework.encoders.module_refnames[0].encoding)
144
end
145
146
# If the caller had a preferred encoder, use this encoder only
147
if ((reqs['Encoder']) and (preferred = framework.encoders[reqs['Encoder']]))
148
encoders = [ [reqs['Encoder'], preferred] ]
149
elsif (reqs['Encoder'])
150
wlog("#{pinst.refname}: Failed to find preferred encoder #{reqs['Encoder']}")
151
raise NoEncodersSucceededError, "Failed to find preferred encoder #{reqs['Encoder']}"
152
else
153
encoders = compatible_encoders
154
end
155
156
encoders.each { |encname, encmod|
157
self.encoder = encmod.new
158
self.encoded = nil
159
160
# If the encoding is requested by an exploit check compatibility
161
# options first of all. For the 'generic/none' encoder compatibility
162
# options don't apply.
163
if (reqs['Exploit'] &&
164
!reqs['Exploit'].compatible?(self.encoder) &&
165
encname !~ /generic\/none/)
166
wlog("#{pinst.refname}: Encoder #{encoder.refname} doesn't match the exploit Compat options",
167
'core', LEV_1)
168
next
169
end
170
171
# If there is an encoder type restriction, check to see if this
172
# encoder matches with what we're searching for.
173
if ((reqs['EncoderType']) and
174
(self.encoder.encoder_type.split(/\s+/).include?(reqs['EncoderType']) == false))
175
wlog("#{pinst.refname}: Encoder #{encoder.refname} is not a compatible encoder type: #{reqs['EncoderType']} != #{self.encoder.encoder_type}",
176
'core', LEV_1)
177
next
178
end
179
180
# If the exploit did not explicitly request a kind of encoder and
181
# the current encoder has a manual ranking, then it should not be
182
# considered as a valid encoder. A manual ranking tells the
183
# framework that an encoder must be explicitly defined as the
184
# encoder of choice for an exploit.
185
if ((reqs['EncoderType'].nil?) and
186
(reqs['Encoder'].nil?) and
187
(self.encoder.rank == ManualRanking))
188
wlog("#{pinst.refname}: Encoder #{encoder.refname} is manual ranked and was not defined as a preferred encoder.",
189
'core', LEV_1)
190
next
191
end
192
193
# If the caller explicitly requires register preservation, make sure
194
# that the module in question can handle it. This is mostly used by
195
# the stage encoder path.
196
if (reqs['ForceSaveRegisters'] and
197
reqs['EncoderOptions'] and
198
(reqs['EncoderOptions']['SaveRegisters'].to_s.length > 0) and
199
(! self.encoder.can_preserve_registers?))
200
wlog("#{pinst.refname}: Encoder #{encoder.refname} does not preserve registers and the caller needs #{reqs['EncoderOptions']['SaveRegisters']} preserved.",
201
'core', LEV_1)
202
next
203
end
204
205
# Import the datastore from payload (and likely exploit by proxy)
206
self.encoder.share_datastore(pinst.datastore)
207
208
# If we have any encoder options, import them into the datastore
209
# of the encoder.
210
if (reqs['EncoderOptions'])
211
self.encoder.datastore.import_options_from_hash(reqs['EncoderOptions'])
212
end
213
214
# Validate the encoder to make sure it's properly initialized.
215
begin
216
self.encoder.validate
217
rescue ::Exception
218
wlog("#{pinst.refname}: Failed to validate encoder #{encoder.refname}: #{$!}",
219
'core', LEV_1)
220
next
221
end
222
223
# Tell the encoder how much space is available
224
self.encoder.available_space = self.space
225
226
eout = self.raw.dup
227
228
next_encoder = false
229
230
# Try encoding with the current encoder
231
#
232
# NOTE: Using more than one iteration may cause successive iterations to switch
233
# to using a different encoder.
234
#
235
1.upto(self.iterations) do |iter|
236
err_start = "#{pinst.refname}: iteration #{iter}"
237
238
begin
239
eout = self.encoder.encode(eout, reqs['BadChars'], nil, pinst.platform)
240
rescue EncodingError => e
241
wlog("#{err_start}: Encoder #{encoder.refname} failed: #{e}", 'core', LEV_1)
242
dlog("#{err_start}: Call stack\n#{e.backtrace}", 'core', LEV_3)
243
next_encoder = true
244
break
245
246
rescue ::Exception => e
247
elog("Broken encoder #{encoder.refname}", error: e)
248
next_encoder = true
249
break
250
end
251
252
# Check to see if we have enough room for the minimum requirements
253
if ((reqs['Space']) and (reqs['Space'] < eout.length + min))
254
wlog("#{err_start}: Encoded payload version is too large (#{eout.length} bytes) with encoder #{encoder.refname}",
255
'core', LEV_1)
256
next_encoder = true
257
break
258
end
259
260
ilog("#{err_start}: Successfully encoded with encoder #{encoder.refname} (size is #{eout.length})",
261
'core', LEV_0)
262
end
263
264
next if next_encoder
265
266
self.encoded = eout
267
break
268
}
269
270
# If the encoded payload is nil, raise an exception saying that we
271
# suck at life.
272
if (self.encoded == nil)
273
self.encoder = nil
274
raise NoEncodersSucceededError,
275
"#{pinst.refname}: All encoders failed to encode.",
276
caller
277
end
278
279
# If there are no bad characters, then the raw is the same as the
280
# encoded
281
else
282
# NOTE: BadChars can contain whitespace, so don't use String#blank?
283
unless reqs['BadChars'].nil? || reqs['BadChars'].empty?
284
ilog("#{pinst.refname}: payload contains no badchars, skipping automatic encoding", 'core', LEV_0)
285
end
286
287
# Space = 0 is a special value used by msfvenom to generate the smallest
288
# payload possible. In that case do not raise an exception indicating
289
# that the payload is too large.
290
if reqs['Space'] && reqs['Space'] > 0 && reqs['Space'] < raw.length + min
291
wlog("#{pinst.refname}: Raw (unencoded) payload is too large (#{raw.length} bytes)", 'core', LEV_1)
292
raise PayloadSpaceViolation, 'The payload exceeds the specified space', caller
293
end
294
295
self.encoded = raw
296
end
297
298
# Prefix the prepend encoder value
299
self.encoded = (reqs['PrependEncoder'] || '') + self.encoded
300
self.encoded << (reqs['AppendEncoder'] || '')
301
end
302
303
#
304
# Construct a NOP sled if necessary
305
#
306
def generate_sled
307
min = reqs['MinNops'] || 0
308
space = reqs['Space']
309
pad_nops = reqs['PadNops']
310
311
self.nop_sled_size = min
312
313
# Calculate the number of NOPs to pad out the buffer with based on the
314
# requirements. If there was a space requirement, check to see if
315
# there's any room at all left for a sled.
316
if ((space) and
317
(space > encoded.length))
318
self.nop_sled_size = reqs['Space'] - self.encoded.length
319
end
320
321
# If the maximum number of NOPs has been exceeded, wrap it back down.
322
if ((reqs['MaxNops']) and
323
(reqs['MaxNops'] < self.nop_sled_size))
324
self.nop_sled_size = reqs['MaxNops']
325
end
326
327
# Check for the DisableNops setting
328
self.nop_sled_size = 0 if reqs['DisableNops']
329
330
# Check for the PadNops setting
331
self.nop_sled_size = (pad_nops - self.encoded.length) if reqs['PadNops']
332
333
# Now construct the actual sled
334
if (self.nop_sled_size > 0)
335
nops = pinst.compatible_nops
336
337
# If the caller had a preferred nop, try to find it and prefix it
338
if ((reqs['Nop']) and
339
(preferred = framework.nops[reqs['Nop']]))
340
nops.unshift([reqs['Nop'], preferred ])
341
elsif (reqs['Nop'])
342
wlog("#{pinst.refname}: Failed to find preferred nop #{reqs['Nop']}")
343
end
344
345
nops.each { |nopname, nopmod|
346
# Create an instance of the nop module
347
self.nop = nopmod.new
348
349
# Propagate options from the payload and possibly exploit
350
self.nop.share_datastore(pinst.datastore)
351
352
# The list of save registers
353
save_regs = (reqs['SaveRegisters'] || []) + (pinst.save_registers || [])
354
355
if (save_regs.empty? == true)
356
save_regs = nil
357
end
358
359
begin
360
nop.copy_ui(pinst)
361
self.nop_sled = nop.generate_sled(self.nop_sled_size,
362
'BadChars' => reqs['BadChars'],
363
'SaveRegisters' => save_regs)
364
365
if nop_sled && nop_sled.length == nop_sled_size
366
break
367
else
368
dlog("#{pinst.refname}: Nop generator #{nop.refname} failed to generate sled for payload", 'core', LEV_1)
369
end
370
rescue
371
dlog("#{pinst.refname}: Nop generator #{nop.refname} failed to generate sled for payload: #{$!}",
372
'core', LEV_1)
373
374
self.nop = nil
375
end
376
}
377
378
if (self.nop_sled == nil)
379
raise NoNopsSucceededError,
380
"#{pinst.refname}: All NOP generators failed to construct sled for.",
381
caller
382
end
383
else
384
self.nop_sled = ''
385
end
386
387
return self.nop_sled
388
end
389
390
391
#
392
# Convert the payload to an executable appropriate for its arch and
393
# platform.
394
#
395
# +opts+ are passed directly to +Msf::Util::EXE.to_executable+
396
#
397
# see +Msf::Exploit::EXE+
398
#
399
def encoded_exe(opts={})
400
# Ensure arch and platform are in the format that to_executable expects
401
if opts[:arch] and not opts[:arch].kind_of? Array
402
opts[:arch] = [ opts[:arch] ]
403
end
404
if (opts[:platform].kind_of? Msf::Module::PlatformList)
405
opts[:platform] = opts[:platform].platforms
406
end
407
408
emod = pinst.assoc_exploit if pinst.respond_to? :assoc_exploit
409
410
if emod
411
if (emod.datastore["EXE::Custom"] and emod.respond_to? :get_custom_exe)
412
return emod.get_custom_exe
413
end
414
# This is a little ghetto, grabbing datastore options from the
415
# associated exploit, but it doesn't really make sense for the
416
# payload to have exe options if the exploit doesn't need an exe.
417
# Msf::Util::EXE chooses reasonable defaults if these aren't given,
418
# so it's not that big of an issue.
419
opts.merge!({
420
:template_path => emod.datastore['EXE::Path'],
421
:template => emod.datastore['EXE::Template'],
422
:inject => emod.datastore['EXE::Inject'],
423
:fallback => emod.datastore['EXE::FallBack'],
424
:sub_method => emod.datastore['EXE::OldMethod']
425
})
426
# Prefer the target's platform/architecture information, but use
427
# the exploit module's if no target specific information exists.
428
opts[:platform] ||= emod.target_platform if emod.respond_to? :target_platform
429
opts[:platform] ||= emod.platform if emod.respond_to? :platform
430
opts[:arch] ||= emod.target_arch if emod.respond_to? :target_arch
431
opts[:arch] ||= emod.arch if emod.respond_to? :arch
432
end
433
# Lastly, try the payload's. This always happens if we don't have an
434
# associated exploit module.
435
opts[:platform] ||= pinst.platform if pinst.respond_to? :platform
436
opts[:arch] ||= pinst.arch if pinst.respond_to? :arch
437
438
Msf::Util::EXE.to_executable(framework, opts[:arch], opts[:platform], encoded, opts)
439
end
440
441
#
442
# Generate a jar file containing the encoded payload.
443
#
444
# Uses the payload's +generate_jar+ method if it is implemented (Java
445
# payloads should all have it). Otherwise, converts the payload to an
446
# executable and uses Msf::Util::EXE.to_jar to create a jar file that dumps
447
# the exe out to a random file name in the system's temporary directory and
448
# executes it.
449
#
450
def encoded_jar(opts={})
451
return pinst.generate_jar(opts) if pinst.respond_to? :generate_jar
452
453
opts[:spawn] ||= pinst.datastore["Spawn"]
454
455
Msf::Util::EXE.to_jar(encoded_exe(opts), opts)
456
end
457
458
#
459
# Similar to +encoded_jar+ but builds a web archive for use in servlet
460
# containers such as Tomcat.
461
#
462
def encoded_war(opts={})
463
return pinst.generate_war(opts) if pinst.respond_to? :generate_war
464
465
Msf::Util::EXE.to_jsp_war(encoded_exe(opts), opts)
466
end
467
468
#
469
# An array containing the architecture(s) that this payload was made to run on
470
#
471
def arch
472
if pinst
473
pinst.arch
474
end
475
end
476
477
#
478
# An array containing the platform(s) that this payload was made to run on
479
#
480
def platform
481
if pinst
482
pinst.platform
483
end
484
end
485
486
#
487
# The raw version of the payload
488
#
489
attr_reader :raw
490
#
491
# The encoded version of the raw payload plus the NOP sled
492
# if one was generated.
493
#
494
attr_reader :encoded
495
#
496
# The size of the NOP sled
497
#
498
attr_reader :nop_sled_size
499
#
500
# The NOP sled itself
501
#
502
attr_reader :nop_sled
503
#
504
# The encoder that was used
505
#
506
attr_reader :encoder
507
#
508
# The NOP generator that was used
509
#
510
attr_reader :nop
511
#
512
# The number of encoding iterations used
513
#
514
attr_reader :iterations
515
#
516
# The maximum number of bytes acceptable for the encoded payload
517
#
518
attr_reader :space
519
protected
520
521
attr_writer :raw # :nodoc:
522
attr_writer :encoded # :nodoc:
523
attr_writer :nop_sled_size # :nodoc:
524
attr_writer :nop_sled # :nodoc:
525
attr_writer :payload # :nodoc:
526
attr_writer :encoder # :nodoc:
527
attr_writer :nop # :nodoc:
528
attr_writer :iterations # :nodoc:
529
attr_writer :space # :nodoc
530
531
#
532
# The payload instance used to generate the payload
533
#
534
attr_accessor :pinst
535
#
536
# The requirements used for generation
537
#
538
attr_accessor :reqs
539
540
def needs_encoding
541
!reqs['Encoder'].blank? || reqs['ForceEncode'] || has_chars?(reqs['BadChars'])
542
end
543
544
def has_chars?(chars)
545
# NOTE: BadChars can contain whitespace, so don't use String#blank?
546
if chars.nil? || chars.empty?
547
return false
548
end
549
550
# payload hasn't been set yet but we have bad characters so assume they'll need to be encoded
551
return true if self.raw.nil?
552
553
return false if self.raw.empty?
554
555
chars.each_byte do |bad|
556
return true if self.raw.index(bad.chr(::Encoding::ASCII_8BIT))
557
end
558
559
false
560
end
561
562
def compatible_encoders
563
arch = reqs['Arch'] || pinst.arch
564
platform = reqs['Platform'] || pinst.platform
565
566
encoders = []
567
568
framework.encoders.each_module_ranked(
569
'Arch' => arch, 'Platform' => platform) { |name, mod|
570
encoders << [ name, mod ]
571
}
572
573
encoders
574
end
575
end
576
577
end
578
579