From 07204dc99e81980d55eeea1f9b54689480b9aac7 Mon Sep 17 00:00:00 2001 From: bwatters Date: Mon, 13 Sep 2021 11:51:00 -0500 Subject: [PATCH] Rubocop -A --- lib/msf/core/post/file.rb | 291 +++++++++++++++++---------------- test/modules/post/test/file.rb | 179 ++++++++++---------- 2 files changed, 247 insertions(+), 223 deletions(-) diff --git a/lib/msf/core/post/file.rb b/lib/msf/core/post/file.rb index df7fdd5f66..5abb05ef57 100644 --- a/lib/msf/core/post/file.rb +++ b/lib/msf/core/post/file.rb @@ -1,33 +1,32 @@ # -*- coding: binary -*- -# + require 'rex/post/meterpreter/extensions/stdapi/command_ids' require 'rex/post/file_stat' module Msf::Post::File - include Msf::Post::Common def initialize(info = {}) super( update_info( info, - 'Compat' => { - 'Meterpreter' => { - 'Commands' => %w[ - core_channel_* - stdapi_fs_chdir - stdapi_fs_delete_dir - stdapi_fs_delete_file - stdapi_fs_file_expand_path - stdapi_fs_file_move - stdapi_fs_getwd - stdapi_fs_ls - stdapi_fs_mkdir - stdapi_fs_stat - ] - } + 'Compat' => { + 'Meterpreter' => { + 'Commands' => %w[ + core_channel_* + stdapi_fs_chdir + stdapi_fs_delete_dir + stdapi_fs_delete_file + stdapi_fs_file_expand_path + stdapi_fs_file_move + stdapi_fs_getwd + stdapi_fs_ls + stdapi_fs_mkdir + stdapi_fs_stat + ] } - ) + } + ) ) end @@ -37,8 +36,12 @@ module Msf::Post::File # # @return [void] def cd(path) - e_path = expand_path(path) rescue path - if session.type == "meterpreter" + e_path = begin + expand_path(path) + rescue StandardError + path + end + if session.type == 'meterpreter' session.fs.dir.chdir(e_path) elsif session.type == 'powershell' cmd_exec("Set-Location -Path \"#{e_path}\"") @@ -56,23 +59,19 @@ module Msf::Post::File # # @return [String] def pwd - if session.type == "meterpreter" + if session.type == 'meterpreter' return session.fs.dir.getwd elsif session.type == 'powershell' return cmd_exec('(Get-Location).Path').strip + elsif session.platform == 'windows' + return session.shell_command_token('echo %CD%').to_s.strip + # XXX: %CD% only exists on XP and newer, figure something out for NT4 + # and 2k + elsif command_exists?('pwd') + return session.shell_command_token('pwd').to_s.strip else - if session.platform == 'windows' - # XXX: %CD% only exists on XP and newer, figure something out for NT4 - # and 2k - return session.shell_command_token("echo %CD%").to_s.strip - else - if command_exists?("pwd") - return session.shell_command_token("pwd").to_s.strip - else - # Result on systems without pwd command - return session.shell_command_token("echo $PWD").to_s.strip - end - end + # Result on systems without pwd command + return session.shell_command_token('echo $PWD').to_s.strip end end @@ -100,13 +99,13 @@ module Msf::Post::File # Result on systems without ls command if directory[-1] != '/' - directory = directory + "/" + directory += '/' end result = [] data = session.shell_command_token("for fn in #{directory}*; do echo $fn; done") parts = data.split("\n") parts.each do |line| - line = line.split("/")[-1] + line = line.split('/')[-1] result.insert(-1, line) end @@ -124,12 +123,10 @@ module Msf::Post::File result = session.fs.dir.mkdir(path) unless directory?(path) elsif session.type == 'powershell' result = cmd_exec("New-Item \"#{path}\" -itemtype directory") + elsif session.platform == 'windows' + result = cmd_exec("mkdir \"#{path}\"") else - if session.platform == 'windows' - result = cmd_exec("mkdir \"#{path}\"") - else - result = cmd_exec("mkdir -p '#{path}'") - end + result = cmd_exec("mkdir -p '#{path}'") end vprint_status("#{path} created") register_dir_for_cleanup(path) @@ -142,8 +139,13 @@ module Msf::Post::File # @param path [String] Remote filename to check def directory?(path) if session.type == 'meterpreter' - stat = session.fs.file.stat(path) rescue nil + stat = begin + session.fs.file.stat(path) + rescue StandardError + nil + end return false unless stat + return stat.directory? elsif session.type == 'powershell' return cmd_exec("Test-Path -Path \"#{path}\" -PathType Container").include?('True') @@ -155,6 +157,7 @@ module Msf::Post::File end return false if f.nil? || f.empty? return false unless f =~ /true/ + true end end @@ -164,7 +167,7 @@ module Msf::Post::File # # @return [String] def expand_path(path) - if session.type == "meterpreter" + if session.type == 'meterpreter' return session.fs.file.expand_path(path) elsif session.type == 'powershell' return cmd_exec("[Environment]::ExpandEnvironmentVariables(\"#{path}\")") @@ -179,11 +182,16 @@ module Msf::Post::File # @param path [String] Remote filename to check def file?(path) if session.type == 'meterpreter' - stat = session.fs.file.stat(path) rescue nil + stat = begin + session.fs.file.stat(path) + rescue StandardError + nil + end return false unless stat + return stat.file? elsif session.type == 'powershell' - return cmd_exec("[System.IO.File]::Exists( \"#{path}\")")&.include?("True") + return cmd_exec("[System.IO.File]::Exists( \"#{path}\")")&.include?('True') else if session.platform == 'windows' f = cmd_exec("cmd.exe /C IF exist \"#{path}\" ( echo true )") @@ -195,6 +203,7 @@ module Msf::Post::File end return false if f.nil? || f.empty? return false unless f =~ /true/ + true end end @@ -233,7 +242,7 @@ module Msf::Post::File def writable?(path) verification_token = Rex::Text.rand_text_alpha_upper(8) if session.type == 'powershell' && file?(path) - return cmd_exec("$a=[System.IO.File]::OpenWrite('#{path}');if($?){echo #{verification_token}};$a.Close()").include?(verification_token) + return cmd_exec("$a=[System.IO.File]::OpenWrite('#{path}');if($?){echo #{verification_token}};$a.Close()").include?(verification_token) end raise "`writable?' method does not support Windows systems" if session.platform == 'windows' @@ -261,14 +270,15 @@ module Msf::Post::File # @return [Boolean] true if +path+ exists and is readable # def readable?(path) - verification_token = Rex::Text::rand_text_alpha(8) + verification_token = Rex::Text.rand_text_alpha(8) return false unless exists?(path) + if session.type == 'powershell' - unless directory?(path) + if directory?(path) + return cmd_exec("[System.IO.Directory]::GetFiles('#{path}'); if($?) {echo #{verification_token}}").include?(verification_token) + else return cmd_exec("[System.IO.File]::OpenRead(\"#{path}\");if($?){echo\ #{verification_token}}").include?(verification_token) - else - return cmd_exec("[System.IO.Directory]::GetFiles('#{path}'); if($?) {echo #{verification_token}}").include?(verification_token) end end @@ -283,10 +293,14 @@ module Msf::Post::File # @param path [String] Remote filename to check def exist?(path) if session.type == 'meterpreter' - stat = session.fs.file.stat(path) rescue nil - return !!(stat) + stat = begin + session.fs.file.stat(path) + rescue StandardError + nil + end + return !!stat elsif session.type == 'powershell' - return cmd_exec("[System.IO.File]::Exists( \"#{path}\")")&.include?("True") + return cmd_exec("[System.IO.File]::Exists( \"#{path}\")")&.include?('True') else if session.platform == 'windows' f = cmd_exec("cmd.exe /C IF exist \"#{path}\" ( echo true )") @@ -295,11 +309,12 @@ module Msf::Post::File end return false if f.nil? || f.empty? return false unless f =~ /true/ + true end end - alias :exists? :exist? + alias exists? exist? # # Retrieve file attributes for +path+ on the remote system @@ -322,7 +337,7 @@ module Msf::Post::File unless ::File.exist?(fname) ::FileUtils.touch(fname) end - output = ::File.open(fname, "a") + output = ::File.open(fname, 'a') data.each_line do |d| output.puts(d) end @@ -414,8 +429,8 @@ module Msf::Post::File # @param data [String] Contents to put in the file # @return [void] def write_file(file_name, data) - if session.type == "meterpreter" - fd = session.fs.file.new(file_name, "wb") + if session.type == 'meterpreter' + fd = session.fs.file.new(file_name, 'wb') fd.write(data) fd.close elsif session.type == 'powershell' @@ -440,8 +455,8 @@ module Msf::Post::File # @param data [String] Contents to put in the file # @return bool def append_file(file_name, data) - if session.type == "meterpreter" - fd = session.fs.file.new(file_name, "ab") + if session.type == 'meterpreter' + fd = session.fs.file.new(file_name, 'ab') fd.write(data) fd.close elsif session.type == 'powershell' @@ -488,7 +503,7 @@ module Msf::Post::File # # @param path [String] Path on the remote filesystem # @param mode [Fixnum] Mode as an octal number - def chmod(path, mode = 0700) + def chmod(path, mode = 0o700) if session.platform == 'windows' raise "`chmod' method does not support Windows systems" end @@ -506,7 +521,7 @@ module Msf::Post::File # @param path [String] Directory in the exploits folder # @param path [String] Filename in the data folder def exploit_data(data_directory, file) - file_path = ::File.join(::Msf::Config.data_directory, "exploits", data_directory, file) + file_path = ::File.join(::Msf::Config.data_directory, 'exploits', data_directory, file) ::File.binread(file_path) end @@ -518,16 +533,14 @@ module Msf::Post::File # @return [void] def rm_f(*remote_files) remote_files.each do |remote| - if session.type == "meterpreter" + if session.type == 'meterpreter' session.fs.file.delete(remote) if file?(remote) elsif session.type == 'powershell' cmd_exec("[System.IO.File]::Delete(\"#{remote}\")") if file?(remote) + elsif session.platform == 'windows' + cmd_exec("del /q /f \"#{remote}\"") else - if session.platform == 'windows' - cmd_exec("del /q /f \"#{remote}\"") - else - cmd_exec("rm -f \"#{remote}\"") - end + cmd_exec("rm -f \"#{remote}\"") end end end @@ -540,21 +553,19 @@ module Msf::Post::File # @return [void] def rm_rf(*remote_dirs) remote_dirs.each do |remote| - if session.type == "meterpreter" + if session.type == 'meterpreter' session.fs.dir.rmdir(remote) if exist?(remote) elsif session.type == 'powershell' cmd_exec("Remove-Item -Path \"#{remote}\" -Force -Recurse") + elsif session.platform == 'windows' + cmd_exec("rd /s /q \"#{remote}\"") else - if session.platform == 'windows' - cmd_exec("rd /s /q \"#{remote}\"") - else - cmd_exec("rm -rf \"#{remote}\"") - end + cmd_exec("rm -rf \"#{remote}\"") end end end - alias :file_rm :rm_f - alias :dir_rm :rm_rf + alias file_rm rm_f + alias dir_rm rm_rf # # Renames a remote file. If the new file path is a directory, the file will be @@ -564,9 +575,8 @@ module Msf::Post::File # @param new_file [String] The new name for the remote file # @return [Boolean] Return true on success and false on failure def rename_file(old_file, new_file) - verification_token = Rex::Text.rand_text_alphanumeric(8) - if session.type == "meterpreter" + if session.type == 'meterpreter' begin new_file = new_file + session.fs.file.separator + session.fs.file.basename(old_file) if directory?(new_file) return (session.fs.file.mv(old_file, new_file).result == 0) @@ -577,13 +587,14 @@ module Msf::Post::File cmd_exec("Move-Item \"#{old_file}\" \"#{new_file}\" -Force; if($?){echo #{verification_token}}").include?(verification_token) elsif session.platform == 'windows' return false unless file?(old_file) # adding this because when the old_file is not present it hangs for a while, should be removed after this issue is fixed. - cmd_exec(%Q|move #{directory?(new_file) ? "" : "/y"} "#{old_file}" "#{new_file}" & if not errorlevel 1 echo #{verification_token}|).include?(verification_token) + + cmd_exec(%(move #{directory?(new_file) ? '' : '/y'} "#{old_file}" "#{new_file}" & if not errorlevel 1 echo #{verification_token})).include?(verification_token) else - cmd_exec(%Q|mv #{directory?(new_file) ? "" : "-f"} "#{old_file}" "#{new_file}" && echo #{verification_token}|).include?(verification_token) + cmd_exec(%(mv #{directory?(new_file) ? '' : '-f'} "#{old_file}" "#{new_file}" && echo #{verification_token})).include?(verification_token) end end - alias :move_file :rename_file - alias :mv_file :rename_file + alias move_file rename_file + alias mv_file rename_file # # @@ -593,9 +604,10 @@ module Msf::Post::File # @param dst_file [String] The name for the remote destination file # @return [Boolean] Return true on success and false on failure def copy_file(src_file, dst_file) - return false if directory?(dst_file) or directory?(src_file) + return false if directory?(dst_file) || directory?(src_file) + verification_token = Rex::Text.rand_text_alpha_upper(8) - if session.type == "meterpreter" + if session.type == 'meterpreter' begin return (session.fs.file.cp(src_file, dst_file).result == 0) rescue Rex::Post::Meterpreter::RequestError => e # when the source file is not present meterpreter will raise an error @@ -603,17 +615,15 @@ module Msf::Post::File end elsif session.type == 'powershell' cmd_exec("Copy-Item \"#{src_file}\" -Destination \"#{dst_file}\"; if($?){echo #{verification_token}}").include?(verification_token) + elsif session.platform == 'windows' + cmd_exec(%(copy /y "#{src_file}" "#{dst_file}" & if not errorlevel 1 echo #{verification_token})).include?(verification_token) else - if session.platform == 'windows' - cmd_exec(%Q|copy /y "#{src_file}" "#{dst_file}" & if not errorlevel 1 echo #{verification_token}|).include?(verification_token) - else - cmd_exec(%Q|cp -f "#{src_file}" "#{dst_file}" && echo #{verification_token}|).include?(verification_token) - end + cmd_exec(%(cp -f "#{src_file}" "#{dst_file}" && echo #{verification_token})).include?(verification_token) end end - alias :cp_file :copy_file + alias cp_file copy_file -protected + protected def _append_file_powershell(file_name, data) _write_file_powershell(file_name, data, true) @@ -630,14 +640,14 @@ protected end def _write_file_powershell_fragment(file_name, data, offset, chunk_size, append = false) - chunk = data[offset..offset+chunk_size] - length = chunk.length - compressed_chunk = Rex::Text.gzip(chunk) - encoded_chunk = Base64.strict_encode64(compressed_chunk) + chunk = data[offset..offset + chunk_size] + length = chunk.length + compressed_chunk = Rex::Text.gzip(chunk) + encoded_chunk = Base64.strict_encode64(compressed_chunk) if offset > 0 || append - file_mode = "Append" + file_mode = 'Append' else - file_mode = "Create" + file_mode = 'Create' end pwsh_code = %($encoded=\"#{encoded_chunk}\"; $mstream = [System.IO.MemoryStream]::new([System.Convert]::FromBase64String($encoded)); @@ -659,6 +669,7 @@ protected loop do chunk = _read_file_powershell_fragment(filename, chunk_size, offset) break if chunk.nil? + data << chunk offset += chunk_size break if chunk.length < chunk_size @@ -666,17 +677,17 @@ protected return data end - def _read_file_powershell_fragment(filename, chunk_size, offset=0) - b64_data= cmd_exec("$mstream = [System.IO.MemoryStream]::new();\ + def _read_file_powershell_fragment(filename, chunk_size, offset = 0) + b64_data = cmd_exec("$mstream = [System.IO.MemoryStream]::new();\ $gzipstream = [System.IO.Compression.GZipStream]::new($mstream, [System.IO.Compression.CompressionMode]::Compress);\ - $get_bytes = [System.IO.File]::ReadAllBytes(\"#{filename}\")[#{offset}..#{offset + chunk_size -1}];\ + $get_bytes = [System.IO.File]::ReadAllBytes(\"#{filename}\")[#{offset}..#{offset + chunk_size - 1}];\ $gzipstream.Write($get_bytes, 0 , $get_bytes.Length);\ $gzipstream.Close();\ [Convert]::ToBase64String($mstream.ToArray())") return nil if b64_data.empty? + uncompressed_fragment = Zlib::GzipReader.new(StringIO.new(Base64.decode64(b64_data))).read return uncompressed_fragment - end # Checks to see if there are non-ansi or newline characters in a given string @@ -701,12 +712,10 @@ protected # # @return [String] def _read_file_meterpreter(file_name) - fd = session.fs.file.new(file_name, "rb") + fd = session.fs.file.new(file_name, 'rb') data = fd.read - until fd.eof? - data << fd.read - end + data << fd.read until fd.eof? data rescue EOFError @@ -718,6 +727,7 @@ protected ensure fd.close if fd end + # Windows ANSI file write for shell sessions. Writes given object content to a remote file. # # NOTE: *This is not binary-safe on Windows shell sessions!* @@ -728,7 +738,7 @@ protected # @return [void] def _win_ansi_write_file(file_name, data, chunk_size = 5000) start_index = 0 - write_length =[chunk_size, data.length].min + write_length = [chunk_size, data.length].min session.shell_command_token("echo | set /p=\"#{data[0, write_length]}\"> \"#{file_name}\"") if data.length > write_length # just use append to finish the rest @@ -746,14 +756,14 @@ protected # @return [void] def _win_ansi_append_file(file_name, data, chunk_size = 5000) start_index = 0 - write_length =[chunk_size, data.length].min + write_length = [chunk_size, data.length].min while start_index < data.length begin session.shell_command_token("> \"#{file_name}\"") - start_index = start_index + write_length + start_index += write_length write_length = [chunk_size, data.length - start_index].min rescue ::Exception => e - print_error("Exception while running #{__method__.to_s}: #{e.to_s}") + print_error("Exception while running #{__method__}: #{e}") file_rm(file_name) end end @@ -772,7 +782,7 @@ protected _win_ansi_write_file(b64_filename, b64_data, chunk_size) cmd_exec("certutil -decode #{b64_filename} #{file_name}") rescue ::Exception => e - print_error("Exception while running #{__method__.to_s}: #{e.to_s}") + print_error("Exception while running #{__method__}: #{e}") ensure file_rm(b64_filename) end @@ -793,14 +803,13 @@ protected cmd_exec("certutil -decode #{b64_filename} #{tmp_filename}") cmd_exec("copy /b #{file_name}+#{tmp_filename} #{file_name}") rescue ::Exception => e - print_error("Exception while running #{__method__.to_s}: #{e.to_s}") + print_error("Exception while running #{__method__}: #{e}") ensure file_rm(b64_filename) file_rm(tmp_filename) end end - # # Write +data+ to the remote file +file_name+. # @@ -811,23 +820,23 @@ protected # session. # # @return [void] - def _write_file_unix_shell(file_name, data, append=false) - redirect = (append ? ">>" : ">") + def _write_file_unix_shell(file_name, data, append = false) + redirect = (append ? '>>' : '>') # Short-circuit an empty string. The : builtin is part of posix # standard and should theoretically exist everywhere. - if data.length == 0 + if data.empty? session.shell_command_token(": #{redirect} #{file_name}") return end d = data.dup - d.force_encoding("binary") if d.respond_to? :force_encoding + d.force_encoding('binary') if d.respond_to? :force_encoding chunks = [] command = nil encoding = :hex - cmd_name = "" + cmd_name = '' line_max = _unix_max_line_length # Leave plenty of room for the filename we're writing to and the @@ -847,40 +856,40 @@ protected # first. # # Both of these work for sure on Linux and FreeBSD - { :cmd => %q^/usr/bin/printf 'CONTENTS'^ , :enc => :octal, :name => "printf" }, - { :cmd => %q^printf 'CONTENTS'^ , :enc => :octal, :name => "printf" }, + { cmd: %q{/usr/bin/printf 'CONTENTS'}, enc: :octal, name: 'printf' }, + { cmd: %q{printf 'CONTENTS'}, enc: :octal, name: 'printf' }, # Works on Solaris - { :cmd => %q^/usr/bin/printf %b 'CONTENTS'^ , :enc => :octal, :name => "printf" }, - { :cmd => %q^printf %b 'CONTENTS'^ , :enc => :octal, :name => "printf" }, + { cmd: %q{/usr/bin/printf %b 'CONTENTS'}, enc: :octal, name: 'printf' }, + { cmd: %q{printf %b 'CONTENTS'}, enc: :octal, name: 'printf' }, # Perl supports both octal and hex escapes, but octal is usually # shorter (e.g. 0 becomes \0 instead of \x00) - { :cmd => %q^perl -e 'print("CONTENTS")'^ , :enc => :octal, :name => "perl" }, + { cmd: %q{perl -e 'print("CONTENTS")'}, enc: :octal, name: 'perl' }, # POSIX awk doesn't have \xNN escapes, use gawk to ensure we're # getting the GNU version. - { :cmd => %q^gawk 'BEGIN {ORS="";print "CONTENTS"}' :hex, :name => "awk" }, + { cmd: %q^gawk 'BEGIN {ORS="";print "CONTENTS"}' %q^echo 'CONTENTS'|xxd -p -r^ , :enc => :bare_hex, :name => "xxd" }, + { cmd: %q{echo 'CONTENTS'|xxd -p -r}, enc: :bare_hex, name: 'xxd' }, # Use echo as a last resort since it frequently doesn't support -e # or -n. bash and zsh's echo builtins are apparently the only ones # that support both. Most others treat all options as just more # arguments to print. In particular, the standalone /bin/echo or # /usr/bin/echo appear never to have -e so don't bother trying # them. - { :cmd => %q^echo -ne 'CONTENTS'^ , :enc => :hex }, - ].each { |foo| + { cmd: %q{echo -ne 'CONTENTS'}, enc: :hex }, + ].each do |foo| # Some versions of printf mangle %. test_str = "\0\xff\xfe#{Rex::Text.rand_text_alpha_upper(4)}\x7f%%\r\n" - #test_str = "\0\xff\xfe" + # test_str = "\0\xff\xfe" case foo[:enc] when :hex - cmd = foo[:cmd].sub("CONTENTS"){ Rex::Text.to_hex(test_str) } + cmd = foo[:cmd].sub('CONTENTS') { Rex::Text.to_hex(test_str) } when :octal - cmd = foo[:cmd].sub("CONTENTS"){ Rex::Text.to_octal(test_str) } + cmd = foo[:cmd].sub('CONTENTS') { Rex::Text.to_octal(test_str) } when :bare_hex - cmd = foo[:cmd].sub("CONTENTS"){ Rex::Text.to_hex(test_str,'') } + cmd = foo[:cmd].sub('CONTENTS') { Rex::Text.to_hex(test_str, '') } end - a = session.shell_command_token("#{cmd}") + a = session.shell_command_token(cmd.to_s) if test_str == a command = foo[:cmd] @@ -890,7 +899,7 @@ protected else vprint_status("#{cmd} Failed: #{a.inspect} != #{test_str.inspect}") end - } + end if command.nil? raise RuntimeError, "Can't find command on the victim for writing binary data", caller @@ -898,18 +907,18 @@ protected # each byte will balloon up to 4 when we encode # (A becomes \x41 or \101) - max = line_max/4 + max = line_max / 4 i = 0 while (i < d.length) - slice = d.slice(i...(i+max)) + slice = d.slice(i...(i + max)) case encoding when :hex chunks << Rex::Text.to_hex(slice) when :octal chunks << Rex::Text.to_octal(slice) when :bare_hex - chunks << Rex::Text.to_hex(slice,'') + chunks << Rex::Text.to_hex(slice, '') end i += max end @@ -918,17 +927,17 @@ protected # The first command needs to use the provided redirection for either # appending or truncating. - cmd = command.sub("CONTENTS") { chunks.shift } + cmd = command.sub('CONTENTS') { chunks.shift } session.shell_command_token("#{cmd} #{redirect} \"#{file_name}\"") # After creating/truncating or appending with the first command, we # need to append from here on out. - chunks.each { |chunk| + chunks.each do |chunk| vprint_status("Next chunk is #{chunk.length} bytes") - cmd = command.sub("CONTENTS") { chunk } + cmd = command.sub('CONTENTS') { chunk } session.shell_command_token("#{cmd} >> '#{file_name}'") - } + end true end @@ -963,11 +972,11 @@ protected else raise NotImplementedError if session.platform == 'windows' raise "`stat' command doesn't exist on target system" unless command_exists?('stat') + return FileStat.new(filename, session) end end - class FileStat < Rex::Post::FileStat attr_accessor :stathash @@ -975,7 +984,8 @@ protected def initialize(filename, session) data = session.shell_command_token("stat --format='%d,%i,%h,%u,%g,%t,%s,%B,%o,%X,%Y,%Z,%f' '#{filename}'").to_s.chomp raise 'format argument of stat command not behaving as expected' unless data =~ /(\d+,){12}\w+/ - data = data.split(",") + + data = data.split(',') @stathash = Hash.new @stathash['st_dev'] = data[0].to_i @stathash['st_ino'] = data[1].to_i @@ -989,8 +999,7 @@ protected @stathash['st_atime'] = data[9].to_i @stathash['st_mtime'] = data[10].to_i @stathash['st_ctime'] = data[11].to_i - @stathash['st_mode'] = data[12].to_i(16) #stat command returns hex value of mode" + @stathash['st_mode'] = data[12].to_i(16) # stat command returns hex value of mode" end end - end diff --git a/test/modules/post/test/file.rb b/test/modules/post/test/file.rb index a60c4cd350..64c65f135e 100644 --- a/test/modules/post/test/file.rb +++ b/test/modules/post/test/file.rb @@ -1,11 +1,10 @@ - -lib = File.join(Msf::Config.install_root, "test", "lib") -$:.push(lib) unless $:.include?(lib) +lib = File.join(Msf::Config.install_root, 'test', 'lib') +$LOAD_PATH.push(lib) unless $LOAD_PATH.include?(lib) require 'module_test' -#load 'test/lib/module_test.rb' -#load 'lib/rex/text.rb' -#load 'lib/msf/core/post/file.rb' +# load 'test/lib/module_test.rb' +# load 'lib/rex/text.rb' +# load 'lib/msf/core/post/file.rb' class MetasploitModule < Msf::Post @@ -13,20 +12,24 @@ class MetasploitModule < Msf::Post include Msf::Post::Common include Msf::Post::File - def initialize(info={}) - super( update_info( info, - 'Name' => 'Testing Remote File Manipulation', - 'Description' => %q{ This module will test Post::File API methods }, - 'License' => MSF_LICENSE, - 'Author' => [ 'egypt'], - 'Platform' => [ 'windows', 'linux', 'java' ], - 'SessionTypes' => [ 'meterpreter', 'shell' ] - )) + def initialize(info = {}) + super( + update_info( + info, + 'Name' => 'Testing Remote File Manipulation', + 'Description' => %q{ This module will test Post::File API methods }, + 'License' => MSF_LICENSE, + 'Author' => [ 'egypt'], + 'Platform' => [ 'windows', 'linux', 'java' ], + 'SessionTypes' => [ 'meterpreter', 'shell' ] + ) + ) register_options( [ - OptString.new("BaseFileName" , [true, "File name to create", "meterpreter-test"]) - ], self.class) + OptString.new('BaseFileName', [true, 'File name to create', 'meterpreter-test']) + ], self.class + ) end # @@ -36,7 +39,7 @@ class MetasploitModule < Msf::Post # def setup @old_pwd = pwd - tmp = (directory?("/tmp")) ? "/tmp" : "%TEMP%" + tmp = directory?('/tmp') ? '/tmp' : '%TEMP%' vprint_status("Setup: changing working directory to #{tmp}") cd(tmp) @@ -44,45 +47,45 @@ class MetasploitModule < Msf::Post end def test_file - it "should test for file existence" do + it 'should test for file existence' do ret = false [ - "c:\\boot.ini", - "c:\\pagefile.sys", - "/etc/passwd", - "/etc/master.passwd", - "%WINDIR%\\system32\\notepad.exe", - "%WINDIR%\\system32\\calc.exe" - ].each { |path| + 'c:\\boot.ini', + 'c:\\pagefile.sys', + '/etc/passwd', + '/etc/master.passwd', + '%WINDIR%\\system32\\notepad.exe', + '%WINDIR%\\system32\\calc.exe' + ].each do |path| ret = true if file?(path) - } + end ret end - it "should test for directory existence" do + it 'should test for directory existence' do ret = false [ - "c:\\", - "/etc/", - "/tmp" - ].each { |path| + 'c:\\', + '/etc/', + '/tmp' + ].each do |path| ret = true if directory?(path) - } + end ret end - it "should create text files" do - rm_f(datastore["BaseFileName"]) - write_file(datastore["BaseFileName"], "foo") + it 'should create text files' do + rm_f(datastore['BaseFileName']) + write_file(datastore['BaseFileName'], 'foo') - file?(datastore["BaseFileName"]) + file?(datastore['BaseFileName']) end - it "should read the text we just wrote" do - f = read_file(datastore["BaseFileName"]) - ret = ("foo" == f) + it 'should read the text we just wrote' do + f = read_file(datastore['BaseFileName']) + ret = (f == 'foo') unless ret print_error("Didn't read what we wrote, actual file on target: |#{f}|") end @@ -90,14 +93,14 @@ class MetasploitModule < Msf::Post ret end - it "should append text files" do + it 'should append text files' do ret = true - append_file(datastore["BaseFileName"], "bar") + append_file(datastore['BaseFileName'], 'bar') - ret &&= read_file(datastore["BaseFileName"]) == "foobar" - append_file(datastore["BaseFileName"], "baz") - final_contents = read_file(datastore["BaseFileName"]) - ret &&= final_contents == "foobarbaz" + ret &&= read_file(datastore['BaseFileName']) == 'foobar' + append_file(datastore['BaseFileName'], 'baz') + final_contents = read_file(datastore['BaseFileName']) + ret &&= final_contents == 'foobarbaz' unless ret print_error("Didn't read what we wrote, actual file on target: #{final_contents}") end @@ -105,68 +108,81 @@ class MetasploitModule < Msf::Post ret end - it "should delete text files" do - rm_f(datastore["BaseFileName"]) + it 'should delete text files' do + rm_f(datastore['BaseFileName']) - not file_exist?(datastore["BaseFileName"]) + !file_exist?(datastore['BaseFileName']) end - it "should move files" do - # Make sure we don't have leftovers from a previous run - moved_file = datastore["BaseFileName"] + "-moved" - rm _f(datastore["BaseFileName"]) rescue nil - rm_f(moved_file) rescue nil + it 'should move files' do + # Make sure we don't have leftovers from a previous run + moved_file = datastore['BaseFileName'] + '-moved' + begin + rm _f(datastore['BaseFileName']) + rescue StandardError + nil + end + begin + rm_f(moved_file) + rescue StandardError + nil + end - # touch a new file - write_file(datastore["BaseFileName"], "") + # touch a new file + write_file(datastore['BaseFileName'], '') - rename_file(datastore["BaseFileName"], moved_file) - res &&= exist?(moved_file) - res &&= !exist?(datastore["BaseFileName"]) + rename_file(datastore['BaseFileName'], moved_file) + res &&= exist?(moved_file) + res &&= !exist?(datastore['BaseFileName']) - # clean up - rm_f(datastore["BaseFileName"]) rescue nil - rm_f(moved_file) rescue nil + # clean up + begin + rm_f(datastore['BaseFileName']) + rescue StandardError + nil + end + begin + rm_f(moved_file) + rescue StandardError + nil + end end - end def test_binary_files - - #binary_data = ::File.read("/bin/ls") - binary_data = ::File.read("/bin/echo") - #binary_data = "\xff\x00\xff\xfe\xff\`$(echo blha)\`" - it "should write binary data" do + # binary_data = ::File.read("/bin/ls") + binary_data = ::File.read('/bin/echo') + # binary_data = "\xff\x00\xff\xfe\xff\`$(echo blha)\`" + it 'should write binary data' do vprint_status "Writing #{binary_data.length} bytes" t = Time.now - write_file(datastore["BaseFileName"], binary_data) + write_file(datastore['BaseFileName'], binary_data) vprint_status("Finished in #{Time.now - t}") - file_exist?(datastore["BaseFileName"]) + file_exist?(datastore['BaseFileName']) end - it "should read the binary data we just wrote" do - bin = read_file(datastore["BaseFileName"]) + it 'should read the binary data we just wrote' do + bin = read_file(datastore['BaseFileName']) vprint_status "Read #{bin.length} bytes" bin == binary_data end - it "should delete binary files" do - rm_f(datastore["BaseFileName"]) + it 'should delete binary files' do + rm_f(datastore['BaseFileName']) - not file_exist?(datastore["BaseFileName"]) + !file_exist?(datastore['BaseFileName']) end - it "should append binary data" do - write_file(datastore["BaseFileName"], "\xde\xad") - append_file(datastore["BaseFileName"], "\xbe\xef") - bin = read_file(datastore["BaseFileName"]) - rm_f(datastore["BaseFileName"]) + it 'should append binary data' do + write_file(datastore['BaseFileName'], "\xde\xad") + append_file(datastore['BaseFileName'], "\xbe\xef") + bin = read_file(datastore['BaseFileName']) + rm_f(datastore['BaseFileName']) bin == "\xde\xad\xbe\xef" end - end def cleanup @@ -176,4 +192,3 @@ class MetasploitModule < Msf::Post end end -