Land #9986, initial ruby_smb simple client integration
parent
0901f35f9c
commit
78f546ce81
12
.rubocop.yml
12
.rubocop.yml
|
@ -17,6 +17,10 @@ Metrics/ClassLength:
|
||||||
Exclude:
|
Exclude:
|
||||||
- 'modules/**/*'
|
- 'modules/**/*'
|
||||||
|
|
||||||
|
Style/ClassAndModuleChildren:
|
||||||
|
Enabled: false
|
||||||
|
Description: 'Forced nesting is harmful for grepping and general code comprehension'
|
||||||
|
|
||||||
Metrics/AbcSize:
|
Metrics/AbcSize:
|
||||||
Enabled: false
|
Enabled: false
|
||||||
Description: 'This is often a red-herring'
|
Description: 'This is often a red-herring'
|
||||||
|
@ -41,6 +45,10 @@ Style/RedundantReturn:
|
||||||
Description: 'This often looks weird when mixed with actual returns, and hurts nothing'
|
Description: 'This often looks weird when mixed with actual returns, and hurts nothing'
|
||||||
Enabled: false
|
Enabled: false
|
||||||
|
|
||||||
|
Style/NumericPredicate:
|
||||||
|
Description: 'This adds no efficiency nor space saving'
|
||||||
|
Enabled: false
|
||||||
|
|
||||||
Style/Documentation:
|
Style/Documentation:
|
||||||
Enabled: true
|
Enabled: true
|
||||||
Description: 'Most Metasploit modules do not have class documentation.'
|
Description: 'Most Metasploit modules do not have class documentation.'
|
||||||
|
@ -109,6 +117,10 @@ Style/WordArray:
|
||||||
Enabled: false
|
Enabled: false
|
||||||
Description: 'Metasploit prefers consistent use of []'
|
Description: 'Metasploit prefers consistent use of []'
|
||||||
|
|
||||||
|
Style/IfUnlessModifier:
|
||||||
|
Enabled: false
|
||||||
|
Description: 'This style might save a couple of lines, but often makes code less clear'
|
||||||
|
|
||||||
Style/RedundantBegin:
|
Style/RedundantBegin:
|
||||||
Exclude:
|
Exclude:
|
||||||
# this pattern is very common and somewhat unavoidable
|
# this pattern is very common and somewhat unavoidable
|
||||||
|
|
|
@ -16,11 +16,6 @@ module Msf
|
||||||
include Msf::Exploit::Remote::Tcp
|
include Msf::Exploit::Remote::Tcp
|
||||||
include Msf::Exploit::Remote::NTLM::Client
|
include Msf::Exploit::Remote::NTLM::Client
|
||||||
|
|
||||||
SIMPLE = Rex::Proto::SMB::SimpleClient
|
|
||||||
XCEPT = Rex::Proto::SMB::Exceptions
|
|
||||||
CONST = Rex::Proto::SMB::Constants
|
|
||||||
|
|
||||||
|
|
||||||
# Alias over the Rex DCERPC protocol modules
|
# Alias over the Rex DCERPC protocol modules
|
||||||
DCERPCPacket = Rex::Proto::DCERPC::Packet
|
DCERPCPacket = Rex::Proto::DCERPC::Packet
|
||||||
DCERPCClient = Rex::Proto::DCERPC::Client
|
DCERPCClient = Rex::Proto::DCERPC::Client
|
||||||
|
@ -78,7 +73,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 +87,7 @@ module Msf
|
||||||
direct = false
|
direct = false
|
||||||
end
|
end
|
||||||
|
|
||||||
c = SIMPLE.new(s, direct)
|
c = Rex::Proto::SMB::SimpleClient.new(s, direct, versions)
|
||||||
|
|
||||||
# setup pipe evasion foo
|
# setup pipe evasion foo
|
||||||
if datastore['SMB::pipe_evasion']
|
if datastore['SMB::pipe_evasion']
|
||||||
|
@ -218,8 +213,8 @@ module Msf
|
||||||
# @raise [Rex::Proto::SMB::Exceptions::ErrorCode]
|
# @raise [Rex::Proto::SMB::Exceptions::ErrorCode]
|
||||||
def smb_file_exist?(file)
|
def smb_file_exist?(file)
|
||||||
begin
|
begin
|
||||||
fd = simple.open(file, 'ro')
|
fd = simple.open(file, 'o')
|
||||||
rescue XCEPT::ErrorCode => e
|
rescue Rex::Proto::SMB::Exceptions::ErrorCode => e
|
||||||
# If attempting to open the file results in a "*_NOT_FOUND" error,
|
# If attempting to open the file results in a "*_NOT_FOUND" error,
|
||||||
# then we can be sure the file is not there.
|
# then we can be sure the file is not there.
|
||||||
#
|
#
|
||||||
|
|
|
@ -75,7 +75,7 @@ module Exploit::Remote::SMB::Client::Psexec
|
||||||
def smb_read_file(smbshare, host, file)
|
def smb_read_file(smbshare, host, file)
|
||||||
begin
|
begin
|
||||||
simple.connect("\\\\#{host}\\#{smbshare}")
|
simple.connect("\\\\#{host}\\#{smbshare}")
|
||||||
file = simple.open(file, 'ro')
|
file = simple.open(file, 'o')
|
||||||
contents = file.read
|
contents = file.read
|
||||||
file.close
|
file.close
|
||||||
simple.disconnect("\\\\#{host}\\#{smbshare}")
|
simple.disconnect("\\\\#{host}\\#{smbshare}")
|
||||||
|
@ -267,7 +267,7 @@ module Exploit::Remote::SMB::Client::Psexec
|
||||||
begin
|
begin
|
||||||
psexec(command)
|
psexec(command)
|
||||||
rescue StandardError => exec_command_error
|
rescue StandardError => exec_command_error
|
||||||
fail_with(Msf::Exploit::Failure::Unknown, "#{peer} - Unable to execute specified command: #{exec_command_error}")
|
fail_with(Failure::Unknown, "#{peer} - Unable to execute specified command: #{exec_command_error}")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -158,7 +158,7 @@ require 'rex/proto/smb/exceptions'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
data = self.socket.read( read_cnt, rand(1024)+1)
|
data = self.socket.read(read_cnt, rand(1024)+1)
|
||||||
break if !(data and data.length > 0)
|
break if !(data and data.length > 0)
|
||||||
raw_response += data
|
raw_response += data
|
||||||
|
|
||||||
|
|
|
@ -119,17 +119,17 @@ class Client
|
||||||
NDR.long(default_opts[:password4])
|
NDR.long(default_opts[:password4])
|
||||||
begin
|
begin
|
||||||
response = dcerpc_client.call(CREATE_SERVICE_W, stubdata)
|
response = dcerpc_client.call(CREATE_SERVICE_W, stubdata)
|
||||||
if response
|
|
||||||
svc_status = error_code(response[24,4])
|
|
||||||
|
|
||||||
if svc_status == ERROR_SUCCESS
|
|
||||||
svc_handle = response[4,20]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
rescue Rex::Proto::DCERPC::Exceptions::Fault => e
|
rescue Rex::Proto::DCERPC::Exceptions::Fault => e
|
||||||
print_error("Error creating service: #{e}")
|
print_error("Error creating service: #{e}")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if response
|
||||||
|
svc_status = error_code(response[24,4])
|
||||||
|
if svc_status == ERROR_SUCCESS
|
||||||
|
svc_handle = response[4,20]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
return svc_handle, svc_status
|
return svc_handle, svc_status
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -169,7 +169,7 @@ class Client
|
||||||
begin
|
begin
|
||||||
response = dcerpc_client.call(CLOSE_SERVICE_HANDLE, handle)
|
response = dcerpc_client.call(CLOSE_SERVICE_HANDLE, handle)
|
||||||
if response
|
if response
|
||||||
svc_status = error_code(response[20,4])
|
svc_status = error_code(response)
|
||||||
end
|
end
|
||||||
rescue Rex::Proto::DCERPC::Exceptions::Fault => e
|
rescue Rex::Proto::DCERPC::Exceptions::Fault => e
|
||||||
print_error("Error closing service handle: #{e}")
|
print_error("Error closing service handle: #{e}")
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -2,6 +2,7 @@
|
||||||
module Rex
|
module Rex
|
||||||
module Proto
|
module Proto
|
||||||
module SMB
|
module SMB
|
||||||
|
|
||||||
class SimpleClient
|
class SimpleClient
|
||||||
|
|
||||||
require 'rex/text'
|
require 'rex/text'
|
||||||
|
@ -14,6 +15,7 @@ require 'rex/proto/smb/utils'
|
||||||
require 'rex/proto/smb/client'
|
require 'rex/proto/smb/client'
|
||||||
require 'rex/proto/smb/simpleclient/open_file'
|
require 'rex/proto/smb/simpleclient/open_file'
|
||||||
require 'rex/proto/smb/simpleclient/open_pipe'
|
require 'rex/proto/smb/simpleclient/open_pipe'
|
||||||
|
require 'ruby_smb'
|
||||||
|
|
||||||
# Some short-hand class aliases
|
# Some short-hand class aliases
|
||||||
CONST = Rex::Proto::SMB::Constants
|
CONST = Rex::Proto::SMB::Constants
|
||||||
|
@ -26,15 +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.client = Rex::Proto::SMB::Client.new(socket)
|
self.versions = versions
|
||||||
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 = '',
|
||||||
|
@ -57,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 = ok['Payload'].v['MaxBuff']
|
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
|
||||||
|
@ -72,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)
|
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
|
||||||
|
@ -135,7 +161,13 @@ 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['Payload']['SMB'].v['TreeID']
|
|
||||||
|
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
|
||||||
end
|
end
|
||||||
|
@ -149,14 +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)
|
||||||
|
if self.versions.include?(2)
|
||||||
|
mode = 0
|
||||||
|
perm.each_byte { |c|
|
||||||
|
case [c].pack('C').downcase
|
||||||
|
when 'x', 'c'
|
||||||
|
mode |= RubySMB::Dispositions::FILE_CREATE
|
||||||
|
when 'o'
|
||||||
|
mode |= RubySMB::Dispositions::FILE_OPEN
|
||||||
|
when 's'
|
||||||
|
mode |= RubySMB::Dispositions::FILE_SUPERSEDE
|
||||||
|
end
|
||||||
|
}
|
||||||
|
|
||||||
def open(path, perm, chunk_size = 48000)
|
if write
|
||||||
mode = UTILS.open_mode_to_mode(perm)
|
file_id = self.client.open(path, mode, read: true, write: true)
|
||||||
access = UTILS.open_mode_to_access(perm)
|
else
|
||||||
|
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)
|
ok = self.client.open(path, mode, access)
|
||||||
file_id = ok['Payload'].v['FileID']
|
file_id = ok['Payload'].v['FileID']
|
||||||
fh = OpenFile.new(self.client, path, self.client.last_tree_id, file_id)
|
end
|
||||||
|
|
||||||
|
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
|
||||||
|
@ -168,8 +220,14 @@ 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)
|
||||||
ok = self.client.create_pipe(path, disposition)
|
ok = self.client.create_pipe(path, disposition)
|
||||||
file_id = ok['Payload'].v['FileID']
|
|
||||||
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)
|
||||||
|
|
|
@ -1,106 +1,137 @@
|
||||||
# -*- coding: binary -*-
|
# -*- coding: binary -*-
|
||||||
module Rex
|
module Rex::Proto::SMB
|
||||||
module Proto
|
class SimpleClient
|
||||||
module SMB
|
#
|
||||||
class SimpleClient
|
# This represents an open file, which can be read, written, or closed
|
||||||
|
#
|
||||||
|
class OpenFile
|
||||||
|
attr_accessor :name, :tree_id, :file_id, :mode, :client, :chunk_size, :versions
|
||||||
|
|
||||||
class OpenFile
|
def initialize(client, name, tree_id, file_id, versions)
|
||||||
attr_accessor :name, :tree_id, :file_id, :mode, :client, :chunk_size
|
self.client = client
|
||||||
|
self.name = name
|
||||||
def initialize(client, name, tree_id, file_id)
|
self.tree_id = tree_id
|
||||||
self.client = client
|
self.file_id = file_id
|
||||||
self.name = name
|
self.chunk_size = 48000
|
||||||
self.tree_id = tree_id
|
self.versions = versions
|
||||||
self.file_id = file_id
|
end
|
||||||
self.chunk_size = 48000
|
|
||||||
end
|
|
||||||
|
|
||||||
def delete
|
|
||||||
begin
|
|
||||||
self.close
|
|
||||||
rescue
|
|
||||||
end
|
|
||||||
self.client.delete(self.name, self.tree_id)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Close this open file
|
|
||||||
def close
|
|
||||||
self.client.close(self.file_id, self.tree_id)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Read data from the file
|
|
||||||
def read(length = nil, offset = 0)
|
|
||||||
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']
|
|
||||||
|
|
||||||
|
def delete
|
||||||
begin
|
begin
|
||||||
ok = self.client.read(self.file_id, fptr, self.chunk_size)
|
close
|
||||||
rescue XCEPT::ErrorCode => e
|
rescue StandardError
|
||||||
case e.error_code
|
end
|
||||||
when 0x00050001
|
client.delete(name, tree_id)
|
||||||
# Novell fires off an access denied error on EOF
|
end
|
||||||
ok = nil
|
|
||||||
else
|
# Close this open file
|
||||||
raise e
|
def close
|
||||||
|
client.close(file_id, tree_id)
|
||||||
|
end
|
||||||
|
|
||||||
|
def read_ruby_smb(length, offset)
|
||||||
|
if length.nil?
|
||||||
|
max_size = client.open_files[client.last_file_id].size
|
||||||
|
fptr = offset
|
||||||
|
|
||||||
|
chunk = [max_size, chunk_size].min
|
||||||
|
|
||||||
|
data = client.read(file_id, fptr, chunk).pack('C*')
|
||||||
|
fptr = data.length
|
||||||
|
|
||||||
|
while data.length < max_size
|
||||||
|
if (max_size - data.length) < chunk
|
||||||
|
chunk = max_size - data.length
|
||||||
|
end
|
||||||
|
data << client.read(file_id, fptr, chunk).pack('C*')
|
||||||
|
fptr = data.length
|
||||||
end
|
end
|
||||||
|
else
|
||||||
|
data = client.read(file_id, offset, length).pack('C*')
|
||||||
|
end
|
||||||
|
|
||||||
|
data
|
||||||
|
end
|
||||||
|
|
||||||
|
def read_rex_smb(length, offset)
|
||||||
|
if length.nil?
|
||||||
|
data = ''
|
||||||
|
fptr = offset
|
||||||
|
ok = client.read(file_id, fptr, chunk_size)
|
||||||
|
while ok && ok['Payload'].v['DataLenLow'] > 0
|
||||||
|
buff = ok.to_s.slice(
|
||||||
|
ok['Payload'].v['DataOffset'] + 4,
|
||||||
|
ok['Payload'].v['DataLenLow']
|
||||||
|
)
|
||||||
|
data << buff
|
||||||
|
break if ok['Payload'].v['Remaining'] == 0
|
||||||
|
fptr += ok['Payload'].v['DataLenLow']
|
||||||
|
|
||||||
|
begin
|
||||||
|
ok = client.read(file_id, fptr, 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
|
||||||
|
else
|
||||||
|
ok = client.read(file_id, offset, length)
|
||||||
|
data = ok.to_s.slice(
|
||||||
|
ok['Payload'].v['DataOffset'] + 4,
|
||||||
|
ok['Payload'].v['DataLenLow']
|
||||||
|
)
|
||||||
|
end
|
||||||
|
data
|
||||||
|
end
|
||||||
|
|
||||||
|
# Read data from the file
|
||||||
|
def read(length = nil, offset = 0)
|
||||||
|
if versions.include?(2)
|
||||||
|
read_ruby_smb(length, offset)
|
||||||
|
else
|
||||||
|
read_rex_smb(length, offset)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return data
|
def <<(data)
|
||||||
else
|
write(data)
|
||||||
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
|
|
||||||
|
|
||||||
def << (data)
|
|
||||||
self.write(data)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Write data to the file
|
|
||||||
def write(data, offset = 0)
|
|
||||||
# Track our offset into the remote file
|
|
||||||
fptr = offset
|
|
||||||
|
|
||||||
# Duplicate the data so we can use slice!
|
|
||||||
data = data.dup
|
|
||||||
|
|
||||||
# Take our first chunk of bytes
|
|
||||||
chunk = data.slice!(0, self.chunk_size)
|
|
||||||
|
|
||||||
# Keep writing data until we run out
|
|
||||||
while (chunk.length > 0)
|
|
||||||
ok = self.client.write(self.file_id, fptr, chunk)
|
|
||||||
cl = ok['Payload'].v['CountLow']
|
|
||||||
|
|
||||||
# Partial write, push the failed data back into the queue
|
|
||||||
if (cl != chunk.length)
|
|
||||||
data = chunk.slice(cl - 1, chunk.length - cl) + data
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Increment our painter and grab the next chunk
|
# Write data to the file
|
||||||
fptr += cl
|
def write(data, offset = 0)
|
||||||
chunk = data.slice!(0, self.chunk_size)
|
# Track our offset into the remote file
|
||||||
|
fptr = offset
|
||||||
|
|
||||||
|
# Duplicate the data so we can use slice!
|
||||||
|
data = data.dup
|
||||||
|
|
||||||
|
# Take our first chunk of bytes
|
||||||
|
chunk = data.slice!(0, chunk_size)
|
||||||
|
|
||||||
|
# Keep writing data until we run out
|
||||||
|
until chunk.empty?
|
||||||
|
ok = client.write(file_id, fptr, chunk)
|
||||||
|
if versions.include?(2)
|
||||||
|
cl = ok
|
||||||
|
else
|
||||||
|
cl = ok['Payload'].v['CountLow']
|
||||||
|
end
|
||||||
|
|
||||||
|
# Partial write, push the failed data back into the queue
|
||||||
|
if cl != chunk.length
|
||||||
|
data = chunk.slice(cl - 1, chunk.length - cl) + data
|
||||||
|
end
|
||||||
|
|
||||||
|
# Increment our painter and grab the next chunk
|
||||||
|
fptr += cl
|
||||||
|
chunk = data.slice!(0, chunk_size)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
|
@ -12,12 +12,6 @@ class MetasploitModule < Msf::Auxiliary
|
||||||
include Msf::Auxiliary::Report
|
include Msf::Auxiliary::Report
|
||||||
include Msf::Auxiliary::Scanner
|
include Msf::Auxiliary::Scanner
|
||||||
|
|
||||||
# Aliases for common classes
|
|
||||||
SIMPLE = Rex::Proto::SMB::SimpleClient
|
|
||||||
XCEPT = Rex::Proto::SMB::Exceptions
|
|
||||||
CONST = Rex::Proto::SMB::Constants
|
|
||||||
|
|
||||||
|
|
||||||
def initialize
|
def initialize
|
||||||
super(
|
super(
|
||||||
'Name' => 'SMB File Download Utility',
|
'Name' => 'SMB File Download Utility',
|
||||||
|
@ -40,7 +34,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']}'...")
|
||||||
|
@ -51,7 +45,7 @@ class MetasploitModule < Msf::Auxiliary
|
||||||
vprint_status("Trying to download #{remote_path}...")
|
vprint_status("Trying to download #{remote_path}...")
|
||||||
|
|
||||||
data = ''
|
data = ''
|
||||||
fd = simple.open("\\#{remote_path}", 'ro')
|
fd = simple.open("#{remote_path}", 'o')
|
||||||
begin
|
begin
|
||||||
data = fd.read
|
data = fd.read
|
||||||
ensure
|
ensure
|
||||||
|
|
|
@ -13,12 +13,6 @@ class MetasploitModule < Msf::Auxiliary
|
||||||
include Msf::Auxiliary::Report
|
include Msf::Auxiliary::Report
|
||||||
include Msf::Auxiliary::Scanner
|
include Msf::Auxiliary::Scanner
|
||||||
|
|
||||||
# Aliases for common classes
|
|
||||||
SIMPLE = Rex::Proto::SMB::SimpleClient
|
|
||||||
XCEPT = Rex::Proto::SMB::Exceptions
|
|
||||||
CONST = Rex::Proto::SMB::Constants
|
|
||||||
|
|
||||||
|
|
||||||
def initialize
|
def initialize
|
||||||
super(
|
super(
|
||||||
'Name' => 'SMB File Upload Utility',
|
'Name' => 'SMB File Upload Utility',
|
||||||
|
@ -46,7 +40,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']}'...")
|
||||||
|
@ -63,7 +57,7 @@ class MetasploitModule < Msf::Auxiliary
|
||||||
begin
|
begin
|
||||||
vprint_status("Trying to upload #{local_path} to #{remote_path}...")
|
vprint_status("Trying to upload #{local_path} to #{remote_path}...")
|
||||||
|
|
||||||
fd = simple.open("\\#{remote_path}", 'rwct')
|
fd = simple.open("#{remote_path}", 's', write: true)
|
||||||
data = ::File.read(datastore['LPATH'], ::File.size(datastore['LPATH']))
|
data = ::File.read(datastore['LPATH'], ::File.size(datastore['LPATH']))
|
||||||
fd.write(data)
|
fd.write(data)
|
||||||
fd.close
|
fd.close
|
||||||
|
|
|
@ -17,12 +17,6 @@ class MetasploitModule < Msf::Auxiliary
|
||||||
include Msf::Auxiliary::Scanner
|
include Msf::Auxiliary::Scanner
|
||||||
include Msf::Auxiliary::Report
|
include Msf::Auxiliary::Report
|
||||||
|
|
||||||
# Aliases for common classes
|
|
||||||
SIMPLE = Rex::Proto::SMB::SimpleClient
|
|
||||||
XCEPT = Rex::Proto::SMB::Exceptions
|
|
||||||
CONST = Rex::Proto::SMB::Constants
|
|
||||||
|
|
||||||
|
|
||||||
def initialize
|
def initialize
|
||||||
super(
|
super(
|
||||||
'Name' => 'SMB Version Detection',
|
'Name' => 'SMB Version Detection',
|
||||||
|
|
|
@ -0,0 +1,62 @@
|
||||||
|
{
|
||||||
|
"COMMAND_LIST": [],
|
||||||
|
"CREDS_FILE": "../JSON/creds.json",
|
||||||
|
"FRAMEWORK_BRANCH": "upstream/master",
|
||||||
|
"HTTP_PORT": 5309,
|
||||||
|
"MSF_HOSTS": [
|
||||||
|
{
|
||||||
|
"CPE": "cpe:/a:rapid7:metasploit:::",
|
||||||
|
"HYPERVISOR_CONFIG": "../JSON/esxi_config.json",
|
||||||
|
"METHOD": "VM_TOOLS_UPLOAD",
|
||||||
|
"MSF_ARTIFACT_PATH": "/home/msfuser/rapid7/test_artifacts",
|
||||||
|
"MSF_PATH": "/home/msfuser/rapid7/metasploit-framework",
|
||||||
|
"TYPE": "VIRTUAL"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"REPORT_PREFIX": "SmbEnumSharesTest",
|
||||||
|
"STARTING_LISTENER": 30000,
|
||||||
|
"SUCCESS_LIST": [
|
||||||
|
"IPC$"
|
||||||
|
],
|
||||||
|
"TARGETS": [
|
||||||
|
{
|
||||||
|
"CPE": "cpe:/o:microsoft:windows_server_2008:r2:sp1:x64",
|
||||||
|
"METHOD": "EXPLOIT",
|
||||||
|
"MODULES": [
|
||||||
|
{
|
||||||
|
"NAME": "auxiliary/scanner/smb/smb_enumshares",
|
||||||
|
"SETTINGS": [
|
||||||
|
"smbuser=vagrant",
|
||||||
|
"smbpass=vagrant"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"TYPE": "VIRTUAL"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"CPE": "cpe:/o:microsoft:windows_server_2016:::x64",
|
||||||
|
"METHOD": "EXPLOIT",
|
||||||
|
"MODULES": [
|
||||||
|
{
|
||||||
|
"NAME": "auxiliary/scanner/smb/smb_enumshares",
|
||||||
|
"SETTINGS": [
|
||||||
|
"smbuser=vagrant",
|
||||||
|
"smbpass=vagrant"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"TYPE": "VIRTUAL"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"TARGET_GLOBALS": {
|
||||||
|
"HYPERVISOR_CONFIG": "../JSON/esxi_config.json",
|
||||||
|
"METERPRETER_JAVA": "C:\\software\\x86\\java\\bin\\java.exe",
|
||||||
|
"METERPRETER_PYTHON": "C:\\software\\x86\\python27\\python.exe",
|
||||||
|
"METHOD": "VM_TOOLS_UPLOAD",
|
||||||
|
"PAYLOAD_DIRECTORY": "C:\\payload_test",
|
||||||
|
"PYTHON": "C:\\software\\x86\\python27\\python.exe",
|
||||||
|
"TESTING_SNAPSHOT": "TESTING_BASE",
|
||||||
|
"TYPE": "VIRTUAL"
|
||||||
|
},
|
||||||
|
"TEST_NAME": "smb_enumshares test"
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
{
|
||||||
|
"COMMAND_LIST": [],
|
||||||
|
"CREDS_FILE": "../JSON/creds.json",
|
||||||
|
"FRAMEWORK_BRANCH": "upstream/master",
|
||||||
|
"HTTP_PORT": 5309,
|
||||||
|
"MSF_HOSTS": [
|
||||||
|
{
|
||||||
|
"CPE": "cpe:/a:rapid7:metasploit:::",
|
||||||
|
"HYPERVISOR_CONFIG": "../JSON/esxi_config.json",
|
||||||
|
"METHOD": "VM_TOOLS_UPLOAD",
|
||||||
|
"MSF_ARTIFACT_PATH": "/home/msfuser/rapid7/test_artifacts",
|
||||||
|
"MSF_PATH": "/home/msfuser/rapid7/metasploit-framework",
|
||||||
|
"TYPE": "VIRTUAL"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"REPORT_PREFIX": "SmbEnumUsersTest",
|
||||||
|
"STARTING_LISTENER": 30000,
|
||||||
|
"SUCCESS_LIST": [
|
||||||
|
"Administrator"
|
||||||
|
],
|
||||||
|
"TARGETS": [
|
||||||
|
{
|
||||||
|
"CPE": "cpe:/o:microsoft:windows_server_2008:r2:sp1:x64",
|
||||||
|
"METHOD": "EXPLOIT",
|
||||||
|
"MODULES": [
|
||||||
|
{
|
||||||
|
"NAME": "auxiliary/scanner/smb/smb_enumusers",
|
||||||
|
"SETTINGS": [
|
||||||
|
"smbuser=vagrant",
|
||||||
|
"smbpass=vagrant"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"TYPE": "VIRTUAL"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"CPE": "cpe:/o:microsoft:windows_server_2016:::x64",
|
||||||
|
"METHOD": "EXPLOIT",
|
||||||
|
"MODULES": [
|
||||||
|
{
|
||||||
|
"NAME": "auxiliary/scanner/smb/smb_enumusers",
|
||||||
|
"SETTINGS": [
|
||||||
|
"smbuser=vagrant",
|
||||||
|
"smbpass=vagrant"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"TYPE": "VIRTUAL"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"TARGET_GLOBALS": {
|
||||||
|
"HYPERVISOR_CONFIG": "../JSON/esxi_config.json",
|
||||||
|
"METERPRETER_JAVA": "C:\\software\\x86\\java\\bin\\java.exe",
|
||||||
|
"METERPRETER_PYTHON": "C:\\software\\x86\\python27\\python.exe",
|
||||||
|
"METHOD": "VM_TOOLS_UPLOAD",
|
||||||
|
"PAYLOAD_DIRECTORY": "C:\\payload_test",
|
||||||
|
"PYTHON": "C:\\software\\x86\\python27\\python.exe",
|
||||||
|
"TESTING_SNAPSHOT": "TESTING_BASE",
|
||||||
|
"TYPE": "VIRTUAL"
|
||||||
|
},
|
||||||
|
"TEST_NAME": "smb_enumusers test"
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
{
|
||||||
|
"COMMAND_LIST": [
|
||||||
|
"sessions -C sessions -l",
|
||||||
|
"sessions -C sysinfo",
|
||||||
|
"sessions -C exit"
|
||||||
|
],
|
||||||
|
"CREDS_FILE": "../JSON/creds.json",
|
||||||
|
"FRAMEWORK_BRANCH": "upstream/master",
|
||||||
|
"HTTP_PORT": 5309,
|
||||||
|
"MODULES": [
|
||||||
|
{
|
||||||
|
"NAME": "exploit/windows/smb/psexec",
|
||||||
|
"SETTINGS": [
|
||||||
|
"SMBUser=vagrant",
|
||||||
|
"SMBPass=vagrant"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"MSF_HOSTS": [
|
||||||
|
{
|
||||||
|
"CPE": "cpe:/a:rapid7:metasploit:::",
|
||||||
|
"HYPERVISOR_CONFIG": "../JSON/esxi_config.json",
|
||||||
|
"METHOD": "VM_TOOLS_UPLOAD",
|
||||||
|
"MSF_ARTIFACT_PATH": "/home/msfuser/rapid7/test_artifacts",
|
||||||
|
"MSF_PATH": "/home/msfuser/rapid7/metasploit-framework",
|
||||||
|
"TYPE": "VIRTUAL"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"PAYLOADS": [
|
||||||
|
{
|
||||||
|
"NAME": "windows/x64/meterpreter/reverse_tcp",
|
||||||
|
"SETTINGS": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"STARTING_LISTENER": 30000,
|
||||||
|
"SUCCESS_LIST": [
|
||||||
|
"Session 1 created in the background"
|
||||||
|
],
|
||||||
|
"TARGETS": [
|
||||||
|
{
|
||||||
|
"CPE": "cpe:/o:microsoft:windows_7:::x64"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"CPE": "cpe:/o:microsoft:windows_10:::x64"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"TARGET_GLOBALS": {
|
||||||
|
"HYPERVISOR_CONFIG": "../JSON/esxi_config.json",
|
||||||
|
"METERPRETER_JAVA": "C:\\software\\x86\\java\\bin\\java.exe",
|
||||||
|
"METERPRETER_PYTHON": "C:\\software\\x86\\python27\\python.exe",
|
||||||
|
"METHOD": "EXPLOIT",
|
||||||
|
"PAYLOAD_DIRECTORY": "C:\\payload_test",
|
||||||
|
"PYTHON": "C:\\software\\x86\\python27\\python.exe",
|
||||||
|
"TYPE": "VIRTUAL"
|
||||||
|
}
|
||||||
|
}
|
|
@ -30,20 +30,6 @@ require 'rex/encoder/ndr'
|
||||||
require 'rex/proto/smb/simpleclient'
|
require 'rex/proto/smb/simpleclient'
|
||||||
|
|
||||||
|
|
||||||
# SMB constants from Rex
|
|
||||||
SIMPLE = Rex::Proto::SMB::SimpleClient
|
|
||||||
XCEPT = Rex::Proto::SMB::Exceptions
|
|
||||||
CONST = Rex::Proto::SMB::Constants
|
|
||||||
|
|
||||||
|
|
||||||
# Alias over the Rex DCERPC protocol modules
|
|
||||||
DCERPCPacket = Rex::Proto::DCERPC::Packet
|
|
||||||
DCERPCClient = Rex::Proto::DCERPC::Client
|
|
||||||
DCERPCResponse = Rex::Proto::DCERPC::Response
|
|
||||||
DCERPCUUID = Rex::Proto::DCERPC::UUID
|
|
||||||
NDR = Rex::Encoder::NDR
|
|
||||||
|
|
||||||
|
|
||||||
def print_error(msg)
|
def print_error(msg)
|
||||||
$stderr.puts "[-] #{msg}"
|
$stderr.puts "[-] #{msg}"
|
||||||
end
|
end
|
||||||
|
@ -102,27 +88,20 @@ opt_pass = ARGV.shift() || ""
|
||||||
opt_share = "ADMIN$"
|
opt_share = "ADMIN$"
|
||||||
opt_domain = "."
|
opt_domain = "."
|
||||||
|
|
||||||
socket = Rex::Socket.create_tcp({ 'PeerHost' => opt_host, 'PeerPort' => opt_port.to_i })
|
begin
|
||||||
|
socket = Rex::Socket.create_tcp({ 'PeerHost' => opt_host, 'PeerPort' => opt_port.to_i })
|
||||||
|
rescue Rex::ConnectionRefused, Rex::HostUnreachable => e
|
||||||
|
print_error("Could not connect: #{e}")
|
||||||
|
exit(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
simple = Rex::Proto::SMB::SimpleClient.new(socket, opt_port.to_i == 445, versions = [1, 2])
|
||||||
|
|
||||||
|
|
||||||
simple = Rex::Proto::SMB::SimpleClient.new(socket, opt_port.to_i == 445)
|
|
||||||
|
|
||||||
simple.login(
|
simple.login(
|
||||||
Rex::Text.rand_text_alpha(8),
|
Rex::Text.rand_text_alpha(8),
|
||||||
opt_user,
|
opt_user,
|
||||||
opt_pass,
|
opt_pass,
|
||||||
opt_domain
|
opt_domain
|
||||||
#datastore['SMB::VerifySignature'],
|
|
||||||
#datastore['NTLM::UseNTLMv2'],
|
|
||||||
#datastore['NTLM::UseNTLM2_session'],
|
|
||||||
#datastore['NTLM::SendLM'],
|
|
||||||
#datastore['NTLM::UseLMKey'],
|
|
||||||
#datastore['NTLM::SendNTLM'],
|
|
||||||
#datastore['SMB::Native_OS'],
|
|
||||||
#datastore['SMB::Native_LM'],
|
|
||||||
#{:use_spn => datastore['NTLM::SendSPN'], :name => self.rhost}
|
|
||||||
)
|
)
|
||||||
simple.connect("\\\\#{opt_host}\\IPC$")
|
simple.connect("\\\\#{opt_host}\\IPC$")
|
||||||
|
|
||||||
|
@ -138,16 +117,12 @@ if (not simple.client.auth_user)
|
||||||
exit(1)
|
exit(1)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
fname = Rex::Text.rand_text_alpha(8) + ".exe"
|
fname = Rex::Text.rand_text_alpha(8) + ".exe"
|
||||||
sname = Rex::Text.rand_text_alpha(8)
|
sname = Rex::Text.rand_text_alpha(8)
|
||||||
|
|
||||||
|
|
||||||
# Upload the payload to the share
|
# Upload the payload to the share
|
||||||
print_status("Uploading payload...")
|
print_status("Uploading payload...")
|
||||||
|
|
||||||
|
|
||||||
simple.connect(opt_share)
|
simple.connect(opt_share)
|
||||||
|
|
||||||
fd = simple.open("\\#{fname}", 'rwct', 500)
|
fd = simple.open("\\#{fname}", 'rwct', 500)
|
||||||
|
@ -177,6 +152,7 @@ print_status("Bound to #{handle} ...")
|
||||||
|
|
||||||
print_status("Obtaining a service manager handle...")
|
print_status("Obtaining a service manager handle...")
|
||||||
scm_handle = nil
|
scm_handle = nil
|
||||||
|
NDR = Rex::Encoder::NDR
|
||||||
stubdata =
|
stubdata =
|
||||||
NDR.uwstring("\\\\#{opt_host}") +
|
NDR.uwstring("\\\\#{opt_host}") +
|
||||||
NDR.long(0) +
|
NDR.long(0) +
|
||||||
|
|
Loading…
Reference in New Issue