# -*- 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)
		if session.type == "meterpreter"
			session.core.use("stdapi") unless session.ext.aliases.include?("stdapi")
		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
				cmds = [
						%Q|attrib.exe -r "#{win_file}"|,
						%Q|del.exe /f /q "#{win_file}"|,
						%Q|rm -f "#{file}" >/dev/null|,
					]

				# 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.
				session.shell_command_token(cmds.join(" ; "))
				print_good("Deleted #{file}")
				true
			end
		end

		super
	end

	#
	# Record file as needing to be cleaned up
	#
	# @param files [Array<String>] 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}")
		end

	end
end
end
