diff --git a/Gemfile b/Gemfile index 71f179e97b29..99326addc634 100644 --- a/Gemfile +++ b/Gemfile @@ -52,3 +52,6 @@ group :test do # Manipulate Time.now in specs gem 'timecop' end + +# remove after https://github.com/rapid7/ruby_smb/pull/260 is landed +gem 'ruby_smb', git: 'https://github.com/zeroSteiner/ruby_smb', branch: 'feat/server/remove-share' diff --git a/Gemfile.lock b/Gemfile.lock index 8acb80705ea8..4acf2bd31aa9 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,3 +1,15 @@ +GIT + remote: https://github.com/zeroSteiner/ruby_smb + revision: feba8f6592c33c252c6b4e5dd576f2722ada6bf2 + branch: feat/server/remove-share + specs: + ruby_smb (3.3.2) + bindata + openssl-ccm + openssl-cmac + rubyntlm + windows_error (>= 0.1.4) + PATH remote: . specs: @@ -473,12 +485,6 @@ GEM ruby-progressbar (1.13.0) ruby-rc4 (0.1.5) ruby2_keywords (0.0.5) - ruby_smb (3.3.1) - bindata - openssl-ccm - openssl-cmac - rubyntlm - windows_error (>= 0.1.4) rubyntlm (0.6.3) rubyzip (2.3.2) sawyer (0.9.2) @@ -565,6 +571,7 @@ DEPENDENCIES rspec-rerun rubocop ruby-prof (= 1.4.2) + ruby_smb! simplecov (= 0.18.2) test-prof timecop diff --git a/lib/msf/core/exploit/remote/smb/server.rb b/lib/msf/core/exploit/remote/smb/server.rb index 974aef82df32..291ebb075b1e 100644 --- a/lib/msf/core/exploit/remote/smb/server.rb +++ b/lib/msf/core/exploit/remote/smb/server.rb @@ -45,19 +45,6 @@ def start_service(opts = {}) def on_client_connect(client) vprint_status("Received SMB connection from #{client.peerhost}") end - - def cleanup_service - if service - begin - self.service.stop - self.service.wait - true - rescue ::Exception => e - print_error(e.message) - false - end - end - end end end end diff --git a/lib/msf/core/exploit/remote/smb/server/share.rb b/lib/msf/core/exploit/remote/smb/server/share.rb index 12ed30f58513..e55c2341b2dd 100644 --- a/lib/msf/core/exploit/remote/smb/server/share.rb +++ b/lib/msf/core/exploit/remote/smb/server/share.rb @@ -28,9 +28,9 @@ def initialize(info = {}) register_options( [ OptPort.new('SRVPORT', [ true, 'The local port to listen on.', 445 ]), - OptString.new('SHARE', [ false, 'Share (Default Random)']), - OptString.new('FILE_NAME', [ false, 'File name to share (Default Random)']), - OptString.new('FOLDER_NAME', [ false, 'Folder name to share (Default none)']) + OptString.new('SHARE', [ false, 'Share (Default: random); cannot contain spaces or slashes'], regex: /^[^\s\/\\]*$/), + OptString.new('FILE_NAME', [ false, 'File name to share (Default: random)']), + OptString.new('FOLDER_NAME', [ false, 'Folder name to share (Default: none)']) ], Msf::Exploit::Remote::SMB::Server::Share) register_advanced_options( [ @@ -58,12 +58,18 @@ def start_service(opts = {}) super(opts) - virtual_disk = RubySMB::Server::Share::Provider::VirtualDisk.new(@share) - # the virtual disk expects the path to use the native File::SEPARATOR so normalize on that here - virtual_disk.add_dynamic_file("#{@folder_name}#{File::SEPARATOR}#{@file_name}".gsub(/\/|\\/, File::SEPARATOR)) do |client, _smb_session| - get_file_contents(client: client) + if share.present? + if service.shares.key?(share) + fail_with(Msf::Module::Failure::BadConfig, "The specified SMB share '#{share}' already exists.") + end + + virtual_disk = RubySMB::Server::Share::Provider::VirtualDisk.new(share) + # the virtual disk expects the path to use the native File::SEPARATOR so normalize on that here + virtual_disk.add_dynamic_file("#{@folder_name}#{File::SEPARATOR}#{@file_name}".gsub(/\/|\\/, File::SEPARATOR)) do |client, _smb_session| + get_file_contents(client: client) + end + service.add_share(virtual_disk) end - service.add_share(virtual_disk) end # Setups the server configuration. @@ -71,8 +77,8 @@ def setup super self.folder_name = datastore['FOLDER_NAME'] - self.share = datastore['SHARE'] || Rex::Text.rand_text_alpha(4 + rand(3)) - self.file_name = datastore['FILE_NAME'] || Rex::Text.rand_text_alpha(4 + rand(3)) + self.share = datastore['SHARE'].present? ? datastore['SHARE'] : Rex::Text.rand_text_alpha(4 + rand(3)) + self.file_name = datastore['FILE_NAME'].present? ? datastore['FILE_NAME'] : Rex::Text.rand_text_alpha(4 + rand(3)) end # Builds the UNC Name for the shared file @@ -92,6 +98,12 @@ def unc def get_file_contents(client:) file_contents end + + def cleanup + self.service.remove_share(share) if share.present? + + super + end end end end diff --git a/lib/rex/proto/smb/server.rb b/lib/rex/proto/smb/server.rb index 264b5f9f977b..e373ee8994cf 100644 --- a/lib/rex/proto/smb/server.rb +++ b/lib/rex/proto/smb/server.rb @@ -18,7 +18,7 @@ class Server include Proto extend Forwardable - def_delegators :@rubysmb_server, :add_share, :dialects, :guid, :shares + def_delegators :@rubysmb_server, :dialects, :guid, :shares, :add_share, :remove_share def initialize(port = 445, listen_host = '0.0.0.0', context = {}, comm = nil, gss_provider: nil, logger: nil) self.listen_host = listen_host @@ -99,10 +99,6 @@ def wait @listener_thread.join if @listener_thread end - def add_share(share_provider, *arg) - - end - attr_accessor :context, :comm, :listener, :listen_host, :listen_port, :on_client_connect_proc end diff --git a/lib/rex/service_manager.rb b/lib/rex/service_manager.rb index 77510632461a..588021d69513 100644 --- a/lib/rex/service_manager.rb +++ b/lib/rex/service_manager.rb @@ -59,7 +59,7 @@ def start(klass, *args, **kwargs) return inst end - inst = klass.new(*args) + inst = klass.new(*args, **kwargs) als = inst.alias # Find an alias that isn't taken.