'Continue' and 'tries' wget-like options for meterpreter 'download'
parent
5dc7d4b16e
commit
3c2f0e814e
|
@ -195,9 +195,22 @@ class Dir < Rex::Post::Dir
|
|||
# Downloads the contents of a remote directory a
|
||||
# local directory, optionally in a recursive fashion.
|
||||
#
|
||||
def Dir.download(dst, src, recursive = false, force = true, glob = nil, &stat)
|
||||
def Dir.download(dst, src, recursive = false, force = true, glob = nil, continue = false, tries = false, tries_no = 0, &stat)
|
||||
tries_cnt = 0
|
||||
begin
|
||||
dir_files = self.entries(src)
|
||||
rescue Rex::TimeoutError
|
||||
if (tries && (tries_no == 0 || tries_cnt < tries_no))
|
||||
tries_cnt += 1
|
||||
stat.call('error listing - retry #', tries_cnt, src) if (stat)
|
||||
retry
|
||||
else
|
||||
stat.call('error listing directory - giving up', src, dst) if (stat)
|
||||
raise
|
||||
end
|
||||
end
|
||||
|
||||
self.entries(src, glob).each { |src_sub|
|
||||
dir_files.each { |src_sub|
|
||||
dst_item = dst + ::File::SEPARATOR + client.unicode_filter_encode(src_sub)
|
||||
src_item = src + client.fs.file.separator + client.unicode_filter_encode(src_sub)
|
||||
|
||||
|
@ -205,12 +218,28 @@ class Dir < Rex::Post::Dir
|
|||
next
|
||||
end
|
||||
|
||||
src_stat = client.fs.filestat.new(src_item)
|
||||
tries_cnt = 0
|
||||
begin
|
||||
src_stat = client.fs.filestat.new(src_item)
|
||||
rescue Rex::TimeoutError
|
||||
if (tries && (tries_no == 0 || tries_cnt < tries_no))
|
||||
tries_cnt += 1
|
||||
stat.call('error opening file - retry #', tries_cnt, src_item) if (stat)
|
||||
retry
|
||||
else
|
||||
stat.call('error opening file - giving up', tries_cnt, src_item) if (stat)
|
||||
raise
|
||||
end
|
||||
end
|
||||
|
||||
if (src_stat.file?)
|
||||
stat.call('downloading', src_item, dst_item) if (stat)
|
||||
stat.call('dir downloading', src_item, dst_item) if (stat)
|
||||
begin
|
||||
result = client.fs.file.download_file(dst_item, src_item)
|
||||
if (continue || tries) # allow to file.download to log messages
|
||||
result = client.fs.file.download(dst_item, src_item, continue, tries, tries_no, &stat)
|
||||
else
|
||||
result = client.fs.file.download(dst_item, src_item, continue, tries, tries_no)
|
||||
end
|
||||
stat.call(result, src_item, dst_item) if (stat)
|
||||
rescue ::Rex::Post::Meterpreter::RequestError => e
|
||||
if force
|
||||
|
@ -231,10 +260,10 @@ class Dir < Rex::Post::Dir
|
|||
end
|
||||
|
||||
stat.call('mirroring', src_item, dst_item) if (stat)
|
||||
download(dst_item, src_item, recursive, force, glob, &stat)
|
||||
download(dst_item, src_item, recursive, force, glob, continue, tries, tries_no, &stat)
|
||||
stat.call('mirrored', src_item, dst_item) if (stat)
|
||||
end
|
||||
}
|
||||
} # entries
|
||||
end
|
||||
|
||||
#
|
||||
|
|
|
@ -280,7 +280,7 @@ class File < Rex::Post::Meterpreter::Extensions::Stdapi::Fs::IO
|
|||
# If a block is given, it will be called before each file is downloaded and
|
||||
# again when each download is complete.
|
||||
#
|
||||
def File.download(dest, *src_files, &stat)
|
||||
def File.download(dest, *src_files, continue, tries, tries_no, &stat)
|
||||
src_files.each { |src|
|
||||
if (::File.basename(dest) != File.basename(src))
|
||||
# The destination when downloading is a local file so use this
|
||||
|
@ -289,7 +289,7 @@ class File < Rex::Post::Meterpreter::Extensions::Stdapi::Fs::IO
|
|||
end
|
||||
|
||||
stat.call('downloading', src, dest) if (stat)
|
||||
result = download_file(dest, src)
|
||||
result = download_file(dest, src, continue, tries, tries_no, &stat)
|
||||
stat.call(result, src, dest) if (stat)
|
||||
}
|
||||
end
|
||||
|
@ -297,7 +297,7 @@ class File < Rex::Post::Meterpreter::Extensions::Stdapi::Fs::IO
|
|||
#
|
||||
# Download a single file.
|
||||
#
|
||||
def File.download_file(dest_file, src_file)
|
||||
def File.download_file(dest_file, src_file, continue=false, tries=false, tries_no=0, &stat)
|
||||
src_fd = client.fs.file.new(src_file, "rb")
|
||||
|
||||
# Check for changes
|
||||
|
@ -313,12 +313,61 @@ class File < Rex::Post::Meterpreter::Extensions::Stdapi::Fs::IO
|
|||
dir = ::File.dirname(dest_file)
|
||||
::FileUtils.mkdir_p(dir) if dir and not ::File.directory?(dir)
|
||||
|
||||
dst_fd = ::File.new(dest_file, "wb")
|
||||
if continue
|
||||
# continue downloading the file - skip downloaded part in the source
|
||||
dst_fd = ::File.new(dest_file, "ab")
|
||||
begin
|
||||
dst_fd.seek(0, ::IO::SEEK_END)
|
||||
in_pos = dst_fd.pos
|
||||
src_fd.seek(in_pos)
|
||||
stat.call('continuing from ', in_pos, src_file) if (stat)
|
||||
rescue
|
||||
# if we can't seek, download again
|
||||
stat.call('error continuing - downloading from scratch', src_file, dest_file) if (stat)
|
||||
dst_fd.close
|
||||
dst_fd = ::File.new(dest_file, "wb")
|
||||
end
|
||||
else
|
||||
dst_fd = ::File.new(dest_file, "wb")
|
||||
end
|
||||
|
||||
# Keep transferring until EOF is reached...
|
||||
begin
|
||||
while ((data = src_fd.read) != nil)
|
||||
dst_fd.write(data)
|
||||
if tries
|
||||
# resume when timeouts encountered
|
||||
seek_back = false
|
||||
tries_cnt = 0
|
||||
begin # while
|
||||
begin # exception
|
||||
if seek_back
|
||||
in_pos = dst_fd.pos
|
||||
src_fd.seek(in_pos)
|
||||
seek_back = false
|
||||
stat.call('resuming at ', in_pos, src_file) if (stat)
|
||||
else
|
||||
# succesfully read and wrote - reset the counter
|
||||
tries_cnt = 0
|
||||
end
|
||||
data = src_fd.read
|
||||
rescue Rex::TimeoutError
|
||||
# timeout encountered - either seek back and retry or quit
|
||||
if (tries && (tries_no == 0 || tries_cnt < tries_no))
|
||||
tries_cnt += 1
|
||||
seek_back = true
|
||||
stat.call('error downloading - retry #', tries_cnt, src_file) if (stat)
|
||||
retry
|
||||
else
|
||||
stat.call('error downloading - giving up', src_file, dest_file) if (stat)
|
||||
raise
|
||||
end
|
||||
end
|
||||
dst_fd.write(data) if (data != nil)
|
||||
end while (data != nil)
|
||||
else
|
||||
# do the simple copying quiting on the first error
|
||||
while ((data = src_fd.read) != nil)
|
||||
dst_fd.write(data)
|
||||
end
|
||||
end
|
||||
rescue EOFError
|
||||
ensure
|
||||
|
|
|
@ -24,6 +24,8 @@ class Console::CommandDispatcher::Stdapi::Fs
|
|||
#
|
||||
@@download_opts = Rex::Parser::Arguments.new(
|
||||
"-h" => [ false, "Help banner." ],
|
||||
"-c" => [ false, "Resume getting a partially-downloaded file." ],
|
||||
"-t" => [ true, "Set number of retries (0 unlimits)" ],
|
||||
"-r" => [ false, "Download recursively." ])
|
||||
#
|
||||
# Options for the upload command.
|
||||
|
@ -335,11 +337,19 @@ class Console::CommandDispatcher::Stdapi::Fs
|
|||
src_items = []
|
||||
last = nil
|
||||
dest = nil
|
||||
continue = false
|
||||
tries = false
|
||||
tries_no = 0
|
||||
|
||||
@@download_opts.parse(args) { |opt, idx, val|
|
||||
case opt
|
||||
when "-r"
|
||||
recursive = true
|
||||
when "-c"
|
||||
continue = true
|
||||
when "-t"
|
||||
tries = true
|
||||
tries_no = val.to_i
|
||||
when nil
|
||||
src_items << last if (last)
|
||||
last = val
|
||||
|
@ -404,12 +414,12 @@ class Console::CommandDispatcher::Stdapi::Fs
|
|||
# Perform direct matching
|
||||
stat = client.fs.file.stat(src)
|
||||
if (stat.directory?)
|
||||
client.fs.dir.download(dest, src, recursive, true, glob) do |step, src, dst|
|
||||
client.fs.dir.download(dest, src, recursive, true, glob, continue, tries, tries_no) do |step, src, dst|
|
||||
print_status("#{step.ljust(11)}: #{src} -> #{dst}")
|
||||
client.framework.events.on_session_download(client, src, dest) if msf_loaded?
|
||||
end
|
||||
elsif (stat.file?)
|
||||
client.fs.file.download(dest, src) do |step, src, dst|
|
||||
client.fs.file.download(dest, src, continue, tries, tries_no) do |step, src, dst|
|
||||
print_status("#{step.ljust(11)}: #{src} -> #{dst}")
|
||||
client.framework.events.on_session_download(client, src, dest) if msf_loaded?
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue