mirror of
https://github.com/nmap/nmap.git
synced 2025-12-07 13:11:28 +00:00
make them look better in output. The full list of changes is anonFTP => ftp-anon ASN => asn-query brutePOP3 => pop3-brute bruteTelnet => telnet-brute daytimeTest => daytime dns-safe-recursion-port => dns-random-srcport dns-safe-recursion-txid => dns-random-txid dns-test-open-recursion => dns-recursion ftpbounce => ftp-bounce HTTPAuth => http-auth HTTP_open_proxy => http-open-proxy HTTPpasswd => http-passwd HTTPtrace => http-trace iax2Detect => iax2-version ircServerInfo => irc-info ircZombieTest => irc-zombie MSSQLm => ms-sql-info MySQLinfo => mysql-info popcapa => pop3-capabilities PPTPversion => pptp-version promiscuous => sniffer-detect RealVNC_auth_bypass => realvnc-auth-bypass robots => robots.txt showHTMLTitle => html-title showOwner => identd-owners skype_v2-version => skypev2-version smb-enumdomains => smb-enum-domains smb-enumsessions => smb-enum-sessions smb-enumshares => smb-enum-shares smb-enumusers => smb-enum-users smb-serverstats => smb-server-stats smb-systeminfo => smb-system-info SMTPcommands => smtp-commands SMTP_openrelay_test => smtp-open-relay SNMPcommunitybrute => snmp-brute SNMPsysdescr => snmp-sysdescr SQLInject => sql-injection SSH-hostkey => ssh-hostkey SSHv1-support => sshv1 SSLv2-support => sslv2 strangeSMTPport => smtp-strangeport UPnP-info => upnp-info xamppDefaultPass => xampp-default-auth zoneTrans => zone-transfer
326 lines
11 KiB
Lua
326 lines
11 KiB
Lua
description = [[
|
|
Enumerates the users logged into a system either locally, through a remote desktop client (terminal
|
|
services), or through a SMB share.
|
|
|
|
Enumerating the local and terminal services users is done by reading the remote registry. Keys under
|
|
<code>HKEY_USERS</code> are SIDs that represent the currently logged in users, and those SIDs can be converted
|
|
to proper names by using the <code>LsaLookupSids()</code> function. Doing this requires any access higher than
|
|
anonymous. Guests, users, or administrators are all able to perform this request on the operating
|
|
systems I (Ron Bowes) tested.
|
|
|
|
Enumerating SMB connections is done using the <code>srvsvc.netsessenum()</code> function, which returns who's
|
|
logged in, when they logged in, and how long they've been idle for. Unfortunately, I couldn't find
|
|
a way to get the user's domain with this function, so the domain isn't printed. The level of access
|
|
required for this varies between Windows versions, but in Windows 2000 anybody (including the
|
|
anonymous account) can access this, and in Windows 2003 a user or administrator account is
|
|
required.
|
|
|
|
Since both of these are related to users being logged into the server, it seemed logical to combine
|
|
them into a single script.
|
|
|
|
I learned the idea and technique for this from sysinternals' tool, PsLoggedOn.exe. I use similar
|
|
function calls to what they use, so thanks go out to them. Thanks also to Matt, for giving me the
|
|
idea to write this one.
|
|
]]
|
|
|
|
---
|
|
--@usage
|
|
-- nmap --script smb-enumsessions.nse -p445 <host>
|
|
-- sudo nmap -sU -sS --script smb-enumsessions.nse -p U:137,T:139 <host>
|
|
--
|
|
--@output
|
|
-- Host script results:
|
|
-- | smb-enum-sessions:
|
|
-- | Users logged in:
|
|
-- | |_ TESTBOX\Administrator since 2008-10-21 08:17:14
|
|
-- | |_ DOMAIN\rbowes since 2008-10-20 09:03:23
|
|
-- | Active SMB Sessions:
|
|
-- |_ |_ ADMINISTRATOR is connected from 10.100.254.138 for [just logged in, it's probably you], idle for [not idle]
|
|
--
|
|
-- @args smb* This script supports the <code>smbusername</code>,
|
|
-- <code>smbpassword</code>, <code>smbhash</code>, <code>smbguest</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 'msrpc'
|
|
require 'smb'
|
|
require 'stdnse'
|
|
|
|
hostrule = function(host)
|
|
|
|
local port = smb.get_port(host)
|
|
|
|
if(port == nil) then
|
|
return false
|
|
else
|
|
return true
|
|
end
|
|
|
|
end
|
|
|
|
---Attempts to enumerate the sessions on a remote system using MSRPC calls. This will likely fail
|
|
-- against a modern system, but will succeed against Windows 2000.
|
|
--
|
|
--@param host The host object.
|
|
--@return Status (true or false).
|
|
--@return List of sessions (if status is true) or an an error string (if status is false).
|
|
local function srvsvc_enum_sessions(host)
|
|
local i
|
|
local status, smbstate
|
|
local bind_result, netsessenum_result
|
|
|
|
-- Create the SMB session
|
|
status, smbstate = msrpc.start_smb(host, msrpc.SRVSVC_PATH)
|
|
if(status == false) then
|
|
return false, smbstate
|
|
end
|
|
|
|
-- Bind to SRVSVC service
|
|
status, bind_result = msrpc.bind(smbstate, msrpc.SRVSVC_UUID, msrpc.SRVSVC_VERSION, nil)
|
|
if(status == false) then
|
|
msrpc.stop_smb(smbstate)
|
|
return false, bind_result
|
|
end
|
|
|
|
-- Call netsessenum
|
|
status, netsessenum_result = msrpc.srvsvc_netsessenum(smbstate, host.ip)
|
|
if(status == false) then
|
|
msrpc.stop_smb(smbstate)
|
|
return false, netsessenum_result
|
|
end
|
|
|
|
-- Stop the SMB session
|
|
msrpc.stop_smb(smbstate)
|
|
|
|
return true, netsessenum_result['sessions']
|
|
end
|
|
|
|
---Enumerates the users logged in locally (or through terminal services) by using functions
|
|
-- that access the registry. To perform this check, guest access or higher is required.
|
|
--
|
|
--@param host The host object.
|
|
--@return An array of user tables, each with the keys <code>name</code>, <code>domain</code>, and <code>changed_date</code> (representing
|
|
-- when they logged in).
|
|
local function winreg_enum_rids(host)
|
|
local i, j
|
|
local elements = {}
|
|
|
|
-- Create the SMB session
|
|
status, smbstate = msrpc.start_smb(host, msrpc.WINREG_PATH)
|
|
if(status == false) then
|
|
return false, smbstate
|
|
end
|
|
|
|
-- Bind to WINREG service
|
|
status, bind_result = msrpc.bind(smbstate, msrpc.WINREG_UUID, msrpc.WINREG_VERSION, nil)
|
|
if(status == false) then
|
|
msrpc.stop_smb(smbstate)
|
|
return false, bind_result
|
|
end
|
|
|
|
status, openhku_result = msrpc.winreg_openhku(smbstate)
|
|
if(status == false) then
|
|
msrpc.stop_smb(smbstate)
|
|
return false, openhku_result
|
|
end
|
|
|
|
-- Loop through the keys under HKEY_USERS and grab the names
|
|
i = 0
|
|
repeat
|
|
status, enumkey_result = msrpc.winreg_enumkey(smbstate, openhku_result['handle'], i)
|
|
|
|
if(status == true) then
|
|
local status, openkey_result
|
|
|
|
local element = {}
|
|
element['name'] = enumkey_result['name']
|
|
element['sid'] = msrpc.string_to_sid(enumkey_result['name'])
|
|
|
|
-- To get the time the user logged in, we check the 'Volatile Environment' key
|
|
-- This can fail with the 'guest' account due to access restrictions
|
|
status, openkey_result = msrpc.winreg_openkey(smbstate, openhku_result['handle'], element['name'] .. "\\Volatile Environment")
|
|
if(status ~= false) then
|
|
local queryinfokey_result, closekey_result
|
|
|
|
-- Query the info about this key. The response will tell us when the user logged into the server.
|
|
status, queryinfokey_result = msrpc.winreg_queryinfokey(smbstate, openkey_result['handle'])
|
|
if(status == false) then
|
|
msrpc.stop_smb(smbstate)
|
|
return false, queryinfokey_result
|
|
end
|
|
|
|
status, closekey_result = msrpc.winreg_closekey(smbstate, openkey_result['handle'])
|
|
if(status == false) then
|
|
msrpc.stop_smb(smbstate)
|
|
return false, closekey_result
|
|
end
|
|
|
|
element['changed_date'] = queryinfokey_result['last_changed_date']
|
|
else
|
|
-- Getting extra details failed, but we can still handle this
|
|
element['changed_date'] = "<unknown>"
|
|
end
|
|
elements[#elements + 1] = element
|
|
end
|
|
|
|
i = i + 1
|
|
until status ~= true
|
|
|
|
status, closekey_result = msrpc.winreg_closekey(smbstate, openhku_result['handle'])
|
|
if(status == false) then
|
|
msrpc.stop_smb(smbstate)
|
|
return false, closekey_result
|
|
end
|
|
|
|
msrpc.stop_smb(smbstate)
|
|
|
|
-- Start a new 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
|
|
|
|
-- Get a policy handle
|
|
status, openpolicy2_result = msrpc.lsa_openpolicy2(smbstate, host.ip)
|
|
if(status == false) then
|
|
msrpc.stop_smb(smbstate)
|
|
return false, openpolicy2_result
|
|
end
|
|
|
|
-- Convert the RIDs to names
|
|
local results = {}
|
|
stdnse.print_debug(3, "MSRPC: Found %d SIDs that might be logged in", #elements)
|
|
for i = 1, #elements, 1 do
|
|
if(elements[i]['sid'] ~= nil) then
|
|
-- The RID is the last subauthority
|
|
local rid = elements[i]['sid']['subauthorities'][elements[i]['sid']['count']]
|
|
stdnse.print_debug(3, "MSRPC: Found an actual RID: %d", rid)
|
|
|
|
-- The server is the rest of the SID, so remove the last subauthority
|
|
elements[i]['sid']['subauthorities'][elements[i]['sid']['count']] = nil
|
|
elements[i]['sid']['count'] = elements[i]['sid']['count'] - 1
|
|
|
|
-- Look up the RID
|
|
stdnse.print_debug(3, "MSRPC: Looking up RID %s in SID %s", rid, msrpc.sid_to_string(elements[i]['sid']))
|
|
status, lookupsids2_result = msrpc.lsa_lookupsids2(smbstate, openpolicy2_result['policy_handle'], elements[i]['sid'], {rid})
|
|
if(status == false) then
|
|
-- It may not succeed, if it doesn't that's ok
|
|
stdnse.print_debug(3, "MSRPC: Lookup failed")
|
|
else
|
|
-- Create the result array
|
|
local result = {}
|
|
result['rid'] = rid
|
|
result['changed_date'] = elements[i]['changed_date']
|
|
|
|
-- Fill in the result from the response
|
|
if(lookupsids2_result['details'][1] == nil) then
|
|
result['name'] = "<unknown>"
|
|
result['domain'] = ""
|
|
else
|
|
result['name'] = lookupsids2_result['details'][1]['name']
|
|
result['type'] = lookupsids2_result['details'][1]['type']
|
|
result['domain'] = lookupsids2_result['domains'][1]['name']
|
|
end
|
|
|
|
if(result['type'] ~= 5) then -- Don't show "well known" accounts
|
|
-- Add it to the results
|
|
results[#results + 1] = result
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Close the policy
|
|
msrpc.lsa_close(smbstate, openpolicy2_result['policy_handle'])
|
|
|
|
-- Stop the session
|
|
msrpc.stop_smb(smbstate)
|
|
|
|
return true, results
|
|
end
|
|
|
|
action = function(host)
|
|
local response = " \n"
|
|
|
|
local status1, status2
|
|
|
|
-- Enumerate the logged in users
|
|
status1, users = winreg_enum_rids(host)
|
|
if(status1 == false) then
|
|
response = response .. "ERROR: Couldn't enumerate login sessions: " .. users .. "\n"
|
|
else
|
|
response = response .. "Users logged in:\n"
|
|
if(#users == 0) then
|
|
response = response .. "|_ <nobody>\n"
|
|
else
|
|
for i = 1, #users, 1 do
|
|
response = response .. string.format("|_ %s\\%s since %s\n", users[i]['domain'], users[i]['name'], users[i]['changed_date'])
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Get the connected sessions
|
|
status2, sessions = srvsvc_enum_sessions(host)
|
|
if(status2 == false) then
|
|
response = response .. "ERROR: Couldn't enumerate network sessions: " .. sessions .. "\n"
|
|
else
|
|
response = response .. "Active SMB Sessions:\n"
|
|
if(#sessions == 0) then
|
|
response = response .. "|_ <none>\n"
|
|
else
|
|
-- Format the result
|
|
for i = 1, #sessions, 1 do
|
|
|
|
local active = sessions[i]['active']
|
|
if(active == 0) then
|
|
active = "[just logged in, it's probably you]"
|
|
elseif(active > 60 * 60 * 24) then
|
|
active = string.format("%dd%dh%02dm%02ds", active / (60*60*24), (active % (60*60*24)) / 3600, (active % 3600) / 60, active % 60)
|
|
elseif(active > 60 * 60) then
|
|
active = string.format("%dh%02dm%02ds", active / 3600, (active % 3600) / 60, active % 60)
|
|
else
|
|
active = string.format("%02dm%02ds", active / 60, active % 60)
|
|
end
|
|
|
|
local idle = sessions[i]['idle']
|
|
if(idle == 0) then
|
|
idle = "[not idle]"
|
|
elseif(idle > 60 * 60 * 24) then
|
|
idle = string.format("%dd%dh%02dm%02ds", idle / (60*60*24), (idle % (60*60*24)) / 3600, (idle % 3600) / 60, idle % 60)
|
|
elseif(idle > 60 * 60) then
|
|
idle = string.format("%dh%02dm%02ds", idle / 3600, (idle % 3600) / 60, idle % 60)
|
|
else
|
|
idle = string.format("%02dm%02ds", idle / 60, idle % 60)
|
|
end
|
|
|
|
response = response .. string.format("|_ %s is connected from %s for %s, idle for %s\n", sessions[i]['user'], sessions[i]['client'], active, idle)
|
|
end
|
|
end
|
|
end
|
|
|
|
if(status1 == false and status2 == false) then
|
|
if(nmap.debugging() > 0) then
|
|
return response
|
|
else
|
|
return nil
|
|
end
|
|
else
|
|
return response
|
|
end
|
|
end
|
|
|
|
|
|
|