Land #4489, Update SMB admin modules to use Scanner & fixes
commit
080ec26afb
|
@ -30,7 +30,9 @@ require 'msf/core/exploit/ntlm'
|
||||||
require 'msf/core/exploit/dcerpc'
|
require 'msf/core/exploit/dcerpc'
|
||||||
require 'msf/core/exploit/smb/client'
|
require 'msf/core/exploit/smb/client'
|
||||||
require 'msf/core/exploit/smb/client/authenticated'
|
require 'msf/core/exploit/smb/client/authenticated'
|
||||||
|
require 'msf/core/exploit/smb/client/local_paths'
|
||||||
require 'msf/core/exploit/smb/client/psexec'
|
require 'msf/core/exploit/smb/client/psexec'
|
||||||
|
require 'msf/core/exploit/smb/client/remote_paths'
|
||||||
require 'msf/core/exploit/smb/server'
|
require 'msf/core/exploit/smb/server'
|
||||||
require 'msf/core/exploit/smb/server/share'
|
require 'msf/core/exploit/smb/server/share'
|
||||||
require 'msf/core/exploit/ftp'
|
require 'msf/core/exploit/ftp'
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
# -*- coding: binary -*-
|
||||||
|
|
||||||
|
module Msf
|
||||||
|
# Mixin for handling options related to local files in SMB modules
|
||||||
|
module Exploit::Remote::SMB::Client::LocalPaths
|
||||||
|
def initialize(info = {})
|
||||||
|
super
|
||||||
|
register_options(
|
||||||
|
[
|
||||||
|
OptString.new('LPATH', [false, 'The path of the local file to utilize']),
|
||||||
|
OptPath.new('FILE_LPATHS', [false, 'A file containing a list of local files to utilize'])
|
||||||
|
], self.class)
|
||||||
|
end
|
||||||
|
|
||||||
|
def setup
|
||||||
|
unless (datastore['FILE_LPATHS'] && !datastore['LPATH']) || (!datastore['FILE_LPATHS'] && datastore['LPATH'])
|
||||||
|
fail_with(::Msf::Module::Failure::BadConfig, 'One and only one of FILE_LPATHS or LPATH must be specified')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def local_paths
|
||||||
|
if datastore['FILE_LPATHS']
|
||||||
|
IO.readlines(datastore['FILE_LPATHS']).map(&:strip)
|
||||||
|
elsif datastore['LPATH']
|
||||||
|
[datastore['LPATH']]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,29 @@
|
||||||
|
# -*- coding: binary -*-
|
||||||
|
|
||||||
|
module Msf
|
||||||
|
# Mixin for handling options related to remote files in SMB modules
|
||||||
|
module Exploit::Remote::SMB::Client::RemotePaths
|
||||||
|
def initialize(info = {})
|
||||||
|
super
|
||||||
|
register_options(
|
||||||
|
[
|
||||||
|
OptString.new('RPATH', [false, 'The name of the remote file relative to the share to operate on']),
|
||||||
|
OptPath.new('FILE_RPATHS', [false, 'A file containing a list remote files relative to the share to operate on'])
|
||||||
|
], self.class)
|
||||||
|
end
|
||||||
|
|
||||||
|
def setup
|
||||||
|
unless (datastore['FILE_RPATHS'] && !datastore['RPATH']) || (!datastore['FILE_RPATHS'] && datastore['RPATH'])
|
||||||
|
fail_with(::Msf::Module::Failure::BadConfig, 'One and only one of FILE_RPATHS or RPATH must be specified')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def remote_paths
|
||||||
|
if datastore['FILE_RPATHS']
|
||||||
|
IO.readlines(datastore['FILE_RPATHS']).map(&:strip)
|
||||||
|
elsif datastore['RPATH']
|
||||||
|
[datastore['RPATH']]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -10,7 +10,9 @@ class Metasploit3 < Msf::Auxiliary
|
||||||
# Exploit mixins should be called first
|
# Exploit mixins should be called first
|
||||||
include Msf::Exploit::Remote::SMB::Client
|
include Msf::Exploit::Remote::SMB::Client
|
||||||
include Msf::Exploit::Remote::SMB::Client::Authenticated
|
include Msf::Exploit::Remote::SMB::Client::Authenticated
|
||||||
|
include Msf::Exploit::Remote::SMB::Client::RemotePaths
|
||||||
include Msf::Auxiliary::Report
|
include Msf::Auxiliary::Report
|
||||||
|
include Msf::Auxiliary::Scanner
|
||||||
|
|
||||||
# Aliases for common classes
|
# Aliases for common classes
|
||||||
SIMPLE = Rex::Proto::SMB::SimpleClient
|
SIMPLE = Rex::Proto::SMB::SimpleClient
|
||||||
|
@ -34,32 +36,41 @@ class Metasploit3 < Msf::Auxiliary
|
||||||
)
|
)
|
||||||
|
|
||||||
register_options([
|
register_options([
|
||||||
OptString.new('SMBSHARE', [true, 'The name of a share on the RHOST', 'C$']),
|
OptString.new('SMBSHARE', [true, 'The name of a share on the RHOST', 'C$'])
|
||||||
OptString.new('RPATH', [true, 'The name of the remote file relative to the share'])
|
|
||||||
], self.class)
|
], self.class)
|
||||||
end
|
end
|
||||||
|
|
||||||
def smb_delete_file
|
def peer
|
||||||
print_status("Connecting to the server...")
|
"#{rhost}:#{rport}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def smb_delete_files
|
||||||
|
vprint_status("#{peer}: Connecting to the server...")
|
||||||
connect()
|
connect()
|
||||||
smb_login()
|
smb_login()
|
||||||
|
|
||||||
print_status("Mounting the remote share \\\\#{datastore['RHOST']}\\#{datastore['SMBSHARE']}'...")
|
vprint_status("#{peer}: Mounting the remote share \\\\#{datastore['RHOST']}\\#{datastore['SMBSHARE']}'...")
|
||||||
self.simple.connect("\\\\#{rhost}\\#{datastore['SMBSHARE']}")
|
self.simple.connect("\\\\#{rhost}\\#{datastore['SMBSHARE']}")
|
||||||
|
|
||||||
simple.delete("\\#{datastore['RPATH']}")
|
remote_paths.each do |remote_path|
|
||||||
|
begin
|
||||||
|
simple.delete("\\#{remote_path}")
|
||||||
|
|
||||||
# If there's no exception raised at this point, we assume the file has been removed.
|
# If there's no exception raised at this point, we assume the file has been removed.
|
||||||
print_status("File deleted: #{datastore['RPATH']}...")
|
print_good("#{peer}: Deleted: #{remote_path}")
|
||||||
end
|
|
||||||
|
|
||||||
def run
|
|
||||||
begin
|
|
||||||
smb_delete_file
|
|
||||||
rescue Rex::Proto::SMB::Exceptions::LoginError => e
|
|
||||||
print_error("Unable to login: #{e.message}")
|
|
||||||
rescue Rex::Proto::SMB::Exceptions::ErrorCode => e
|
rescue Rex::Proto::SMB::Exceptions::ErrorCode => e
|
||||||
print_error("Cannot delete the file: #{e.message}")
|
elog("#{e.class} #{e.message}\n#{e.backtrace * "\n"}")
|
||||||
|
print_error("#{peer}: Cannot delete #{remote_path}: #{e.message}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def run_host(_ip)
|
||||||
|
begin
|
||||||
|
smb_delete_files
|
||||||
|
rescue Rex::Proto::SMB::Exceptions::LoginError => e
|
||||||
|
elog("#{e.class} #{e.message}\n#{e.backtrace * "\n"}")
|
||||||
|
print_error("#{peer}: Unable to login: #{e.message}")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,9 @@ class Metasploit3 < Msf::Auxiliary
|
||||||
# Exploit mixins should be called first
|
# Exploit mixins should be called first
|
||||||
include Msf::Exploit::Remote::SMB::Client
|
include Msf::Exploit::Remote::SMB::Client
|
||||||
include Msf::Exploit::Remote::SMB::Client::Authenticated
|
include Msf::Exploit::Remote::SMB::Client::Authenticated
|
||||||
|
include Msf::Exploit::Remote::SMB::Client::RemotePaths
|
||||||
include Msf::Auxiliary::Report
|
include Msf::Auxiliary::Report
|
||||||
|
include Msf::Auxiliary::Scanner
|
||||||
|
|
||||||
# Aliases for common classes
|
# Aliases for common classes
|
||||||
SIMPLE = Rex::Proto::SMB::SimpleClient
|
SIMPLE = Rex::Proto::SMB::SimpleClient
|
||||||
|
@ -34,42 +36,50 @@ class Metasploit3 < Msf::Auxiliary
|
||||||
)
|
)
|
||||||
|
|
||||||
register_options([
|
register_options([
|
||||||
OptString.new('SMBSHARE', [true, 'The name of a share on the RHOST', 'C$']),
|
OptString.new('SMBSHARE', [true, 'The name of a share on the RHOST', 'C$'])
|
||||||
OptString.new('RPATH', [true, 'The name of the remote file relative to the share'])
|
|
||||||
], self.class)
|
], self.class)
|
||||||
|
end
|
||||||
|
|
||||||
|
def peer
|
||||||
|
"#{rhost}:#{rport}"
|
||||||
end
|
end
|
||||||
|
|
||||||
def smb_download
|
def smb_download
|
||||||
print_status("Connecting to the #{rhost}:#{rport}...")
|
vprint_status("#{peer}: Connecting...")
|
||||||
connect()
|
connect()
|
||||||
smb_login()
|
smb_login()
|
||||||
|
|
||||||
print_status("Mounting the remote share \\\\#{datastore['RHOST']}\\#{datastore['SMBSHARE']}'...")
|
vprint_status("#{peer}: Mounting the remote share \\\\#{rhost}\\#{datastore['SMBSHARE']}'...")
|
||||||
self.simple.connect("\\\\#{rhost}\\#{datastore['SMBSHARE']}")
|
self.simple.connect("\\\\#{rhost}\\#{datastore['SMBSHARE']}")
|
||||||
|
|
||||||
print_status("Trying to download #{datastore['RPATH']}...")
|
remote_paths.each do |remote_path|
|
||||||
|
begin
|
||||||
|
vprint_status("#{peer}: Trying to download #{remote_path}...")
|
||||||
|
|
||||||
data = ''
|
data = ''
|
||||||
fd = simple.open("\\#{datastore['RPATH']}", 'ro')
|
fd = simple.open("\\#{remote_path}", 'ro')
|
||||||
begin
|
begin
|
||||||
data = fd.read
|
data = fd.read
|
||||||
ensure
|
ensure
|
||||||
fd.close
|
fd.close
|
||||||
end
|
end
|
||||||
|
|
||||||
fname = datastore['RPATH'].split("\\")[-1]
|
fname = remote_path.split("\\")[-1]
|
||||||
path = store_loot("smb.shares.file", "application/octet-stream", rhost, data, fname)
|
path = store_loot("smb.shares.file", "application/octet-stream", rhost, data, fname)
|
||||||
print_good("#{fname} saved as: #{path}")
|
print_good("#{peer}: #{remote_path} saved as: #{path}")
|
||||||
|
rescue Rex::Proto::SMB::Exceptions::ErrorCode => e
|
||||||
|
elog("#{e.class} #{e.message}\n#{e.backtrace * "\n"}")
|
||||||
|
print_error("#{peer} Unable to download #{remote_path}: #{e.message}")
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def run
|
def run_host(ip)
|
||||||
begin
|
begin
|
||||||
smb_download
|
smb_download
|
||||||
rescue Rex::Proto::SMB::Exceptions::LoginError => e
|
rescue Rex::Proto::SMB::Exceptions::LoginError => e
|
||||||
print_error("Unable to login: #{e.message}")
|
elog("#{e.class} #{e.message}\n#{e.backtrace * "\n"}")
|
||||||
rescue Rex::Proto::SMB::Exceptions::ErrorCode => e
|
print_error("#{peer} Unable to login: #{e.message}")
|
||||||
print_error("Unable to download the file: #{e.message}")
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,11 @@ class Metasploit3 < Msf::Auxiliary
|
||||||
|
|
||||||
# Exploit mixins should be called first
|
# Exploit mixins should be called first
|
||||||
include Msf::Exploit::Remote::SMB::Client
|
include Msf::Exploit::Remote::SMB::Client
|
||||||
|
include Msf::Exploit::Remote::SMB::Client::Authenticated
|
||||||
|
include Msf::Exploit::Remote::SMB::Client::LocalPaths
|
||||||
|
include Msf::Exploit::Remote::SMB::Client::RemotePaths
|
||||||
include Msf::Auxiliary::Report
|
include Msf::Auxiliary::Report
|
||||||
|
include Msf::Auxiliary::Scanner
|
||||||
|
|
||||||
# Aliases for common classes
|
# Aliases for common classes
|
||||||
SIMPLE = Rex::Proto::SMB::SimpleClient
|
SIMPLE = Rex::Proto::SMB::SimpleClient
|
||||||
|
@ -38,32 +42,43 @@ class Metasploit3 < Msf::Auxiliary
|
||||||
)
|
)
|
||||||
|
|
||||||
register_options([
|
register_options([
|
||||||
OptString.new('SMBSHARE', [true, 'The name of a writeable share on the server', 'C$']),
|
OptString.new('SMBSHARE', [true, 'The name of a writeable share on the server', 'C$'])
|
||||||
OptString.new('RPATH', [true, 'The name of the remote file relative to the share']),
|
|
||||||
OptString.new('LPATH', [true, 'The path of the local file to upload'])
|
|
||||||
], self.class)
|
], self.class)
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def run
|
def peer
|
||||||
|
"#{rhost}:#{rport}"
|
||||||
|
end
|
||||||
|
|
||||||
data = ::File.read(datastore['LPATH'], ::File.size(datastore['LPATH']))
|
def run_host(_ip)
|
||||||
print_status("Read #{data.length} bytes from #{datastore['LPATH']}...")
|
begin
|
||||||
|
vprint_status("#{peer}: Connecting to the server...")
|
||||||
print_status("Connecting to the server...")
|
|
||||||
connect()
|
connect()
|
||||||
smb_login()
|
smb_login()
|
||||||
|
|
||||||
print_status("Mounting the remote share \\\\#{datastore['RHOST']}\\#{datastore['SMBSHARE']}'...")
|
vprint_status("#{peer}: Mounting the remote share \\\\#{datastore['RHOST']}\\#{datastore['SMBSHARE']}'...")
|
||||||
self.simple.connect("\\\\#{rhost}\\#{datastore['SMBSHARE']}")
|
self.simple.connect("\\\\#{rhost}\\#{datastore['SMBSHARE']}")
|
||||||
|
|
||||||
print_status("Trying to upload #{datastore['RPATH']}...")
|
remote_path = remote_paths.first
|
||||||
|
local_paths.each do |local_path|
|
||||||
|
begin
|
||||||
|
vprint_status("#{peer}: Trying to upload #{local_path} to #{remote_path}...")
|
||||||
|
|
||||||
fd = simple.open("\\#{datastore['RPATH']}", 'rwct')
|
fd = simple.open("\\#{remote_path}", 'rwct')
|
||||||
|
data = ::File.read(datastore['LPATH'], ::File.size(datastore['LPATH']))
|
||||||
fd.write(data)
|
fd.write(data)
|
||||||
fd.close
|
fd.close
|
||||||
|
|
||||||
print_status("The file has been uploaded to #{datastore['RPATH']}...")
|
print_good("#{peer}: #{local_path} uploaded to #{remote_path}")
|
||||||
|
rescue Rex::Proto::SMB::Exceptions::ErrorCode => e
|
||||||
|
elog("#{e.class} #{e.message}\n#{e.backtrace * "\n"}")
|
||||||
|
print_error("#{peer} Unable to upload #{local_path} to #{remote_path} : #{e.message}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
rescue Rex::Proto::SMB::Exceptions::LoginError => e
|
||||||
|
elog("#{e.class} #{e.message}\n#{e.backtrace * "\n"}")
|
||||||
|
print_error("#{peer} Unable to login: #{e.message}")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
# -*- coding:binary -*-
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
require 'msf/core'
|
||||||
|
require 'msf/core/data_store'
|
||||||
|
|
||||||
|
describe Msf::Exploit::Remote::SMB::Client::LocalPaths do
|
||||||
|
subject do
|
||||||
|
mod = ::Msf::Module.new
|
||||||
|
mod.extend described_class
|
||||||
|
mod
|
||||||
|
end
|
||||||
|
|
||||||
|
before(:all) do
|
||||||
|
prefix = "local_#{Rex::Text.rand_text_alpha(10)}"
|
||||||
|
# create a single random file to be used for LPATH
|
||||||
|
@lpath = prefix
|
||||||
|
# create file containing several random file names to be used for FILE_LPATHS
|
||||||
|
@indices = Array(0..(1 + rand(5))).map(&:to_s)
|
||||||
|
@file_lpaths = Tempfile.new(prefix)
|
||||||
|
|
||||||
|
File.open(@file_lpaths, 'wb') do |f|
|
||||||
|
@indices.map do |i|
|
||||||
|
f.puts(i)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#setup' do
|
||||||
|
context 'when PATH and FILE_LPATHS are not set correctly' do
|
||||||
|
it 'raises if both are set' do
|
||||||
|
subject.datastore['LPATH'] = @lpath
|
||||||
|
subject.datastore['FILE_LPATHS'] = @file_lpaths
|
||||||
|
expect { subject.setup }.to raise_error(RuntimeError, /bad\-config/)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should raise if neither are set' do
|
||||||
|
expect { subject.setup }.to raise_error(RuntimeError, /bad\-config/)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#local_paths' do
|
||||||
|
context 'when LPATH and FILE_LPATHS are set correctly' do
|
||||||
|
it 'returns one remote file if LPATH is set' do
|
||||||
|
subject.datastore['LPATH'] = @lpath
|
||||||
|
expect(subject.local_paths).to eql([@lpath])
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns all files if FILE_LPATHS is set' do
|
||||||
|
subject.datastore['FILE_LPATHS'] = @file_lpaths
|
||||||
|
expect(subject.local_paths).to eql(@indices)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
after(:all) do
|
||||||
|
@file_lpaths.unlink
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,61 @@
|
||||||
|
# -*- coding:binary -*-
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
require 'msf/core'
|
||||||
|
require 'msf/core/data_store'
|
||||||
|
|
||||||
|
describe Msf::Exploit::Remote::SMB::Client::RemotePaths do
|
||||||
|
subject do
|
||||||
|
mod = ::Msf::Module.new
|
||||||
|
mod.extend described_class
|
||||||
|
mod
|
||||||
|
end
|
||||||
|
|
||||||
|
before(:all) do
|
||||||
|
prefix = "remote_#{Rex::Text.rand_text_alpha(10)}"
|
||||||
|
# create a single random file to be used for RPATH
|
||||||
|
@rpath = prefix
|
||||||
|
# create file containing several random file names to be used for FILE_RPATHS
|
||||||
|
@indices = Array(0..(1 + rand(5))).map(&:to_s)
|
||||||
|
@file_rpaths = Tempfile.new(prefix)
|
||||||
|
|
||||||
|
File.open(@file_rpaths, 'wb') do |f|
|
||||||
|
@indices.map do |i|
|
||||||
|
f.puts(i)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#setup' do
|
||||||
|
context 'when RPATH and FILE_RPATHS are not set correctly' do
|
||||||
|
it 'raises if both are set' do
|
||||||
|
subject.datastore['RPATH'] = @rpath
|
||||||
|
subject.datastore['FILE_RPATHS'] = @file_rpaths
|
||||||
|
expect { subject.setup }.to raise_error(RuntimeError, /bad\-config/)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'raises if neither are set' do
|
||||||
|
expect { subject.setup }.to raise_error(RuntimeError, /bad\-config/)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#remote_paths' do
|
||||||
|
context 'when RPATH and FILE_RPATHS are set correctly' do
|
||||||
|
it 'returns one remote file if rpath is set' do
|
||||||
|
subject.datastore['RPATH'] = @rpath
|
||||||
|
expect(subject.remote_paths).to eql([@rpath])
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns all files if FILE_RPATHS is set' do
|
||||||
|
subject.datastore['FILE_RPATHS'] = @file_rpaths
|
||||||
|
expect(subject.remote_paths).to eql(@indices)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
after(:all) do
|
||||||
|
@file_rpaths.unlink
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in New Issue