Add share enumeration methods to the SMB mixin

bug/bundler_fix
HD Moore 2017-05-26 17:01:18 -05:00
parent 072ab7291c
commit 8caaba01f1
1 changed files with 195 additions and 0 deletions

View File

@ -638,6 +638,201 @@ module Msf
lang
end
# Map an integer share type to a human friendly descriptor
def smb_lookup_share_type(val)
[ 'DISK', 'PRINTER', 'DEVICE', 'IPC', 'SPECIAL', 'TEMPORARY' ][val]
end
# Retrieve detailed information about a specific share using any available method
def smb_netsharegetinfo(share)
smb_srvsvc_netsharegetinfo(share)
end
# Retrieve detailed share dinformation via the NetShareGetInfo function in the Server Service
def smb_srvsvc_netsharegetinfo(share)
shares = []
simple.connect("\\\\#{rhost}\\IPC$")
handle = dcerpc_handle('4b324fc8-1670-01d3-1278-5a47bf6ee188', '3.0', 'ncacn_np', ["\\srvsvc"])
begin
dcerpc_bind(handle)
rescue Rex::Proto::SMB::Exceptions::ErrorCode => e
vprint_error(e.message)
return []
end
stubdata =
NDR.uwstring("\\\\#{rhost}") +
NDR.wstring(share) +
NDR.long(2)
response = dcerpc.call(0x10, stubdata)
if ! response
raise RuntimeError, "Invalid DCERPC response: <empty>"
end
head = response.slice!(0, 40)
if head.length != 40
raise RuntimeError, "Invalid DCERPC response: not enough data"
end
share_info = {
share_type: head[12, 4].unpack('V').first,
permissions: head[20, 4].unpack('V').first,
max_users: head[24, 4].unpack('V').first,
}
idx = 0
[:share, :comment, :path, :password].each do |field|
field_info = response[idx, 12].unpack("V*")
break if field_info.length == 0
idx += 12
field_text = response[idx, field_info.first * 2]
share_info[ field ] = field_text.gsub("\x00", '')
idx += (field_info.first * 2)
idx += (idx % 4)
end
share_info
end
# Retreive a list of all shares using any available method
def smb_netshareenumall
begin
return smb_srvsvc_netshareenumall
rescue Rex::Proto::SMB::Exceptions::ErrorCode => e
vprint_error("Warning: NetShareEnumAll failed via Server Service, falling back to LANMAN: #{e}")
fail_with(Failure::NoTarget, "No matching target")
return smb_lanman_netshareenumall
end
end
# Retrieve a list of shares via the NetShareEnumAll function in the Server Service
def smb_srvsvc_netshareenumall
shares = []
simple.connect("\\\\#{rhost}\\IPC$")
handle = dcerpc_handle('4b324fc8-1670-01d3-1278-5a47bf6ee188', '3.0', 'ncacn_np', ["\\srvsvc"])
begin
dcerpc_bind(handle)
rescue Rex::Proto::SMB::Exceptions::ErrorCode => e
vprint_error(e.message)
return []
end
stubdata =
NDR.uwstring("\\\\#{rhost}") +
NDR.long(1) #level
ref_id = stubdata[0,4].unpack("V")[0]
ctr = [1, ref_id + 4 , 0, 0].pack("VVVV")
stubdata << ctr
stubdata << NDR.align(ctr)
stubdata << ["FFFFFFFF"].pack("H*")
stubdata << [ref_id + 8, 0].pack("VV")
response = dcerpc.call(0x0f, stubdata)
res = response.dup
win_error = res.slice!(-4, 4).unpack("V")[0]
if win_error != 0
raise RuntimeError, "Invalid DCERPC response: win_error = #{win_error}"
end
# Remove unused data
res.slice!(0,12) # level, CTR header, Reference ID of CTR
share_count = res.slice!(0, 4).unpack("V")[0]
res.slice!(0,4) # Reference ID of CTR1
share_max_count = res.slice!(0, 4).unpack("V")[0]
if share_max_count != share_count
raise RuntimeError, "Invalid DCERPC response: count != count max (#{share_count}/#{share_max_count})"
end
# RerenceID / Type / ReferenceID of Comment
types = res.slice!(0, share_count * 12).scan(/.{12}/n).map{|a| a[4,2].unpack("v")[0]}
share_count.times do |t|
length, offset, max_length = res.slice!(0, 12).unpack("VVV")
if offset != 0
raise RuntimeError, "Invalid DCERPC response: offset != 0 (#{offset})"
end
if length != max_length
raise RuntimeError, "Invalid DCERPC response: length !=max_length (#{length}/#{max_length})"
end
name = res.slice!(0, 2 * length).gsub('\x00','')
res.slice!(0,2) if length % 2 == 1 # pad
comment_length, comment_offset, comment_max_length = res.slice!(0, 12).unpack("VVV")
if comment_offset != 0
raise RuntimeError, "Invalid DCERPC response: comment_offset != 0 (#{comment_offset})"
end
if comment_length != comment_max_length
raise RuntimeError, "Invalid DCERPC response: comment_length != comment_max_length (#{comment_length}/#{comment_max_length})"
end
comment = res.slice!(0, 2 * comment_length)
res.slice!(0,2) if comment_length % 2 == 1 # pad
name = Rex::Text.to_ascii(name).gsub("\x00", "")
s_type = Rex::Text.to_ascii(smb_lookup_share_type(types[t])).gsub("\x00", "")
comment = Rex::Text.to_ascii(comment).gsub("\x00", "")
shares << [ name, s_type, comment ]
end
shares
end
# Retrieve a list of shares via the NetShareEnumAll function in the LANMAN service
# This method can only return shares with names 12 bytes or less
def smb_lanman_netshareenumall
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, smb_lookup_share_type(stype), scomm]
end
shares
end
# @return [Rex::Proto::SMB::SimpleClient]
attr_accessor :simple
end