simpleclient versions option

GSoC/Meterpreter_Web_Console
Jacob Robles 2018-04-30 08:35:00 -05:00 committed by Brent Cook
parent ff202a5f5b
commit 534d05ff44
5 changed files with 139 additions and 69 deletions

View File

@ -78,7 +78,7 @@ module Msf
# #
# @param (see Exploit::Remote::Tcp#connect) # @param (see Exploit::Remote::Tcp#connect)
# @return (see Exploit::Remote::Tcp#connect) # @return (see Exploit::Remote::Tcp#connect)
def connect(global=true) def connect(global=true, versions: [1])
disconnect() if global disconnect() if global
@ -92,7 +92,7 @@ module Msf
direct = false direct = false
end end
c = SIMPLE.new(s, direct) c = SIMPLE.new(s, direct, versions)
# setup pipe evasion foo # setup pipe evasion foo
if datastore['SMB::pipe_evasion'] if datastore['SMB::pipe_evasion']

View File

@ -28,13 +28,17 @@ EVADE = Rex::Proto::SMB::Evasions
attr_accessor :last_error, :server_max_buffer_size attr_accessor :last_error, :server_max_buffer_size
# Private accessors # Private accessors
attr_accessor :socket, :client, :direct, :shares, :last_share attr_accessor :socket, :client, :direct, :shares, :last_share, :versions
# Pass the socket object and a boolean indicating whether the socket is netbios or cifs # Pass the socket object and a boolean indicating whether the socket is netbios or cifs
def initialize(socket, direct = false) def initialize(socket, direct = false, versions = [1])
self.socket = socket self.socket = socket
self.direct = direct self.direct = direct
self.versions = versions
self.shares = {}
self.server_max_buffer_size = 1024 # 4356 (workstation) or 16644 (server) expected
if self.versions.include?(2)
self.client = RubySMB::Client.new(RubySMB::Dispatcher::Socket.new(self.socket, read_timeout: 60), self.client = RubySMB::Client.new(RubySMB::Dispatcher::Socket.new(self.socket, read_timeout: 60),
username: '', username: '',
password: '')#Rex::Proto::SMB::Client.new(socket) password: '')#Rex::Proto::SMB::Client.new(socket)
@ -46,8 +50,9 @@ attr_accessor :socket, :client, :direct, :shares, :last_share
# Modify the \PIPE\ string in trans_named_pipe calls # Modify the \PIPE\ string in trans_named_pipe calls
'obscure_trans_pipe' => EVADE::EVASION_NONE, 'obscure_trans_pipe' => EVADE::EVASION_NONE,
} }
self.shares = {} else
self.server_max_buffer_size = 1024 # 4356 (workstation) or 16644 (server) expected self.client = Rex::Proto::SMB::Client.new(socket)
end
end end
def login(name = '', user = '', pass = '', domain = '', def login(name = '', user = '', pass = '', domain = '',
@ -70,7 +75,11 @@ attr_accessor :socket, :client, :direct, :shares, :last_share
self.client.send_ntlm = send_ntlm self.client.send_ntlm = send_ntlm
ok = self.client.negotiate ok = self.client.negotiate
if self.versions.include?(2)
self.server_max_buffer_size = self.client.server_max_buffer_size self.server_max_buffer_size = self.client.server_max_buffer_size
else
self.server_max_buffer_size = ok['Payload'].v['MaxBuff']
end
# Disable NTLMv2 Session for Windows 2000 (breaks authentication on some systems) # Disable NTLMv2 Session for Windows 2000 (breaks authentication on some systems)
# XXX: This in turn breaks SMB auth for Windows 2000 configured to enforce NTLMv2 # XXX: This in turn breaks SMB auth for Windows 2000 configured to enforce NTLMv2
@ -85,7 +94,11 @@ attr_accessor :socket, :client, :direct, :shares, :last_share
# always a string # always a string
pass ||= '' pass ||= ''
if self.versions.include?(2)
ok = self.client.session_setup(user, pass, domain, true) ok = self.client.session_setup(user, pass, domain, true)
else
ok = self.client.session_setup(user, pass, domain)
end
rescue ::Interrupt rescue ::Interrupt
raise $! raise $!
rescue ::Exception => e rescue ::Exception => e
@ -149,7 +162,11 @@ attr_accessor :socket, :client, :direct, :shares, :last_share
def connect(share) def connect(share)
ok = self.client.tree_connect(share) ok = self.client.tree_connect(share)
if self.versions.include?(2)
tree_id = ok.id tree_id = ok.id
else
tree_id = ok['Payload']['SMB'].v['TreeID']
end
self.shares[share] = tree_id self.shares[share] = tree_id
self.last_share = share self.last_share = share
@ -164,8 +181,8 @@ attr_accessor :socket, :client, :direct, :shares, :last_share
false false
end end
def open(path, perm, chunk_size = 48000, read: true, write: false) def open(path, perm, chunk_size = 48000, read: true, write: false)
if self.versions.include?(2)
mode = 0 mode = 0
perm.each_byte { |c| perm.each_byte { |c|
case [c].pack('C').downcase case [c].pack('C').downcase
@ -179,12 +196,19 @@ attr_accessor :socket, :client, :direct, :shares, :last_share
} }
if write if write
ok = self.client.open(path, mode, read: true, write: true) file_id = self.client.open(path, mode, read: true, write: true)
else else
ok = self.client.open(path, mode, read: true) file_id = self.client.open(path, mode, read: true)
end
else
mode = UTILS.open_mode_to_mode(perm)
access = UTILS.open_mode_to_access(perm)
ok = self.client.open(path, mode, access)
file_id = ok['Payload'].v['FileID']
end end
fh = OpenFile.new(self.client, path, self.client.last_tree_id, ok) fh = OpenFile.new(self.client, path, self.client.last_tree_id, file_id, self.versions)
fh.chunk_size = chunk_size fh.chunk_size = chunk_size
fh fh
end end
@ -195,8 +219,15 @@ attr_accessor :socket, :client, :direct, :shares, :last_share
def create_pipe(path, perm = 'c') def create_pipe(path, perm = 'c')
disposition = UTILS.create_mode_to_disposition(perm) disposition = UTILS.create_mode_to_disposition(perm)
file_id = self.client.create_pipe(path, disposition) ok = self.client.create_pipe(path, disposition)
fh = OpenPipe.new(self.client, path, self.client.last_tree_id, file_id)
if self.versions.include?(2)
file_id = ok
else
file_id = ok['Payload'].v['FileID']
end
fh = OpenPipe.new(self.client, path, self.client.last_tree_id, file_id, self.versions)
end end
def trans_pipe(fid, data, no_response = nil) def trans_pipe(fid, data, no_response = nil)

View File

@ -5,14 +5,15 @@ module SMB
class SimpleClient class SimpleClient
class OpenFile class OpenFile
attr_accessor :name, :tree_id, :file_id, :mode, :client, :chunk_size attr_accessor :name, :tree_id, :file_id, :mode, :client, :chunk_size, :versions
def initialize(client, name, tree_id, file_id) def initialize(client, name, tree_id, file_id, versions)
self.client = client self.client = client
self.name = name self.name = name
self.tree_id = tree_id self.tree_id = tree_id
self.file_id = file_id self.file_id = file_id
self.chunk_size = 48000 self.chunk_size = 48000
self.versions = versions
end end
def delete def delete
@ -30,6 +31,7 @@ class OpenFile
# Read data from the file # Read data from the file
def read(length = nil, offset = 0) def read(length = nil, offset = 0)
if self.versions.include?(2)
if (length == nil) if (length == nil)
data = '' data = ''
max_size = self.client.open_files[self.client.last_file_id].size max_size = self.client.open_files[self.client.last_file_id].size
@ -59,6 +61,44 @@ class OpenFile
data = ok.pack('C*') data = ok.pack('C*')
return data return data
end end
else
if (length == nil)
data = ''
fptr = offset
ok = self.client.read(self.file_id, fptr, self.chunk_size)
while (ok and ok['Payload'].v['DataLenLow'] > 0)
buff = ok.to_s.slice(
ok['Payload'].v['DataOffset'] + 4,
ok['Payload'].v['DataLenLow']
)
data << buff
if ok['Payload'].v['Remaining'] == 0
break
end
fptr += ok['Payload'].v['DataLenLow']
begin
ok = self.client.read(self.file_id, fptr, self.chunk_size)
rescue XCEPT::ErrorCode => e
case e.error_code
when 0x00050001
# Novell fires off an access denied error on EOF
ok = nil
else
raise e
end
end
end
return data
else
ok = self.client.read(self.file_id, offset, length)
data = ok.to_s.slice(
ok['Payload'].v['DataOffset'] + 4,
ok['Payload'].v['DataLenLow']
)
return data
end
end
end end
def << (data) def << (data)
@ -79,16 +119,15 @@ class OpenFile
# Keep writing data until we run out # Keep writing data until we run out
while (chunk.length > 0) while (chunk.length > 0)
ok = self.client.write(self.file_id, fptr, chunk) ok = self.client.write(self.file_id, fptr, chunk)
if self.versions.include?(2)
cl = ok cl = ok
else
cl = ok['Payload'].v['CountLow']
end
# Partial write, push the failed data back into the queue # Partial write, push the failed data back into the queue
if (cl != chunk.length) if (cl != chunk.length)
begin
data = chunk.slice(cl - 1, chunk.length - cl) + data data = chunk.slice(cl - 1, chunk.length - cl) + data
rescue
self.write(data, offset)
return
end
end end
# Increment our painter and grab the next chunk # Increment our painter and grab the next chunk

View File

@ -40,7 +40,7 @@ class MetasploitModule < Msf::Auxiliary
def smb_download def smb_download
vprint_status("Connecting...") vprint_status("Connecting...")
connect() connect(versions: [1, 2])
smb_login() smb_login()
vprint_status("#{peer}: Mounting the remote share \\\\#{rhost}\\#{datastore['SMBSHARE']}'...") vprint_status("#{peer}: Mounting the remote share \\\\#{rhost}\\#{datastore['SMBSHARE']}'...")

View File

@ -46,7 +46,7 @@ class MetasploitModule < Msf::Auxiliary
def run_host(_ip) def run_host(_ip)
begin begin
vprint_status("Connecting to the server...") vprint_status("Connecting to the server...")
connect() connect(versions: [1, 2])
smb_login() smb_login()
vprint_status("Mounting the remote share \\\\#{datastore['RHOST']}\\#{datastore['SMBSHARE']}'...") vprint_status("Mounting the remote share \\\\#{datastore['RHOST']}\\#{datastore['SMBSHARE']}'...")