1
0
mirror of https://github.com/nmap/nmap.git synced 2026-01-03 05:09:14 +00:00

Importing changes from my branch. There are two major updates:

1) I wrote a function that formats output from scripts in a consistent way. Although we haven't really come to a concensus on how it should look yet, it's easy to change when we do. 
2) New script: smb-enum-groups.nse. Enumerate the local groups on a system and their membership.
This commit is contained in:
ron
2009-11-20 16:05:06 +00:00
parent d4756993bd
commit 08da8db7f0
26 changed files with 1599 additions and 1424 deletions

View File

@@ -1326,6 +1326,280 @@ function samr_querydomaininfo2(smbstate, domain_handle, level)
return true, result
end
---Call the <code>EnumDomainAliases</code> function, which retrieves a list of groups for a given domain
--
--@param smbstate The SMB state table
--@param domain_handle The domain_handle, returned by <code>samr_opendomain</code>
--@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values.
function samr_enumdomainaliases(smbstate, domain_handle)
local i, j
local status, result
local arguments
local pos, align
arguments = ''
-- [in] policy_handle *domain_handle,
arguments = arguments .. msrpctypes.marshall_policy_handle(domain_handle)
-- [in,out,ref] uint32 *resume_handle,
arguments = arguments .. msrpctypes.marshall_int32_ptr(nil)
-- [out,ref] samr_SamArray **sam,
-- [in] uint32 max_size, (note: Wireshark says this is flags. Either way..)
arguments = arguments .. msrpctypes.marshall_int32(0x400)
-- [out,ref] uint32 *num_entries
-- Do the call
status, result = call_function(smbstate, 0x0f, arguments)
if(status ~= true) then
return false, result
end
-- Make arguments easier to use
arguments = result['arguments']
pos = 1
-- [in] policy_handle *domain_handle,
-- [in,out,ref] uint32 *resume_handle,
pos, result['resume_handle'] = msrpctypes.unmarshall_int32(arguments, pos)
-- [out,ref] samr_SamArray **sam,
pos, result['sam'] = msrpctypes.unmarshall_samr_SamArray_ptr(arguments, pos)
-- [in] uint32 max_size,
-- [out,ref] uint32 *num_entries
pos, result['num_entries'] = msrpctypes.unmarshall_int32(arguments, pos)
pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos)
if(result['return'] == nil) then
return false, "Read off the end of the packet (samr.enumdomainaliases)"
end
if(result['return'] ~= 0) then
return false, smb.get_status_name(result['return']) .. " (samr.enumdomainaliases)"
end
return true, result
end
---Call the <code>EnumDomainAliases</code> function, which retrieves a list of groups for a given domain
--
--@param smbstate The SMB state table
--@param domain_handle The domain_handle, returned by <code>samr_opendomain</code>
--@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values.
function samr_lookupnames(smbstate, domain_handle, names)
local i, j
local status, result
local arguments
local pos, align
arguments = ''
-- [in,ref] policy_handle *domain_handle,
arguments = arguments .. msrpctypes.marshall_policy_handle(domain_handle)
-- [in,range(0,1000)] uint32 num_names,
arguments = arguments .. msrpctypes.marshall_int32(#names)
-- [in,size_is(1000),length_is(num_names)] lsa_String names[],
arguments = arguments .. msrpctypes.marshall_lsa_String_array2(names)
-- [out,ref] samr_Ids *rids,
-- [out,ref] samr_Ids *types
-- Do the call
status, result = call_function(smbstate, 0x11, arguments)
if(status ~= true) then
return false, result
end
-- Make arguments easier to use
arguments = result['arguments']
pos = 1
-- [in,ref] policy_handle *domain_handle,
-- [in,range(0,1000)] uint32 num_names,
-- [in,size_is(1000),length_is(num_names)] lsa_String names[],
-- [out,ref] samr_Ids *rids,
pos, result['rids'] = msrpctypes.unmarshall_samr_Ids(arguments, pos)
-- [out,ref] samr_Ids *types
pos, result['types'] = msrpctypes.unmarshall_samr_Ids(arguments, pos)
pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos)
if(result['return'] == nil) then
return false, "Read off the end of the packet (samr.lookupnames)"
end
if(result['return'] == smb.status_codes['NT_STATUS_NONE_MAPPED']) then
return false, "Couldn't find any names the host recognized"
end
if(result['return'] ~= 0 and result['return'] ~= smb.status_codes['NT_STATUS_SOME_NOT_MAPPED']) then
return false, smb.get_status_name(result['return']) .. " (samr.lookupnames)"
end
return true, result
end
---Call the <code>OpenAlias</code> function, which gets a handle to a group.
--
--@param smbstate The SMB state table
--@param domain_handle The domain_handle, returned by <code>samr_opendomain</code>
--@param rid The RID of the alias
--@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values.
function samr_openalias(smbstate, domain_handle, rid)
local i, j
local status, result
local arguments
local pos, align
arguments = ''
-- [in,ref] policy_handle *domain_handle,
arguments = arguments .. msrpctypes.marshall_policy_handle(domain_handle)
-- [in] samr_AliasAccessMask access_mask,
arguments = arguments .. msrpctypes.marshall_int32(0x0002000c) -- Full read permission
-- [in] uint32 rid,
arguments = arguments .. msrpctypes.marshall_int32(rid)
-- [out,ref] policy_handle *alias_handle
-- Do the call
status, result = call_function(smbstate, 0x1b, arguments)
if(status ~= true) then
return false, result
end
-- Make arguments easier to use
arguments = result['arguments']
pos = 1
-- [in,ref] policy_handle *domain_handle,
-- [in] samr_AliasAccessMask access_mask,
-- [in] uint32 rid,
-- [out,ref] policy_handle *alias_handle
pos, result['alias_handle'] = msrpctypes.unmarshall_policy_handle(arguments, pos)
pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos)
if(result['return'] == nil) then
return false, "Read off the end of the packet (samr.openalias)"
end
if(result['return'] ~= 0) then
return false, smb.get_status_name(result['return']) .. " (samr.openalias)"
end
return true, result
end
---Call the <code>GetMembersInAlias</code> function, which retrieves a list of users in
-- a group.
--
--@param smbstate The SMB state table
--@param alias_handle The alias_handle, returned by <code>samr_openalias</code>
--@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values.
function samr_getmembersinalias(smbstate, alias_handle)
local i, j
local status, result
local arguments
local pos, align
arguments = ''
-- [in,ref] policy_handle *alias_handle,
arguments = arguments .. msrpctypes.marshall_policy_handle(alias_handle)
-- [out,ref] lsa_SidArray *sids
-- Do the call
status, result = call_function(smbstate, 0x21, arguments)
if(status ~= true) then
return false, result
end
-- Make arguments easier to use
arguments = result['arguments']
pos = 1
-- [in,ref] policy_handle *alias_handle,
-- [out,ref] lsa_SidArray *sids
pos, result['sids'] = msrpctypes.unmarshall_lsa_SidArray(arguments, pos)
pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos)
if(result['return'] == nil) then
return false, "Read off the end of the packet (samr.getmembersinalias)"
end
if(result['return'] ~= 0) then
return false, smb.get_status_name(result['return']) .. " (samr.getmembersinalias)"
end
return true, result
end
---Call the <code>LookupRids</code> function, which converts a list of RIDs to
-- names.
--
--NOTE: This doesn't appear to work (it generates a fault, despite the packet being properly formatted).
--if you ever feel like you need this function, check out <code>lsa_lookupsids2</code>.
--
--@param smbstate The SMB state table
--@param domain_handle The domain_handle, returned by <code>samr_opendomain</code>
--@param rids An array of RIDs to look up
--@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values.
--function samr_lookuprids(smbstate, domain_handle, rids)
-- local i, j
-- local status, result
-- local arguments
-- local pos, align
--
-- arguments = ''
--
---- [in,ref] policy_handle *domain_handle,
-- arguments = arguments .. msrpctypes.marshall_policy_handle(domain_handle)
---- [in,range(0,1000)] uint32 num_rids,
-- arguments = arguments .. msrpctypes.marshall_int32(#rids)
---- [in,size_is(1000),length_is(num_rids)] uint32 rids[],
-- arguments = arguments .. msrpctypes.marshall_int32_array(rids)
---- [out,ref] lsa_Strings *names,
---- [out,ref] samr_Ids *types
--
--
-- -- Do the call
-- status, result = call_function(smbstate, 0x12, arguments)
-- if(status ~= true) then
-- return false, result
-- end
--
-- -- Make arguments easier to use
-- arguments = result['arguments']
-- pos = 1
--
---- [in,ref] policy_handle *domain_handle,
---- [in,range(0,1000)] uint32 num_rids,
---- [in,size_is(1000),length_is(num_rids)] uint32 rids[],
---- [out,ref] lsa_Strings *names,
---- [out,ref] samr_Ids *types
--
--
-- pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos)
--stdnse.print_debug("Return = %08x\n", result['return'])
-- if(result['return'] == nil) then
-- return false, "Read off the end of the packet (samr.getmembersinalias)"
-- end
-- if(result['return'] ~= 0) then
-- return false, smb.get_status_name(result['return']) .. " (samr.getmembersinalias)"
-- end
--
-- return true, result
--end
---Call the <code>close</code> function, which closes a handle of any type (for example, domain_handle or connect_handle)
--@param smbstate The SMB state table
--@param handle The handle to close
@@ -2807,8 +3081,6 @@ end
function samr_enum_users(host)
local i, j
stdnse.print_debug(3, "Entering enum_samr()")
local smbstate
local bind_result, connect4_result, enumdomains_result
local connect_handle
@@ -2929,11 +3201,199 @@ function samr_enum_users(host)
-- Stop the SAMR SMB
stop_smb(smbstate)
stdnse.print_debug(3, "Leaving enum_samr()")
return true, response
end
function samr_enum_groups(host)
local i, j
stdnse.print_debug(1, "MSRPC: Attempting to enumerate groups on %s", host.ip)
-- Create the SMB session
local status, smbstate = start_smb(host, SAMR_PATH, true)
if(status == false) then
return false, smbstate
end
-- Bind to SAMR service
local status, bind_result = bind(smbstate, SAMR_UUID, SAMR_VERSION, nil)
if(status == false) then
stop_smb(smbstate)
return false, bind_result
end
-- Call connect4()
local status, connect4_result = samr_connect4(smbstate, host.ip)
if(status == false) then
stop_smb(smbstate)
return false, connect4_result
end
-- Save the connect_handle
local connect_handle = connect4_result['connect_handle']
-- Call EnumDomains()
local status, enumdomains_result = samr_enumdomains(smbstate, connect_handle)
if(status == false) then
stop_smb(smbstate)
return false, enumdomains_result
end
-- If no domains were returned, go back with an error
if(#enumdomains_result['sam']['entries'] == 0) then
stop_smb(smbstate)
return false, "Couldn't find any domains"
end
-- Now, loop through the domains and find the groups
local domains = {}
for _, domain in ipairs(enumdomains_result['sam']['entries']) do
-- Get a handy domain name
domain = domain['name']
domains[domain] = {}
-- Call LookupDomain()
local status, lookupdomain_result = samr_lookupdomain(smbstate, connect_handle, domain)
if(status == false) then
stop_smb(smbstate)
return false, lookupdomain_result
end
-- Save the sid
local domain_sid = lookupdomain_result['sid']
-- Call OpenDomain()
local status, opendomain_result = samr_opendomain(smbstate, connect_handle, domain_sid)
if(status == false) then
stop_smb(smbstate)
return false, opendomain_result
end
-- Save the domain handle
local domain_handle = opendomain_result['domain_handle']
-- Get a list of groups
local status, enumaliases_result = samr_enumdomainaliases(smbstate, domain_handle)
if(status == false) then
stop_smb(smbstate)
return false, "Couldn't enumerate groups: " .. enumaliases_result
end
-- Print some output
stdnse.print_debug(1, "MSRPC: Found %d groups in %s", #enumaliases_result['sam']['entries'], domain)
-- Record the results
local group_rids = {}
for _, group in ipairs(enumaliases_result['sam']['entries']) do
-- The RID
local group_rid = group['idx']
-- Keep a list of just RIDs, for easier lookup after
table.insert(group_rids, group_rid)
-- Save the output, this is what will be returned
domains[domain][group_rid] = {}
domains[domain][group_rid]['name'] = group['name']
end -- Loop over group entries
for _, group_rid in ipairs(group_rids) do
-- Get a handle to the alias
local status, openalias_result = samr_openalias(smbstate, domain_handle, group_rid)
if(not(status)) then
stop_smb(smbstate)
return false, "Couldn't open handle to group: " .. openalias_result
end
local group_handle = openalias_result['alias_handle']
-- Get the members of the group
local status, getmembers_result = samr_getmembersinalias(smbstate, group_handle)
if(not(status)) then
stop_smb(smbstate)
return false, "Couldn't get members in group: " .. getmembers_result
end
-- Save the SIDs
local member_sids = {}
if(getmembers_result and getmembers_result.sids and getmembers_result.sids.sids) then
-- Set the list of member_sids
member_sids = getmembers_result.sids.sids
end
-- Print some output
stdnse.print_debug(1, "MSRPC: Adding group '%s' (RID: %d) with %d members", domains[domain][group_rid]['name'], group_rid, #member_sids)
-- Save the output
domains[domain][group_rid]['member_sids'] = member_sids
-- Close the group
samr_close(smbstate, group_handle)
end -- Loop over group RIDs
-- Close the domain handle
samr_close(smbstate, domain_handle)
end -- Domain loop
-- Close the connect handle
samr_close(smbstate, connect_handle)
-- Stop the SAMR SMB
stop_smb(smbstate)
-- Now, we need a handle to LSA (in order to convert the RIDs to users
-- Create the SMB session
local status, smbstate = start_smb(host, LSA_PATH, true)
if(status == false) then
return false, smbstate
end
-- Bind to LSA service
local status, bind_result = bind(smbstate, LSA_UUID, LSA_VERSION, nil)
if(status == false) then
stop_smb(smbstate)
return false, bind_result
end
-- Open the LSA policy
local status, openpolicy2_result = lsa_openpolicy2(smbstate, host.ip)
if(status == false) then
stop_smb(smbstate)
return false, openpolicy2_result
end
-- Loop through the domains
for domain, domain_data in pairs(domains) do
for group_rid, group in pairs(domain_data) do
-- Look up the SIDs
local status, lookupsids2_result = lsa_lookupsids2(smbstate, openpolicy2_result['policy_handle'], group['member_sids'])
if(status == false) then
stop_smb(smbstate)
return false, "Error looking up RIDs: " .. lookupsids2_result
end
if(lookupsids2_result and lookupsids2_result.names and lookupsids2_result.names.names and (#lookupsids2_result.names.names > 0)) then
local members = {}
for _, resolved_name in ipairs(lookupsids2_result.names.names) do
if(resolved_name.sid_type == "SID_NAME_USER") then
table.insert(members, resolved_name.name)
end
end
domains[domain][group_rid]['members'] = members
else
domains[domain][group_rid]['members'] = {}
end
end
end
-- Close the handle
lsa_close(smbstate, openpolicy2_result['policy_handle'])
stop_smb(smbstate)
return true, domains
end
---Attempt to enumerate users using LSA functions.
--
--@param host The host object.
@@ -2950,8 +3410,6 @@ function lsa_enum_users(host)
local response = {}
local status, smbstate, bind_result, openpolicy2_result, lookupnames2_result, lookupsids2_result
stdnse.print_debug(3, "Entering enum_lsa()")
-- Create the SMB session
status, smbstate = start_smb(host, LSA_PATH, true)
if(status == false) then
@@ -3019,7 +3477,7 @@ function lsa_enum_users(host)
-- Put the details for each name into an array
-- NOTE: Be sure to mirror any changes here in the next bit!
for j = 1, #lookupsids2_result['names']['names'], 1 do
if(lookupsids2_result['names']['names'][j]['sid_type'] ~= "SID_NAME_UNKNOWN") then
if(lookupsids2_result['names']['names'][j]['sid_type'] == "SID_NAME_USER") then
local result = {}
result['name'] = lookupsids2_result['names']['names'][j]['name']
result['rid'] = 500 + j - 1
@@ -3061,7 +3519,7 @@ function lsa_enum_users(host)
-- Check if the username matches the rid (one server we discovered returned every user as valid,
-- this is to prevent that infinite loop)
if(tonumber(name) ~= rid) then
if(lookupsids2_result['names']['names'][j]['sid_type'] ~= "SID_NAME_UNKNOWN") then
if(lookupsids2_result['names']['names'][j]['sid_type'] == "SID_NAME_USER") then
local result = {}
result['name'] = name
result['rid'] = rid
@@ -3096,8 +3554,6 @@ function lsa_enum_users(host)
stop_smb(smbstate)
stdnse.print_debug(3, "Leaving enum_lsa()")
return true, response
end