simpleclient versions option
parent
ff202a5f5b
commit
534d05ff44
|
@ -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']
|
||||||
|
|
|
@ -28,26 +28,31 @@ 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.client = RubySMB::Client.new(RubySMB::Dispatcher::Socket.new(self.socket, read_timeout: 60),
|
|
||||||
username: '',
|
|
||||||
password: '')#Rex::Proto::SMB::Client.new(socket)
|
|
||||||
self.client.evasion_opts = {
|
|
||||||
# Padding is performed between packet headers and data
|
|
||||||
'pad_data' => EVADE::EVASION_NONE,
|
|
||||||
# File path padding is performed on all open/create calls
|
|
||||||
'pad_file' => EVADE::EVASION_NONE,
|
|
||||||
# Modify the \PIPE\ string in trans_named_pipe calls
|
|
||||||
'obscure_trans_pipe' => EVADE::EVASION_NONE,
|
|
||||||
}
|
|
||||||
self.shares = {}
|
self.shares = {}
|
||||||
self.server_max_buffer_size = 1024 # 4356 (workstation) or 16644 (server) expected
|
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),
|
||||||
|
username: '',
|
||||||
|
password: '')#Rex::Proto::SMB::Client.new(socket)
|
||||||
|
self.client.evasion_opts = {
|
||||||
|
# Padding is performed between packet headers and data
|
||||||
|
'pad_data' => EVADE::EVASION_NONE,
|
||||||
|
# File path padding is performed on all open/create calls
|
||||||
|
'pad_file' => EVADE::EVASION_NONE,
|
||||||
|
# Modify the \PIPE\ string in trans_named_pipe calls
|
||||||
|
'obscure_trans_pipe' => EVADE::EVASION_NONE,
|
||||||
|
}
|
||||||
|
else
|
||||||
|
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
|
||||||
self.server_max_buffer_size = self.client.server_max_buffer_size
|
if self.versions.include?(2)
|
||||||
|
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 ||= ''
|
||||||
|
|
||||||
ok = self.client.session_setup(user, pass, domain, true)
|
if self.versions.include?(2)
|
||||||
|
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)
|
||||||
|
|
||||||
tree_id = ok.id
|
if self.versions.include?(2)
|
||||||
|
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,27 +181,34 @@ 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)
|
||||||
mode = 0
|
if self.versions.include?(2)
|
||||||
perm.each_byte { |c|
|
mode = 0
|
||||||
case [c].pack('C').downcase
|
perm.each_byte { |c|
|
||||||
when 'x', 'c'
|
case [c].pack('C').downcase
|
||||||
mode |= RubySMB::Dispositions::FILE_CREATE
|
when 'x', 'c'
|
||||||
when 'o'
|
mode |= RubySMB::Dispositions::FILE_CREATE
|
||||||
mode |= RubySMB::Dispositions::FILE_OPEN
|
when 'o'
|
||||||
when 's'
|
mode |= RubySMB::Dispositions::FILE_OPEN
|
||||||
mode |= RubySMB::Dispositions::FILE_SUPERSEDE
|
when 's'
|
||||||
end
|
mode |= RubySMB::Dispositions::FILE_SUPERSEDE
|
||||||
}
|
end
|
||||||
|
}
|
||||||
|
|
||||||
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
|
||||||
|
file_id = self.client.open(path, mode, read: true)
|
||||||
|
end
|
||||||
else
|
else
|
||||||
ok = self.client.open(path, mode, read: true)
|
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)
|
||||||
|
|
|
@ -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,34 +31,73 @@ 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 (length == nil)
|
if self.versions.include?(2)
|
||||||
data = ''
|
if (length == nil)
|
||||||
max_size = self.client.open_files[self.client.last_file_id].size
|
data = ''
|
||||||
fptr = offset
|
max_size = self.client.open_files[self.client.last_file_id].size
|
||||||
|
fptr = offset
|
||||||
|
|
||||||
if max_size < self.chunk_size
|
if max_size < self.chunk_size
|
||||||
chunk = max_size
|
chunk = max_size
|
||||||
else
|
else
|
||||||
chunk = self.chunk_size
|
chunk = self.chunk_size
|
||||||
end
|
|
||||||
|
|
||||||
ok = self.client.read(self.file_id, fptr, chunk)
|
|
||||||
data << ok.pack('C*')
|
|
||||||
fptr = data.length
|
|
||||||
|
|
||||||
while (ok && data.length < max_size)
|
|
||||||
if (max_size - data.length) < chunk
|
|
||||||
chunk = max_size - data.length
|
|
||||||
end
|
end
|
||||||
|
|
||||||
ok = self.client.read(self.file_id, fptr, chunk)
|
ok = self.client.read(self.file_id, fptr, chunk)
|
||||||
data << ok.pack('C*')
|
data << ok.pack('C*')
|
||||||
fptr = data.length
|
fptr = data.length
|
||||||
|
|
||||||
|
while (ok && data.length < max_size)
|
||||||
|
if (max_size - data.length) < chunk
|
||||||
|
chunk = max_size - data.length
|
||||||
|
end
|
||||||
|
ok = self.client.read(self.file_id, fptr, chunk)
|
||||||
|
data << ok.pack('C*')
|
||||||
|
fptr = data.length
|
||||||
|
end
|
||||||
|
return data
|
||||||
|
else
|
||||||
|
ok = self.client.read(self.file_id, offset, length)
|
||||||
|
data = ok.pack('C*')
|
||||||
|
return data
|
||||||
end
|
end
|
||||||
return data
|
|
||||||
else
|
else
|
||||||
ok = self.client.read(self.file_id, offset, length)
|
if (length == nil)
|
||||||
data = ok.pack('C*')
|
data = ''
|
||||||
return 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
|
end
|
||||||
|
|
||||||
|
@ -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)
|
||||||
cl = ok
|
if self.versions.include?(2)
|
||||||
|
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
|
||||||
|
|
|
@ -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']}'...")
|
||||||
|
|
|
@ -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']}'...")
|
||||||
|
|
Loading…
Reference in New Issue