'Continue' and 'tries' wget-like options for meterpreter 'download'

bug/bundler_fix
caye 2016-07-10 16:24:36 +00:00
parent 5dc7d4b16e
commit 3c2f0e814e
No known key found for this signature in database
GPG Key ID: 8449D21ECBABD316
3 changed files with 103 additions and 15 deletions

View File

@ -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
#

View File

@ -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

View File

@ -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