Land #3738, SMBServer file descriptor updates

bug/bundler_fix
Tod Beardsley 2014-10-20 12:40:43 -05:00
commit 85f48a3fb2
No known key found for this signature in database
GPG Key ID: 1EFFB682ADB9F193
1 changed files with 89 additions and 4 deletions

View File

@ -661,6 +661,16 @@ module Exploit::Remote::SMBServer
[
OptPort.new('SRVPORT', [ true, "The local port to listen on.", 445 ])
], self.class)
register_advanced_options(
[
OptInt.new('SMBServerMaximumBuffer', [ true, "The maximum number of data in megabytes to buffer", 2 ]),
OptInt.new('SMBServerIdleTimeout', [ true, "The maximum amount of time to keep an idle session open in seconds", 120 ]),
], self.class)
@smb_server_last_pool_sweep = Time.now.to_f
@smb_server_pool_mutex = Mutex.new
@smb_server_request_counter = 0
end
def setup
@ -685,16 +695,59 @@ module Exploit::Remote::SMBServer
def smb_conn(c)
@state[c] = {:name => "#{c.peerhost}:#{c.peerport}", :ip => c.peerhost, :port => c.peerport}
smb_pool_update(c)
end
def smb_stop(c)
# Make sure the socket is closed
begin
c.close
# Handle any number of errors that a double-close or failed shutdown can trigger
rescue ::IOError, ::EOFError,
::Errno::ECONNRESET, ::Errno::ENOTCONN, ::Errno::ECONNABORTED,
::Errno::ETIMEDOUT, ::Errno::ENETRESET, ::Errno::ESHUTDOWN
end
# Delete the state table entry
@state.delete(c)
end
def smb_recv(c)
smb = @state[c]
smb[:data] ||= ''
smb[:data] << c.get_once
buff = ''
begin
buff = c.get_once(-1, 0.25)
# Handle any number of errors that a read can trigger depending on socket state
rescue ::IOError, ::EOFError,
::Errno::ECONNRESET, ::Errno::ENOTCONN, ::Errno::ECONNABORTED,
::Errno::ETIMEDOUT, ::Errno::ENETRESET, ::Errno::ESHUTDOWN
vprint_status("Dropping connection from #{smb[:name]} due to exception: #{$!.class} #{$!}")
smb_stop(c)
return
end
# The client said it had data, but lied, kill the session
unless buff and buff.length > 0
vprint_status("Dropping connection from #{smb[:name]} due to empty payload...")
smb_stop(c)
return
end
# Append the new data to the buffer
smb[:data] << buff
# Prevent a simplistic DoS if the buffer is too big
if smb[:data].length > (1024*1024*datastore['SMBServerMaximumBuffer'])
vprint_status("Dropping connection from #{smb[:name]} due to oversized buffer of #{smb[:data].length} bytes...")
smb_stop(c)
return
end
# Update the last-seen timestamp and purge old entries
smb_pool_update(c)
while(smb[:data].length > 0)
@ -736,10 +789,11 @@ module Exploit::Remote::SMBServer
pkt = CONST::SMB_BASE_PKT.make_struct
pkt.from_s(buff)
# Only response to requests, ignore server replies
# Only respond to requests, ignore server replies
if (pkt['Payload']['SMB'].v['Flags1'] & 128 != 0)
print_status("Ignoring server response from #{smb[:name]}")
next
vprint_status("Dropping connection from #{smb[:name]} due to missing client request flag")
smb_stop(c)
return
end
cmd = pkt['Payload']['SMB'].v['Command']
@ -784,6 +838,37 @@ module Exploit::Remote::SMBServer
c.put(pkt.to_s)
end
# Update the last-seen timestamp and purge old entries
def smb_pool_update(c)
@state[c][:last_action] = Time.now.to_f
@smb_server_request_counter += 1
unless @smb_server_request_counter % 100 == 0 ||
@smb_server_last_pool_sweep + datastore['SMBServerIdleTimeout'].to_f < Time.now.to_f
return
end
# Synchronize pool sweeps in case we move to threaded services
@smb_server_pool_mutex.synchronize do
purge_list = []
@smb_server_last_pool_sweep = Time.now.to_f
@state.keys.each do |sc|
if @state[sc][:last_action] + datastore['SMBServerIdleTimeout'].to_f < Time.now.to_f
purge_list << sc
end
end
# Purge any idle connections to rescue file descriptors
purge_list.each do |sc|
vprint_status("Dropping connection from #{@state[sc][:name]} due to idle timeout...")
smb_stop(sc)
end
end
end
end