Nmap Development mailing list archives

Re: [NSE] SMB authentication patch


From: Ron <ron () skullsecurity net>
Date: Mon, 13 Oct 2008 22:30:14 -0500

Hi David,

David Fifield wrote:
On Fri, Oct 10, 2008 at 07:14:47PM -0500, Ron wrote:
I'll send you a separate message with a pcap log and the user2sid
results.
Good find on that one. It turns out I was handling aliases incorrectly,
so all the users ended up numbered incorrectly, which caused them to
over-write each other. I was able to replicate it on my system, and it's
now fixed.

I tried that and got

Host script results:
|_ MSRPC: NetSessEnum(): ERROR: Read off the end of the packet
I managed to replicate/fix that, as well, but it stems from a deeper
problem, which I'm not so sure about.

Looking at the logs, it seems that you're automatically being logged in
as "GUEST". Normally that happens if your username wasn't found, which
is strange. Is there anything weird about your user accounts, or are you
part of a domain, or something like that?

Can you try adding a smbdomain argument, and try setting it to "MSHOME"
or "MAC-MINI"? (those are the two domains I've seen in your packet
caps), and running smb-enumusers.nse? If you're logged in properly, it
should display more information about the accounts (descriptions, full
names, etc). Maybe try disabling the 'GUEST' account on your system, see
if that makes a difference. If not, we need to figure out why it's
forcing you to GUEST instead of a full user -- can you try mounting a
share on that machine remotely, and getting a packet cap?

On the plus side, this unintentionally made me realize that I wasn't
testing GUEST access, so I've added a GUEST account to my testing. The
other funny thing is that the information being returned is 100% stuff
that could be recovered without a user account. Scary, eh?

I'll send you a pcap log of that too.
Thanks, it was helpful

David Fifield

I've attached an updated patch as well as the new module (which gives
far more information to you, even as a GUEST, now). I'm also version
controlling my stuff in my own svn repository -- would that be easier
for your testing than using attachments?

Ron
---Currently enumerates the active sessions on a system, where a session is defined as a connection to
-- a share. It does this using the srvsvc.netsessenum() function. \n
--\n
--The future direction for this will be to enumerate users logged into the server as well (the same way
--the psloggedin.exe tool does), but that's the future. 
--
--@usage
-- nmap --script smb-enumsessions.nse -p445 <host>\n
-- sudo nmap -sU -sS --script smb-enumsessions.nse -p U:137,T:139 <host>\n
--
--@output
-- Host script results:
-- |  MSRPC: NetSessEnum():  
-- |  User ADMINISTRATOR logged on from 192.168.1.44 for 00m18s, idle for 00m18s
-- |  User RON logged on from 192.168.1.42 for 00m05s, idle for 00m02s
-- |_ User ADMINISTRATOR logged on from 192.168.1.3 for [just logged in, it's probably you], idle for [not idle]
--
--@args  smbusername The SMB username to log in with. The form DOMAIN\username and username@DOMAIN
--                   are NOT understood. To set a domain, use the smbdomain argument. 
--@args  smbdomain   The domain to log in with. If you aren't in a domained environment, then anything
--                   will (should?) be accepted by the server. 
--@args  smbpassword The password to connect with. Be cautious with this, since some servers will lock
--                   accounts if the incorrect password is given (although it's rare for the 
--                   'administrator' account to be lockoutable, in the off chance that it is, you could
--                   get yourself in trouble). 
--@args  smbhash     A password hash to use when logging in. This is given as a single hex string (32
--                   characters) or a pair of hex strings (2 x 32 characters, optionally separated by a 
--                   single character). These hashes are the Lanman or NTLM hash of the user's password,
--                   and are stored by systems, on the harddrive or memory. They can be retrived from memory
--                   using the fgdump or pwdump tools. 
--@args  smbguest    If this is set to 'true' or '1', a 'guest' login will be attempted if the normal one 
--                   fails. This should be harmless, but I thought I would disable it by default anyway
--                   because I'm not entirely sure of any possible consequences. 
--@args  smbtype     The type of SMB authentication to use. By default, NTLMv1 is used, which is a pretty
--                   decent compromise between security and compatibility. If you are paranoid, you might 
--                   want to use 'v2' or 'lmv2' for this (actually, if you're paranoid, you should be 
--                   avoiding this protocol altogether :P). If you're using an extremely old system, you 
--                   might need to set this to 'v1' or 'lm', which are less secure but more compatible. \n
--\n
--                   If you want finer grained control, these are the possible options:\n
--                   <ul>
--                       <li>v1 -- Sends LMv1 and NTLMv1</li>
--                       <li>LMv1 -- Sends LMv1 only</li>
--                       <li>NTLMv1 -- Sends NTLMv1 only (default)</li>
--                       <li>v2 -- Sends LMv2 and NTLMv2</li>
--                       <li>LMv2 -- Sends LMv2 only</li>
--                   </ul>
--
-----------------------------------------------------------------------

id = "MSRPC: NetSessEnum()"
description = "Tries calling the NetSessEnum() RPC function to get a list of active sessions"
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 shares 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, result) If status is false, result is an error string. Otherwise, result is 
--        a list of all shares on a system. 
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

-- TODO: Comment
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 element = {}
                        element['name'] = enumkey_result['name']
                        element['sid'] = msrpc.string_to_sid(enumkey_result['name'])
                        element['changed_date'] = enumkey_result['changed_date']

                        elements[#elements + 1] = element
                end

                i = i + 1
        until status ~= true
--TODO: Close the key
        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']
                                -- TODO: How can I find the domain here?
        
                                -- 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 status, sessions
        local response = " \n"

        -- Enumerate the logged in users
        status, users = winreg_enum_rids(host)
        if(status == 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, logged in since %s [testing -- may not 
be accurate]\n", users[i]['domain'], users[i]['name'], users[i]['changed_date'])
                        end
                end
        end

        -- Get the connected sessions
        status, sessions = srvsvc_enum_sessions(host)
        if(status == 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

        return response
end







Attachment: smb_updates-20081013.diff.gz
Description:


_______________________________________________
Sent through the nmap-dev mailing list
http://cgi.insecure.org/mailman/listinfo/nmap-dev
Archived at http://SecLists.Org

Current thread: