Land #15441, add date filtering to stdapi_fs_search
This commit is contained in:
commit
4289c8b3ea
|
@ -29,9 +29,9 @@ PATH
|
|||
metasploit-concern
|
||||
metasploit-credential
|
||||
metasploit-model
|
||||
metasploit-payloads (= 2.0.56)
|
||||
metasploit-payloads (= 2.0.57)
|
||||
metasploit_data_models
|
||||
metasploit_payloads-mettle (= 1.0.12)
|
||||
metasploit_payloads-mettle (= 1.0.13)
|
||||
mqtt
|
||||
msgpack
|
||||
nessus_rest
|
||||
|
@ -256,7 +256,7 @@ GEM
|
|||
activemodel (~> 6.0)
|
||||
activesupport (~> 6.0)
|
||||
railties (~> 6.0)
|
||||
metasploit-payloads (2.0.56)
|
||||
metasploit-payloads (2.0.57)
|
||||
metasploit_data_models (5.0.4)
|
||||
activerecord (~> 6.0)
|
||||
activesupport (~> 6.0)
|
||||
|
@ -267,7 +267,7 @@ GEM
|
|||
railties (~> 6.0)
|
||||
recog (~> 2.0)
|
||||
webrick
|
||||
metasploit_payloads-mettle (1.0.12)
|
||||
metasploit_payloads-mettle (1.0.13)
|
||||
method_source (1.0.0)
|
||||
mini_portile2 (2.6.1)
|
||||
minitest (5.14.4)
|
||||
|
|
|
@ -77,9 +77,9 @@ metasploit-concern, 4.0.3, "New BSD"
|
|||
metasploit-credential, 5.0.4, "New BSD"
|
||||
metasploit-framework, 6.1.8, "New BSD"
|
||||
metasploit-model, 4.0.3, "New BSD"
|
||||
metasploit-payloads, 2.0.56, "3-clause (or ""modified"") BSD"
|
||||
metasploit-payloads, 2.0.57, "3-clause (or ""modified"") BSD"
|
||||
metasploit_data_models, 5.0.4, "New BSD"
|
||||
metasploit_payloads-mettle, 1.0.12, "3-clause (or ""modified"") BSD"
|
||||
metasploit_payloads-mettle, 1.0.13, "3-clause (or ""modified"") BSD"
|
||||
method_source, 1.0.0, MIT
|
||||
mini_portile2, 2.6.1, MIT
|
||||
minitest, 5.14.4, MIT
|
||||
|
|
|
@ -75,7 +75,7 @@ class File < Rex::Post::Meterpreter::Extensions::Stdapi::Fs::IO
|
|||
#
|
||||
# Raises a RequestError if +root+ is not a directory.
|
||||
#
|
||||
def File.search( root=nil, glob="*.*", recurse=true, timeout=-1 )
|
||||
def File.search( root=nil, glob="*.*", recurse=true, timeout=-1, modified_start_date=nil, modified_end_date=nil)
|
||||
|
||||
files = ::Array.new
|
||||
|
||||
|
@ -87,6 +87,8 @@ class File < Rex::Post::Meterpreter::Extensions::Stdapi::Fs::IO
|
|||
request.add_tlv( TLV_TYPE_SEARCH_ROOT, root )
|
||||
request.add_tlv( TLV_TYPE_SEARCH_GLOB, glob )
|
||||
request.add_tlv( TLV_TYPE_SEARCH_RECURSE, recurse )
|
||||
request.add_tlv( TLV_TYPE_SEARCH_M_START_DATE, modified_start_date) if modified_start_date
|
||||
request.add_tlv( TLV_TYPE_SEARCH_M_END_DATE, modified_end_date) if modified_end_date
|
||||
|
||||
# we set the response timeout to -1 to wait indefinitely as a
|
||||
# search could take an indeterminate amount of time to complete.
|
||||
|
@ -96,7 +98,8 @@ class File < Rex::Post::Meterpreter::Extensions::Stdapi::Fs::IO
|
|||
files << {
|
||||
'path' => client.unicode_filter_encode(results.get_tlv_value(TLV_TYPE_FILE_PATH).chomp( self.separator )),
|
||||
'name' => client.unicode_filter_encode(results.get_tlv_value(TLV_TYPE_FILE_NAME)),
|
||||
'size' => results.get_tlv_value(TLV_TYPE_FILE_SIZE)
|
||||
'size' => results.get_tlv_value(TLV_TYPE_FILE_SIZE),
|
||||
'mtime'=> results.get_tlv_value(TLV_TYPE_SEARCH_MTIME)
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
|
@ -47,6 +47,10 @@ TLV_TYPE_SEARCH_RECURSE = TLV_META_TYPE_BOOL | 1230
|
|||
TLV_TYPE_SEARCH_GLOB = TLV_META_TYPE_STRING | 1231
|
||||
TLV_TYPE_SEARCH_ROOT = TLV_META_TYPE_STRING | 1232
|
||||
TLV_TYPE_SEARCH_RESULTS = TLV_META_TYPE_GROUP | 1233
|
||||
TLV_TYPE_SEARCH_MTIME = TLV_META_TYPE_UINT | 1235
|
||||
TLV_TYPE_SEARCH_M_START_DATE= TLV_META_TYPE_UINT | 1236
|
||||
TLV_TYPE_SEARCH_M_END_DATE = TLV_META_TYPE_UINT | 1237
|
||||
|
||||
|
||||
TLV_TYPE_FILE_MODE_T = TLV_META_TYPE_UINT | 1234
|
||||
##
|
||||
|
|
|
@ -134,6 +134,14 @@ class Console::CommandDispatcher::Stdapi::Fs
|
|||
"Stdapi: File system"
|
||||
end
|
||||
|
||||
def vali_date(str)
|
||||
result = DateTime.parse(str)
|
||||
return result.to_time.to_i
|
||||
rescue
|
||||
print_error("Bad date/time specification (#{str}). Use this format: \"YYYY-mm-dd\" or \"YYYY-mm-ddTHH:MM:SS\", e.g \"1970-01-01\"")
|
||||
nil
|
||||
end
|
||||
|
||||
#
|
||||
# Search for files.
|
||||
#
|
||||
|
@ -143,12 +151,16 @@ class Console::CommandDispatcher::Stdapi::Fs
|
|||
recurse = true
|
||||
globs = []
|
||||
files = []
|
||||
modified_start_date = nil
|
||||
modified_end_date = nil
|
||||
|
||||
opts = Rex::Parser::Arguments.new(
|
||||
"-h" => [ false, "Help Banner" ],
|
||||
"-d" => [ true, "The directory/drive to begin searching from. Leave empty to search all drives. (Default: #{root})" ],
|
||||
"-f" => [ true, "A file pattern glob to search for. (e.g. *secret*.doc?)" ],
|
||||
"-r" => [ true, "Recursively search sub directories. (Default: #{recurse})" ]
|
||||
"-r" => [ true, "Recursively search sub directories. (Default: #{recurse})" ],
|
||||
"-a" => [ true, "Find files modified after timestamp (UTC). Format: YYYY-mm-dd or YYYY-mm-ddTHH:MM:SS"],
|
||||
"-b" => [ true, "Find files modified before timestamp (UTC). Format: YYYY-mm-dd or YYYY-mm-ddTHH:MM:SS"]
|
||||
)
|
||||
|
||||
opts.parse(args) { | opt, idx, val |
|
||||
|
@ -164,6 +176,12 @@ class Console::CommandDispatcher::Stdapi::Fs
|
|||
globs << val
|
||||
when "-r"
|
||||
recurse = false if val =~ /^(f|n|0)/i
|
||||
when "-a"
|
||||
modified_start_date = vali_date(val)
|
||||
return unless modified_start_date
|
||||
when "-b"
|
||||
modified_end_date = vali_date(val)
|
||||
return unless modified_end_date
|
||||
end
|
||||
}
|
||||
|
||||
|
@ -173,7 +191,7 @@ class Console::CommandDispatcher::Stdapi::Fs
|
|||
end
|
||||
|
||||
globs.uniq.each do |glob|
|
||||
files += client.fs.file.search(root, glob, recurse)
|
||||
files += client.fs.file.search(root, glob, recurse, -1, modified_start_date, modified_end_date)
|
||||
end
|
||||
|
||||
if files.empty?
|
||||
|
@ -181,15 +199,27 @@ class Console::CommandDispatcher::Stdapi::Fs
|
|||
return
|
||||
end
|
||||
|
||||
print_line("Found #{files.length} result#{ files.length > 1 ? 's' : '' }...")
|
||||
files.each do | file |
|
||||
if file['size'] > 0
|
||||
print(" #{file['path']}#{ file['path'].empty? ? '' : client.fs.file.separator }#{file['name']} (#{file['size']} bytes)\n")
|
||||
else
|
||||
print(" #{file['path']}#{ file['path'].empty? ? '' : client.fs.file.separator }#{file['name']}\n")
|
||||
end
|
||||
end
|
||||
header = "Found #{files.length} result#{ files.length > 1 ? 's' : '' }..."
|
||||
results_table = Rex::Text::Table.new(
|
||||
'WordWrap' => false,
|
||||
'Width' => 120,
|
||||
'Header' => header,
|
||||
'Indent' => 0,
|
||||
'SortIndex' => 0,
|
||||
'Columns' => ['Path', 'Size (bytes)', 'Modified (UTC)'],
|
||||
)
|
||||
|
||||
files.each do | file |
|
||||
filestr = ''
|
||||
unless file['path'].empty?
|
||||
filestr += "#{file['path']}#{client.fs.file.separator}"
|
||||
end
|
||||
filestr += file['name']
|
||||
datestr = ''
|
||||
datestr = Time.at(file['mtime']).to_s if file['mtime']
|
||||
results_table << [filestr, file['size'], datestr]
|
||||
end
|
||||
print_line results_table.to_s
|
||||
end
|
||||
|
||||
#
|
||||
|
|
|
@ -70,9 +70,9 @@ Gem::Specification.new do |spec|
|
|||
# are needed when there's no database
|
||||
spec.add_runtime_dependency 'metasploit-model'
|
||||
# Needed for Meterpreter
|
||||
spec.add_runtime_dependency 'metasploit-payloads', '2.0.56'
|
||||
spec.add_runtime_dependency 'metasploit-payloads', '2.0.57'
|
||||
# Needed for the next-generation POSIX Meterpreter
|
||||
spec.add_runtime_dependency 'metasploit_payloads-mettle', '1.0.12'
|
||||
spec.add_runtime_dependency 'metasploit_payloads-mettle', '1.0.13'
|
||||
# Needed by msfgui and other rpc components
|
||||
spec.add_runtime_dependency 'msgpack'
|
||||
# get list of network interfaces, like eth* from OS.
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
module MetasploitModule
|
||||
|
||||
CachedSize = 1026792
|
||||
CachedSize = 1027040
|
||||
|
||||
include Msf::Payload::Single
|
||||
include Msf::Sessions::MeterpreterOptions
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
module MetasploitModule
|
||||
|
||||
CachedSize = 1026792
|
||||
CachedSize = 1027040
|
||||
|
||||
include Msf::Payload::Single
|
||||
include Msf::Sessions::MeterpreterOptions
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
module MetasploitModule
|
||||
|
||||
CachedSize = 1026792
|
||||
CachedSize = 1027040
|
||||
|
||||
include Msf::Payload::Single
|
||||
include Msf::Sessions::MeterpreterOptions
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
module MetasploitModule
|
||||
|
||||
CachedSize = 1026920
|
||||
CachedSize = 1027168
|
||||
|
||||
include Msf::Payload::Single
|
||||
include Msf::Sessions::MeterpreterOptions
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
module MetasploitModule
|
||||
|
||||
CachedSize = 1026920
|
||||
CachedSize = 1027168
|
||||
|
||||
include Msf::Payload::Single
|
||||
include Msf::Sessions::MeterpreterOptions
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
module MetasploitModule
|
||||
|
||||
CachedSize = 1026920
|
||||
CachedSize = 1027168
|
||||
|
||||
include Msf::Payload::Single
|
||||
include Msf::Sessions::MeterpreterOptions
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
module MetasploitModule
|
||||
|
||||
CachedSize = 1573904
|
||||
CachedSize = 1573952
|
||||
|
||||
include Msf::Payload::Single
|
||||
include Msf::Sessions::MeterpreterOptions
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
module MetasploitModule
|
||||
|
||||
CachedSize = 1573904
|
||||
CachedSize = 1573952
|
||||
|
||||
include Msf::Payload::Single
|
||||
include Msf::Sessions::MeterpreterOptions
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
module MetasploitModule
|
||||
|
||||
CachedSize = 1573904
|
||||
CachedSize = 1573952
|
||||
|
||||
include Msf::Payload::Single
|
||||
include Msf::Sessions::MeterpreterOptions
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
module MetasploitModule
|
||||
|
||||
CachedSize = 1468324
|
||||
CachedSize = 1468596
|
||||
|
||||
include Msf::Payload::Single
|
||||
include Msf::Sessions::MeterpreterOptions
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
module MetasploitModule
|
||||
|
||||
CachedSize = 1468324
|
||||
CachedSize = 1468596
|
||||
|
||||
include Msf::Payload::Single
|
||||
include Msf::Sessions::MeterpreterOptions
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
module MetasploitModule
|
||||
|
||||
CachedSize = 1468324
|
||||
CachedSize = 1468596
|
||||
|
||||
include Msf::Payload::Single
|
||||
include Msf::Sessions::MeterpreterOptions
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
module MetasploitModule
|
||||
|
||||
CachedSize = 1471264
|
||||
CachedSize = 1471536
|
||||
|
||||
include Msf::Payload::Single
|
||||
include Msf::Sessions::MeterpreterOptions
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
module MetasploitModule
|
||||
|
||||
CachedSize = 1471264
|
||||
CachedSize = 1471536
|
||||
|
||||
include Msf::Payload::Single
|
||||
include Msf::Sessions::MeterpreterOptions
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
module MetasploitModule
|
||||
|
||||
CachedSize = 1471264
|
||||
CachedSize = 1471536
|
||||
|
||||
include Msf::Payload::Single
|
||||
include Msf::Sessions::MeterpreterOptions
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
module MetasploitModule
|
||||
|
||||
CachedSize = 810048
|
||||
CachedSize = 810040
|
||||
|
||||
include Msf::Payload::Single
|
||||
include Msf::Sessions::MeterpreterOptions
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
module MetasploitModule
|
||||
|
||||
CachedSize = 810048
|
||||
CachedSize = 810040
|
||||
|
||||
include Msf::Payload::Single
|
||||
include Msf::Sessions::MeterpreterOptions
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
module MetasploitModule
|
||||
|
||||
CachedSize = 810048
|
||||
CachedSize = 810040
|
||||
|
||||
include Msf::Payload::Single
|
||||
include Msf::Sessions::MeterpreterOptions
|
||||
|
|
|
@ -0,0 +1,188 @@
|
|||
|
||||
require 'rex/post/meterpreter/extensions/stdapi/command_ids'
|
||||
require 'rex'
|
||||
|
||||
lib = File.join(Msf::Config.install_root, "test", "lib")
|
||||
$:.push(lib) unless $:.include?(lib)
|
||||
require 'module_test'
|
||||
|
||||
class MetasploitModule < Msf::Post
|
||||
|
||||
include Msf::ModuleTest::PostTest
|
||||
|
||||
def initialize(info={})
|
||||
super( update_info( info,
|
||||
'Name' => 'Testing Meterpreter Search',
|
||||
'Description' => %q{ This module will test the meterpreter search method },
|
||||
'License' => MSF_LICENSE,
|
||||
'Author' => [ 'timwr'],
|
||||
'Platform' => [ 'windows', 'linux', 'java' ],
|
||||
'SessionTypes' => [ 'meterpreter' ]
|
||||
))
|
||||
register_options(
|
||||
[
|
||||
OptBool.new("AddEntropy" , [false, "Add entropy token to file and directory names.", false]),
|
||||
OptString.new("BaseFileName" , [true, "File/dir base name", "meterpreter-test"])
|
||||
], self.class)
|
||||
end
|
||||
|
||||
def setup
|
||||
@old_pwd = session.fs.dir.getwd
|
||||
stat = session.fs.file.stat("/tmp") rescue nil
|
||||
if (stat and stat.directory?)
|
||||
tmp = "/tmp"
|
||||
else
|
||||
tmp = session.sys.config.getenv('TEMP')
|
||||
end
|
||||
vprint_status("Setup: changing working directory to #{tmp}")
|
||||
session.fs.dir.chdir(tmp)
|
||||
|
||||
if datastore["AddEntropy"]
|
||||
entropy_value = '-' + ('a'..'z').to_a.shuffle[0,8].join
|
||||
else
|
||||
entropy_value = ""
|
||||
end
|
||||
|
||||
@file_name = "#{datastore["BaseFileName"]}#{entropy_value}.txt"
|
||||
vprint_status("Source File Name: #{@file_name}")
|
||||
|
||||
session.fs.file.rm(@file_name) rescue nil
|
||||
fd = session.fs.file.open(@file_name, "wb")
|
||||
fd.close
|
||||
super
|
||||
end
|
||||
|
||||
def test_fs_search
|
||||
vprint_status("Starting search tests")
|
||||
|
||||
pwd = session.fs.dir.getwd
|
||||
|
||||
it "should search for new files" do
|
||||
res = true
|
||||
found = false
|
||||
files = client.fs.file.search(pwd, "*", false)
|
||||
files.each do |file|
|
||||
if file['name'] == @file_name
|
||||
res &&= (found == false)
|
||||
found = true
|
||||
res &&= (file['path'] == pwd)
|
||||
end
|
||||
end
|
||||
res &&= found
|
||||
res
|
||||
end
|
||||
|
||||
it "should search recursively for files" do
|
||||
res = true
|
||||
found = false
|
||||
files = client.fs.file.search(pwd, "*", true)
|
||||
files.each do |file|
|
||||
if file['name'] == @file_name
|
||||
res &&= (found == false)
|
||||
found = true
|
||||
res &&= (file['path'] == pwd)
|
||||
end
|
||||
end
|
||||
res &&= found
|
||||
res
|
||||
end
|
||||
|
||||
it "should search with globs for files" do
|
||||
res = true
|
||||
found = false
|
||||
files = client.fs.file.search(pwd, "*.txt", true)
|
||||
files.each do |file|
|
||||
if file['name'] == @file_name
|
||||
res &&= (found == false)
|
||||
found = true
|
||||
res &&= (file['path'] == pwd)
|
||||
end
|
||||
end
|
||||
res &&= found
|
||||
res
|
||||
end
|
||||
|
||||
it "should search with globs ignoring files" do
|
||||
res = true
|
||||
files = client.fs.file.search(pwd, "*.ignoretxt", true)
|
||||
files.each do |file|
|
||||
res = false
|
||||
end
|
||||
res
|
||||
end
|
||||
end
|
||||
|
||||
def test_fs_search_date
|
||||
vprint_status("Starting search date tests")
|
||||
|
||||
pwd = session.fs.dir.getwd
|
||||
|
||||
yesterday = (Time.now - 1.week).to_i
|
||||
tomorrow = (Time.now + 1.week).to_i
|
||||
it "should search with dates for files" do
|
||||
res = true
|
||||
found = false
|
||||
files = client.fs.file.search(pwd, "*", true, -1, yesterday, tomorrow)
|
||||
files.each do |file|
|
||||
if file['name'] == @file_name
|
||||
res &&= (found == false)
|
||||
res &&= (file['path'] == pwd)
|
||||
res &&= (file['mtime'] > yesterday)
|
||||
res &&= (file['mtime'] < tomorrow)
|
||||
found = true
|
||||
end
|
||||
end
|
||||
res &&= found
|
||||
res
|
||||
end
|
||||
|
||||
it "should search with dates ignores new files" do
|
||||
res = true
|
||||
files = client.fs.file.search(pwd, "*", true, -1, tomorrow, nil)
|
||||
files.each do |file|
|
||||
res = false if file['name'] == @file_name
|
||||
end
|
||||
res
|
||||
end
|
||||
|
||||
it "should search with dates ignores old files" do
|
||||
res = true
|
||||
files = client.fs.file.search(pwd, "*", true, -1, nil, yesterday)
|
||||
files.each do |file|
|
||||
res = false if file['name'] == @file_name
|
||||
end
|
||||
res
|
||||
end
|
||||
|
||||
genesis_date = "3 January 2009 18:15:13 +0000"
|
||||
genesis = DateTime.parse(genesis_date).to_i
|
||||
|
||||
if not ['windows', 'win'].include? session.platform
|
||||
cmd_exec("touch -d '#{genesis_date}' #{@file_name}")
|
||||
elsif session.priv.present?
|
||||
client.priv.fs.set_file_mace(@file_name, genesis)
|
||||
else
|
||||
vprint_status("Session does not support setting the modified date, skipping exact date tests")
|
||||
return
|
||||
end
|
||||
|
||||
it "should search with date inclusive of exact date" do
|
||||
res = false
|
||||
files = client.fs.file.search(pwd, "*", true, -1, genesis, genesis)
|
||||
files.each do |file|
|
||||
if file['name'] == @file_name
|
||||
res = (file['mtime'] == genesis)
|
||||
end
|
||||
end
|
||||
res
|
||||
end
|
||||
end
|
||||
|
||||
def cleanup
|
||||
session.fs.file.rm(@file_name) rescue nil
|
||||
vprint_status("Cleanup: changing working directory back to #{@old_pwd}")
|
||||
session.fs.dir.chdir(@old_pwd)
|
||||
super
|
||||
end
|
||||
|
||||
end
|
Loading…
Reference in New Issue