mirror of
https://github.com/nmap/nmap.git
synced 2026-08-04 06:40:48 +00:00
Merging changes from my experimental branch; the new versions of this scripts, which have significant changes to their core functionality, managed to hold their own against Brandon's network. More testing would be very helpful, though, especially with credentials (most of Brandon's scans were anonymous).
This commit is contained in:
parent
a246aaf469
commit
773000b65a
17 changed files with 1930 additions and 515 deletions
|
|
@ -63,6 +63,8 @@ Entry{ category = "version", filename = "skypev2-version.nse" }
|
|||
Entry{ category = "intrusive", filename = "smb-check-vulns.nse" }
|
||||
Entry{ category = "discovery", filename = "smb-enum-domains.nse" }
|
||||
Entry{ category = "intrusive", filename = "smb-enum-domains.nse" }
|
||||
Entry{ category = "discovery", filename = "smb-enum-processes.nse" }
|
||||
Entry{ category = "intrusive", filename = "smb-enum-processes.nse" }
|
||||
Entry{ category = "discovery", filename = "smb-enum-sessions.nse" }
|
||||
Entry{ category = "intrusive", filename = "smb-enum-sessions.nse" }
|
||||
Entry{ category = "discovery", filename = "smb-enum-shares.nse" }
|
||||
|
|
|
|||
|
|
@ -61,6 +61,9 @@ author = "Ron Bowes"
|
|||
copyright = "Ron Bowes"
|
||||
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
|
||||
categories = {"intrusive"}
|
||||
-- Set the runlevel to >2 so this runs last (so if it DOES crash something, it doesn't
|
||||
-- till other scans have had a chance to run)
|
||||
runlevel = 2
|
||||
|
||||
require 'msrpc'
|
||||
require 'smb'
|
||||
|
|
@ -181,6 +184,7 @@ end
|
|||
|
||||
|
||||
action = function(host)
|
||||
|
||||
local status, result
|
||||
local response = " \n"
|
||||
local found = false
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ hostrule = function(host)
|
|||
end
|
||||
|
||||
action = function(host)
|
||||
|
||||
local response = " \n"
|
||||
local status, smbstate
|
||||
local i, j
|
||||
|
|
|
|||
282
scripts/smb-enum-processes.nse
Normal file
282
scripts/smb-enum-processes.nse
Normal file
|
|
@ -0,0 +1,282 @@
|
|||
|
||||
description = [[
|
||||
Pulls a list of processes from the remote server over SMB (using the remote registry service and
|
||||
HKEY_PERFORMANCE_DATA).
|
||||
|
||||
Requires Administrator access.
|
||||
|
||||
WARNING: I have experienced crashes in regsvc.exe while making registry calls against a fully patched Windows
|
||||
2000 system; I've fixed the issue that caused it, but there's no guarantee that it (or a similar vuln in the
|
||||
same code) won't show up again.
|
||||
]]
|
||||
|
||||
---
|
||||
-- @usage
|
||||
-- nmap --script smb-enum-processes.nse -p445 <host>
|
||||
-- sudo nmap -sU -sS --script smb-enum-processes.nse -p U:137,T:139 <host>
|
||||
--
|
||||
---
|
||||
-- @output
|
||||
-- Host script results:
|
||||
-- |_ smb-enum-processes: Idle, _Total, System, wmiprvse, VMwareUser, VMwareTray, smss, csrss, winlogon, services, lsass, logon.scr, spoolsv, msdtc, VMwareService, svchost, alg, explorer
|
||||
-- --
|
||||
-- Host script results:
|
||||
-- | smb-enum-processes:
|
||||
-- | Idle
|
||||
-- | | PID: 0, Parent: 0 [Idle]
|
||||
-- | | Priority: 0
|
||||
-- | |_Thread Count: 1, Handle Count: 0
|
||||
-- | System
|
||||
-- | | PID: 4, Parent: 0 [Idle]
|
||||
-- | | Priority: 8
|
||||
-- | |_Thread Count: 48, Handle Count: 392
|
||||
-- | VMwareUser
|
||||
-- | | PID: 212, Parent: 1832 [explorer]
|
||||
-- | | Priority: 8
|
||||
-- | |_Thread Count: 1, Handle Count: 45
|
||||
-- | VMwareTray
|
||||
-- | | PID: 240, Parent: 1832 [explorer]
|
||||
-- | | Priority: 8
|
||||
-- | |_Thread Count: 1, Handle Count: 41
|
||||
-- | smss
|
||||
-- | | PID: 252, Parent: 4 [System]
|
||||
-- | | Priority: 11
|
||||
-- | |_Thread Count: 3, Handle Count: 19
|
||||
-- | csrss
|
||||
-- | | PID: 300, Parent: 252 [smss]
|
||||
-- | | Priority: 13
|
||||
-- | |_Thread Count: 10, Handle Count: 347
|
||||
-- | winlogon
|
||||
-- | | PID: 324, Parent: 252 [smss]
|
||||
-- | | Priority: 13
|
||||
-- | |_Thread Count: 18, Handle Count: 513
|
||||
-- | services
|
||||
-- | | PID: 372, Parent: 324 [winlogon]
|
||||
-- | | Priority: 9
|
||||
-- | |_Thread Count: 17, Handle Count: 275
|
||||
-- | lsass
|
||||
-- | | PID: 384, Parent: 324 [winlogon]
|
||||
-- | | Priority: 9
|
||||
-- | |_Thread Count: 29, Handle Count: 415
|
||||
-- | logon.scr
|
||||
-- | | PID: 868, Parent: 324 [winlogon]
|
||||
-- | | Priority: 4
|
||||
-- | |_Thread Count: 1, Handle Count: 22
|
||||
-- ...
|
||||
--
|
||||
-- @args smb* This script supports the <code>smbusername</code>,
|
||||
-- <code>smbpassword</code>, <code>smbhash</code>, and <code>smbtype</code>
|
||||
-- script arguments of the <code>smb</code> module.
|
||||
-----------------------------------------------------------------------
|
||||
|
||||
author = "Ron Bowes"
|
||||
copyright = "Ron Bowes"
|
||||
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
|
||||
categories = {"discovery","intrusive"}
|
||||
|
||||
require "bin"
|
||||
require 'msrpc'
|
||||
require 'msrpcperformance'
|
||||
require 'smb'
|
||||
require 'stdnse'
|
||||
|
||||
-- Strings used to separate processes from one another.
|
||||
local separators = {
|
||||
first = "-+-";
|
||||
last = " `-";
|
||||
middle = " +-";
|
||||
only = "---";
|
||||
}
|
||||
|
||||
function psl_add (psl, ps)
|
||||
-- Add process.
|
||||
psl[ps.pid] = ps
|
||||
|
||||
-- Add dummy parent if no real one exists.
|
||||
if psl[ps.ppid] == nil then
|
||||
psl[ps.ppid] = {
|
||||
name = 'Unknown';
|
||||
pid = ps.ppid;
|
||||
ppid = ps.ppid;
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
function psl_mode (list, i)
|
||||
local mode
|
||||
|
||||
-- Decide connector for process.
|
||||
if table.maxn(list) == 1 then
|
||||
mode = "only"
|
||||
elseif i == 1 then
|
||||
mode = "first"
|
||||
elseif i == table.maxn(list) then
|
||||
mode = "last"
|
||||
else
|
||||
mode = "middle"
|
||||
end
|
||||
|
||||
return mode
|
||||
end
|
||||
|
||||
function psl_print (psl)
|
||||
local result = ""
|
||||
|
||||
-- Find how many root processes there are.
|
||||
local roots = {}
|
||||
for i,ps in pairs(psl) do
|
||||
if psl[ps.ppid] == nil or ps.ppid == ps.pid then
|
||||
table.insert(roots, i)
|
||||
end
|
||||
end
|
||||
table.sort(roots)
|
||||
|
||||
-- Create vertical sibling link.
|
||||
local bars = {}
|
||||
if table.maxn(roots) ~= 1 then
|
||||
table.insert(bars, 2)
|
||||
end
|
||||
|
||||
-- Print out each root of the tree.
|
||||
for i,root in ipairs(roots) do
|
||||
local mode = psl_mode(roots, i)
|
||||
result = result .. psl_tree(psl, root, 0, bars, mode)
|
||||
end
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
function psl_tree (psl, pid, column, bars, mode)
|
||||
local ps = psl[pid]
|
||||
|
||||
-- Delete vertical sibling link.
|
||||
if mode == 'last' then
|
||||
table.remove(bars)
|
||||
end
|
||||
|
||||
-- Print lead-in.
|
||||
local prefix = ''
|
||||
if mode == 'middle' or mode == 'last' then
|
||||
prefix = '\n'
|
||||
|
||||
local i = 1
|
||||
for j = 1, column do
|
||||
if table.maxn(bars) >= i and
|
||||
bars[i] == j then
|
||||
prefix = prefix .. '|'
|
||||
i = i + 1
|
||||
else
|
||||
prefix = prefix .. ' '
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Format process itself.
|
||||
output = separators[mode] .. ps.name .. '(' .. ps.pid .. ')'
|
||||
column = column + #output
|
||||
local result = prefix .. output
|
||||
|
||||
-- Find process' children.
|
||||
local children = {}
|
||||
for child_pid,child in pairs(psl) do
|
||||
if child_pid ~= pid and child.ppid == pid then
|
||||
table.insert(children, child_pid)
|
||||
end
|
||||
end
|
||||
table.sort(children)
|
||||
|
||||
-- Create vertical sibling link between children.
|
||||
if table.maxn(children) > 1 then
|
||||
table.insert(bars, column + 2)
|
||||
end
|
||||
|
||||
-- Format process' children.
|
||||
for i,pid in ipairs(children) do
|
||||
local mode = psl_mode(children, i)
|
||||
result = result .. psl_tree(psl, pid, column, bars, mode)
|
||||
end
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
hostrule = function(host)
|
||||
return smb.get_port(host) ~= nil
|
||||
end
|
||||
|
||||
action = function(host)
|
||||
|
||||
local status, result
|
||||
local process
|
||||
local response = " \n"
|
||||
|
||||
-- Get the process list
|
||||
status, result = msrpcperformance.get_performance_data(host, "230")
|
||||
if(status == false) then
|
||||
if(nmap.debugging() > 0) then
|
||||
return "ERROR: " .. result
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
-- Get the process table
|
||||
process = result['Process']
|
||||
|
||||
-- for i, v in pairs(result['Processor']['_Total']) do
|
||||
-- io.write(string.format("i = %s\n", i))
|
||||
-- end
|
||||
|
||||
-- Put the processes into an array, and sort them by process id
|
||||
local names = {}
|
||||
for i, v in pairs(process) do
|
||||
if(i ~= '_Total') then
|
||||
names[#names + 1] = i
|
||||
end
|
||||
end
|
||||
table.sort(names, function (a, b) return process[a]['ID Process'] < process[b]['ID Process'] end)
|
||||
|
||||
-- Put the processes into an array indexed by process id and with a value equal to the name (so we can look it up
|
||||
-- easily when we need to)
|
||||
local process_id = {}
|
||||
for i, v in pairs(process) do
|
||||
process_id[v['ID Process']] = i
|
||||
end
|
||||
|
||||
|
||||
if(nmap.verbosity() == 1) then
|
||||
local psl = {}
|
||||
for i,name in ipairs(names) do
|
||||
if(name ~= '_Total') then
|
||||
psl_add(psl, {
|
||||
name = name;
|
||||
pid = process[name]['ID Process'];
|
||||
ppid = process[name]['Creating Process ID'];
|
||||
})
|
||||
end
|
||||
end
|
||||
response = ' \n' .. psl_print(psl)
|
||||
elseif(nmap.verbosity() > 0) then
|
||||
for i = 1, #names, 1 do
|
||||
local name = names[i]
|
||||
if(name ~= '_Total') then
|
||||
local parent = process_id[process[name]['Creating Process ID']]
|
||||
if(parent == nil) then
|
||||
parent = "n/a"
|
||||
end
|
||||
|
||||
-- response = response .. string.format("%6d %24s (Parent: %24s, Priority: %4d, Threads: %4d, Handles: %4d)\n", process[name]['ID Process'], name, parent, process[name]['Priority Base'], process[name]['Thread Count'], process[name]['Handle Count'])
|
||||
|
||||
response = response .. string.format("%s [%d]\n", name, process[name]['ID Process'])
|
||||
response = response .. string.format("| Parent: %s [%s]\n", process[name]['Creating Process ID'], parent)
|
||||
response = response .. string.format("| Priority: %s, Thread Count: %s, Handle Count: %s\n", process[name]['Priority Base'], process[name]['Thread Count'], process[name]['Handle Count'])
|
||||
end
|
||||
|
||||
end
|
||||
else
|
||||
response = stdnse.strjoin(", ", names)
|
||||
end
|
||||
|
||||
return response
|
||||
end
|
||||
|
||||
|
||||
|
|
@ -247,6 +247,7 @@ local function winreg_enum_rids(host)
|
|||
end
|
||||
|
||||
action = function(host)
|
||||
|
||||
local response = " \n"
|
||||
|
||||
local status1, status2
|
||||
|
|
|
|||
|
|
@ -191,7 +191,9 @@ function check_shares(host, shares)
|
|||
stdnse.print_debug(3, "EnumShares: Access was denied")
|
||||
denied_shares[#denied_shares + 1] = shares[i]
|
||||
else
|
||||
stdnse.print_debug(3, "ERROR: EnumShares: Share didn't pan out: %s", err)
|
||||
-- If we're here, an error that we weren't prepared for came up.
|
||||
smb.stop(smbstate)
|
||||
return false, string.format("Error while checking shares: %s", err)
|
||||
end
|
||||
else
|
||||
-- Add it to allowed shares
|
||||
|
|
@ -244,6 +246,7 @@ local function get_share_info(host, name)
|
|||
end
|
||||
|
||||
action = function(host)
|
||||
|
||||
local enum_result
|
||||
local result, shared
|
||||
local response = " \n"
|
||||
|
|
|
|||
|
|
@ -9,10 +9,10 @@ can be fine tuned using Nmap parameters. For the most possible information,
|
|||
leave the defaults; however, there are advantages to using them individually.
|
||||
|
||||
Advantages of using SAMR enumeration:
|
||||
* Stealthier (requires one packet/user account, whereas LSA uses at least 20
|
||||
packets; additionally, LSA makes a lot of noise in the Windows event log (LSA
|
||||
enumeration is the only script I (Ron Bowes) have been called on by the
|
||||
administrator of a box I was testing against).
|
||||
* Stealthier (requires one packet/user account, whereas LSA uses at least 10
|
||||
packets while SAMR uses half that; additionally, LSA makes a lot of noise in
|
||||
the Windows event log (LSA enumeration is the only script I (Ron Bowes) have
|
||||
been called on by the administrator of a box I was testing against).
|
||||
* More information is returned (more than just the username).
|
||||
* Every account will be found, since they're being enumerated with a function
|
||||
that's designed to enumerate users.
|
||||
|
|
@ -58,16 +58,11 @@ a user on a domain or system. An LSA function is exposed which lets us convert t
|
|||
(say, 1000) to the username (say, "Ron"). So, the technique will essentially try
|
||||
converting 1000 to a name, then 1001, 1002, etc., until we think we're done.
|
||||
|
||||
To do this, this script breaks users into groups of five RIDs, then checked individually
|
||||
(checking too many at once causes problems). We continue checking until we reach
|
||||
1100, and get an empty group of five. This probably isn't the most effective way, but it
|
||||
seems to work. It might be a good idea to modify this, in the future, with some more
|
||||
intelligence. I (Ron Bowes) performed a test on an old server with a lot of accounts,
|
||||
and these were the active RIDs: 500, 501, 1000, 1030, 1031, 1053, 1054, 1055,
|
||||
1056, 1057, 1058, 1059, 1060, 1061, 1062, 1063, 1064, 1065, 1066, 1067, 1070,
|
||||
1075, 1081, 1088, 1090. The jump from 1000 to 1030 is quite large and can easily
|
||||
result in missing accounts, in an automated check. An ideal solution might be to continue
|
||||
doing groups of 5, but wait until we get 5-10 consecutive empty groups before giving up.
|
||||
To do this, the script breaks users into groups of RIDs based on the <code>LSA_GROUPSIZE</code>
|
||||
constant. All members of this group are checked simultaneously, and the responses recorded.
|
||||
When a series of empty groups are found (<code>LSA_MINEMPTY</code> groups, specifically),
|
||||
the scan ends. As long as you are getting a few groups with active accounts, the scan will
|
||||
continue.
|
||||
|
||||
Before attempting this conversion, the SID of the server has to be determined.
|
||||
The SID is determined by doing the reverse operation; that is, by converting a name into
|
||||
|
|
@ -156,284 +151,8 @@ hostrule = function(host)
|
|||
return smb.get_port(host) ~= nil
|
||||
end
|
||||
|
||||
---Attempt to enumerate users through SAMR methods. See the file description for more information.
|
||||
--
|
||||
--@param host The host object.
|
||||
--@return Status (true or false).
|
||||
--@return Array of user tables (if status is true) or an an error string (if
|
||||
--status is false). Each user table contains the fields <code>name</code>,
|
||||
--<code>domain</code>, <code>fullname</code>, <code>rid</code>, and
|
||||
--<code>description</code>.
|
||||
local function enum_samr(host)
|
||||
local i, j
|
||||
|
||||
stdnse.print_debug(3, "Entering enum_samr()")
|
||||
|
||||
local smbstate
|
||||
local bind_result, connect4_result, enumdomains_result
|
||||
local connect_handle
|
||||
local status, smbstate
|
||||
local response = {}
|
||||
|
||||
-- Create the SMB session
|
||||
status, smbstate = msrpc.start_smb(host, msrpc.SAMR_PATH)
|
||||
|
||||
if(status == false) then
|
||||
return false, smbstate
|
||||
end
|
||||
|
||||
-- Bind to SAMR service
|
||||
status, bind_result = msrpc.bind(smbstate, msrpc.SAMR_UUID, msrpc.SAMR_VERSION, nil)
|
||||
if(status == false) then
|
||||
msrpc.stop_smb(smbstate)
|
||||
return false, bind_result
|
||||
end
|
||||
|
||||
-- Call connect4()
|
||||
status, connect4_result = msrpc.samr_connect4(smbstate, host.ip)
|
||||
if(status == false) then
|
||||
msrpc.stop_smb(smbstate)
|
||||
return false, connect4_result
|
||||
end
|
||||
|
||||
-- Save the connect_handle
|
||||
connect_handle = connect4_result['connect_handle']
|
||||
|
||||
-- Call EnumDomains()
|
||||
status, enumdomains_result = msrpc.samr_enumdomains(smbstate, connect_handle)
|
||||
if(status == false) then
|
||||
msrpc.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
|
||||
msrpc.stop_smb(smbstate)
|
||||
return false, "Couldn't find any domains"
|
||||
end
|
||||
|
||||
-- Now, loop through the domains and find the users
|
||||
for i = 1, #enumdomains_result['sam']['entries'], 1 do
|
||||
|
||||
local domain = enumdomains_result['sam']['entries'][i]['name']
|
||||
-- We don't care about the 'builtin' domain, in all my tests it's empty
|
||||
if(domain ~= 'Builtin') then
|
||||
local sid
|
||||
local domain_handle
|
||||
local opendomain_result, querydisplayinfo_result
|
||||
|
||||
-- Call LookupDomain()
|
||||
status, lookupdomain_result = msrpc.samr_lookupdomain(smbstate, connect_handle, domain)
|
||||
if(status == false) then
|
||||
msrpc.stop_smb(smbstate)
|
||||
return false, lookupdomain_result
|
||||
end
|
||||
|
||||
-- Save the sid
|
||||
sid = lookupdomain_result['sid']
|
||||
|
||||
-- Call OpenDomain()
|
||||
status, opendomain_result = msrpc.samr_opendomain(smbstate, connect_handle, sid)
|
||||
if(status == false) then
|
||||
msrpc.stop_smb(smbstate)
|
||||
return false, opendomain_result
|
||||
end
|
||||
|
||||
-- Save the domain handle
|
||||
domain_handle = opendomain_result['domain_handle']
|
||||
|
||||
-- Loop as long as we're getting valid results
|
||||
j = 0
|
||||
repeat
|
||||
-- Call QueryDisplayInfo()
|
||||
status, querydisplayinfo_result = msrpc.samr_querydisplayinfo(smbstate, domain_handle, j)
|
||||
if(status == false) then
|
||||
msrpc.stop_smb(smbstate)
|
||||
return false, querydisplayinfo_result
|
||||
end
|
||||
|
||||
-- Save the response
|
||||
if(querydisplayinfo_result['return'] ~= 0 and querydisplayinfo_result['info'] ~= nil and querydisplayinfo_result['info']['entries'] ~= nil and querydisplayinfo_result['info']['entries'][1] ~= nil) then
|
||||
local array = {}
|
||||
local k
|
||||
|
||||
-- The reason these are all indexed from '1' is because we request names one at a time.
|
||||
array['domain'] = domain
|
||||
array['name'] = querydisplayinfo_result['info']['entries'][1]['account_name']
|
||||
array['fullname'] = querydisplayinfo_result['info']['entries'][1]['full_name']
|
||||
array['description'] = querydisplayinfo_result['info']['entries'][1]['description']
|
||||
array['rid'] = querydisplayinfo_result['info']['entries'][1]['rid']
|
||||
array['flags'] = querydisplayinfo_result['info']['entries'][1]['acct_flags']
|
||||
array['source'] = "SAMR Enumeration"
|
||||
|
||||
-- Clean up the 'flags' array
|
||||
for k = 1, #array['flags'], 1 do
|
||||
array['flags'][k] = msrpc.samr_AcctFlags_tostr(array['flags'][k])
|
||||
end
|
||||
|
||||
-- Add it to the array
|
||||
response[#response + 1] = array
|
||||
end
|
||||
j = j + 1
|
||||
until querydisplayinfo_result['return'] == 0
|
||||
|
||||
-- Close the domain handle
|
||||
msrpc.samr_close(smbstate, domain_handle)
|
||||
|
||||
-- Finally, fill in the response!
|
||||
-- for i = 1, #querydisplayinfo_result['details'], 1 do
|
||||
-- querydisplayinfo_result['details'][i]['domain'] = domain
|
||||
-- -- All we get from this is users
|
||||
-- querydisplayinfo_result['details'][i]['typestr'] = "User"
|
||||
-- querydisplayinfo_result['details'][i]['source'] = "SAMR Enumeration"
|
||||
-- response[#response + 1] = querydisplayinfo_result['details'][i]
|
||||
-- end
|
||||
end -- Checking for 'builtin'
|
||||
end -- Domain loop
|
||||
|
||||
-- Close the connect handle
|
||||
msrpc.samr_close(smbstate, connect_handle)
|
||||
|
||||
-- Stop the SAMR SMB
|
||||
msrpc.stop_smb(smbstate)
|
||||
|
||||
stdnse.print_debug(3, "Leaving enum_samr()")
|
||||
|
||||
return true, response
|
||||
end
|
||||
|
||||
---Attempt to enumerate users through LSA methods. See the file description for more information.
|
||||
--
|
||||
--@param host The host object.
|
||||
--@return Status (true or false).
|
||||
--@return Array of user tables (if status is true) or an an error string (if
|
||||
--status is false). Each user table contains the fields <code>name</code>,
|
||||
--<code>domain</code>, and <code>rid</code>.
|
||||
local function enum_lsa(host)
|
||||
|
||||
local smbstate
|
||||
local status
|
||||
local response = {}
|
||||
|
||||
stdnse.print_debug(3, "Entering enum_lsa()")
|
||||
|
||||
-- Create the SMB session
|
||||
status, smbstate = msrpc.start_smb(host, msrpc.LSA_PATH)
|
||||
if(status == false) then
|
||||
return false, smbstate
|
||||
end
|
||||
|
||||
-- Bind to LSA service
|
||||
status, bind_result = msrpc.bind(smbstate, msrpc.LSA_UUID, msrpc.LSA_VERSION, nil)
|
||||
if(status == false) then
|
||||
msrpc.stop_smb(smbstate)
|
||||
return false, bind_result
|
||||
end
|
||||
|
||||
-- Open the LSA policy
|
||||
status, openpolicy2_result = msrpc.lsa_openpolicy2(smbstate, host.ip)
|
||||
if(status == false) then
|
||||
msrpc.stop_smb(smbstate)
|
||||
return false, openpolicy2_result
|
||||
end
|
||||
|
||||
-- Start with some common names, as well as the name returned by the negotiate call
|
||||
-- Vista doesn't like a 'null' after the server name, so fix that (TODO: the way I strip the null here feels hackish, is there a better way?)
|
||||
names = {"administrator", "guest", "test", smbstate['domain'], string.sub(smbstate['server'], 1, #smbstate['server'] - 1) }
|
||||
|
||||
-- Get the server's name from nbstat
|
||||
local result, server_name = netbios.get_server_name(host.ip)
|
||||
if(result == true) then
|
||||
names[#names + 1] = server_name
|
||||
end
|
||||
|
||||
-- Get the logged in user from nbstat
|
||||
local result, user_name = netbios.get_user_name(host.ip)
|
||||
if(result == true) then
|
||||
names[#names + 1] = user_name
|
||||
end
|
||||
|
||||
-- Look up the names, if any are valid than the server's SID will be returned
|
||||
status, lookupnames2_result = msrpc.lsa_lookupnames2(smbstate, openpolicy2_result['policy_handle'], names)
|
||||
if(status == false) then
|
||||
msrpc.stop_smb(smbstate)
|
||||
return false, lookupnames2_result
|
||||
end
|
||||
-- Loop through the domains returned and find the users in each
|
||||
for i = 1, #lookupnames2_result['domains']['domains'], 1 do
|
||||
local domain = lookupnames2_result['domains']['domains'][i]['name']
|
||||
local sid = lookupnames2_result['domains']['domains'][i]['sid']
|
||||
local sids = { }
|
||||
local start = 1000
|
||||
|
||||
-- Start by looking up 500 - 505 (will likely be Administrator + guest)
|
||||
for j = 500, 505, 1 do
|
||||
sids[#sids + 1] = sid .. "-" .. j
|
||||
end
|
||||
status, lookupsids2_result = msrpc.lsa_lookupsids2(smbstate, openpolicy2_result['policy_handle'], sids)
|
||||
if(status == false) then
|
||||
stdnse.print_debug(1, string.format("Error looking up RIDs: %s", lookupsids2_result))
|
||||
else
|
||||
-- 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
|
||||
local result = {}
|
||||
result['name'] = lookupsids2_result['names']['names'][j]['name']
|
||||
result['rid'] = 500 + j - 1
|
||||
result['domain'] = domain
|
||||
result['typestr'] = msrpc.lsa_SidType_tostr(lookupsids2_result['names']['names'][j]['sid_type'])
|
||||
result['source'] = "LSA Bruteforce"
|
||||
response[#response + 1] = result
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Now do groups of 5 users, until we get past 1100 and have an empty group
|
||||
repeat
|
||||
local used_names = 0
|
||||
local sids = {}
|
||||
for j = start, start + 4, 1 do
|
||||
sids[#sids + 1] = sid .. "-" .. j
|
||||
end
|
||||
|
||||
-- Try converting this group of RIDs into names
|
||||
status, lookupsids2_result = msrpc.lsa_lookupsids2(smbstate, openpolicy2_result['policy_handle'], sids)
|
||||
if(status == false) then
|
||||
stdnse.print_debug(1, string.format("Error looking up RIDs: %s", lookupsids2_result))
|
||||
else
|
||||
-- Put the details for each name into an array
|
||||
for j = 1, #lookupsids2_result['names']['names'], 1 do
|
||||
if(lookupsids2_result['names']['names'][j]['sid_type'] ~= "SID_NAME_UNKNOWN") then
|
||||
local result = {}
|
||||
result['name'] = lookupsids2_result['names']['names'][j]['name']
|
||||
result['rid'] = start + j - 1
|
||||
result['domain'] = domain
|
||||
result['typestr'] = msrpc.lsa_SidType_tostr(lookupsids2_result['names']['names'][j]['sid_type'])
|
||||
result['source'] = "LSA Bruteforce"
|
||||
response[#response + 1] = result
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Go to the next set of RIDs
|
||||
start = start + 5
|
||||
until status == false or (used_names == 0 and start > 1100)
|
||||
end
|
||||
|
||||
-- Close the handle
|
||||
msrpc.lsa_close(smbstate, openpolicy2_result['policy_handle'])
|
||||
|
||||
msrpc.stop_smb(smbstate)
|
||||
|
||||
stdnse.print_debug(3, "Leaving enum_lsa()")
|
||||
|
||||
return true, response
|
||||
end
|
||||
|
||||
|
||||
|
||||
action = function(host)
|
||||
|
||||
local i, j
|
||||
local samr_status = false
|
||||
local lsa_status = false
|
||||
|
|
@ -450,7 +169,7 @@ action = function(host)
|
|||
-- Try enumerating through LSA first. Since LSA provides less information, we want the
|
||||
-- SAMR result to overwrite it.
|
||||
if(do_lsa) then
|
||||
lsa_status, lsa_result = enum_lsa(host)
|
||||
lsa_status, lsa_result = msrpc.lsa_enum_users(host)
|
||||
if(lsa_status == false) then
|
||||
if(nmap.debugging() > 0) then
|
||||
response = response .. "ERROR: Couldn't enumerate through LSA: " .. lsa_result .. "\n"
|
||||
|
|
@ -468,7 +187,7 @@ action = function(host)
|
|||
|
||||
-- Try enumerating through SAMR
|
||||
if(do_samr) then
|
||||
samr_status, samr_result = enum_samr(host)
|
||||
samr_status, samr_result = msrpc.samr_enum_users(host)
|
||||
if(samr_status == false) then
|
||||
if(nmap.debugging() > 0) then
|
||||
response = response .. "ERROR: Couldn't enumerate through SAMR: " .. samr_result .. "\n"
|
||||
|
|
@ -523,6 +242,7 @@ action = function(host)
|
|||
end
|
||||
if(names[name]['fullname'] ~= nil) then response = response .. string.format(" |_ Full name: %s\n", names[name]['fullname']) end
|
||||
if(names[name]['description'] ~= nil) then response = response .. string.format(" |_ Description: %s\n", names[name]['description']) end
|
||||
|
||||
if(names[name]['flags'] ~= nil) then response = response .. string.format(" |_ Flags: %s\n", stdnse.strjoin(", ", names[name]['flags'])) end
|
||||
|
||||
if(nmap.verbosity() > 1) then
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue