# -*- coding: binary -*- module Msf module Exploit::FileDropper def initialize(info = {}) super register_advanced_options( [ OptInt.new( 'FileDropperDelay', [ false, 'Delay in seconds before attempting file cleanup' ]) ], self.class) end # # When a new session is created, attempt to delete any files that the # exploit created. # # @param (see Msf::Exploit#on_new_session) # @return [void] # def on_new_session(session) super if session.type == "meterpreter" session.core.use("stdapi") unless session.ext.aliases.include?("stdapi") end if not @dropped_files or @dropped_files.empty? return true end @dropped_files.delete_if do |file| win_file = file.gsub("/", "\\\\") if session.type == "meterpreter" begin # Meterpreter should do this automatically as part of # fs.file.rm(). Until that has been implemented, remove the # read-only flag with a command. if session.platform =~ /win/ session.shell_command_token(%Q|attrib.exe -r #{win_file}|) end session.fs.file.rm(file) print_good("Deleted #{file}") true rescue ::Rex::Post::Meterpreter::RequestError false end else win_cmds = [ %Q|attrib.exe -r "#{win_file}"|, %Q|del.exe /f /q "#{win_file}"| ] # We need to be platform-independent here. Since we can't be # certain that {#target} is accurate because exploits with # automatic targets frequently change it, we just go ahead and # run both a windows and a unixy command in the same line. One # of them will definitely fail and the other will probably # succeed. Doing it this way saves us an extra round-trip. # Trick shared by @mihi42 session.shell_command_token("rm -f \"#{file}\" >/dev/null ; echo ' & #{win_cmds.join(" & ")} & echo \" ' >/dev/null") print_good("Deleted #{file}") true end end end # # Record file as needing to be cleaned up # # @param files [Array] List of paths on the target that should # be deleted during cleanup. Each filename should be either a full # path or relative to the current working directory of the session # (not necessarily the same as the cwd of the server we're # exploiting). # @return [void] def register_files_for_cleanup(*files) @dropped_files ||= [] @dropped_files += files nil end # Singular version alias register_file_for_cleanup register_files_for_cleanup # # While the exploit cleanup do a last attempt to delete any files created # if there is a file_rm method available. Warn the user if any files were # not cleaned up. # # @see Msf::Exploit#cleanup # @see Msf::Post::File#file_rm def cleanup super # Check if file_rm method is available (local exploit, mixin support, module support) if not @dropped_files or @dropped_files.empty? return end if respond_to?(:file_rm) delay = datastore['FileDropperDelay'] if delay print_status("Waiting #{delay}s before file cleanup...") select(nil,nil,nil,delay) end @dropped_files.delete_if do |file| begin file_rm(file) print_good("Deleted #{file}") true #rescue ::Rex::SocketError, ::EOFError, ::IOError, ::Errno::EPIPE, ::Rex::Post::Meterpreter::RequestError => e rescue ::Exception => e vprint_error("Failed to delete #{file}: #{e}") elog("Failed to delete #{file}: #{e.class}: #{e}") elog("Call stack:\n#{e.backtrace.join("\n")}") false end end end @dropped_files.each do |f| print_warning("This exploit may require manual cleanup of '#{f}' on the target") end end end end