home *** CD-ROM | disk | FTP | other *** search
/ Hackers Magazine 57 / CdHackersMagazineNr57.iso / Software / Networking / nmap-5.00-setup.exe / scripts / smb-enum-users.nse < prev    next >
Text File  |  2009-07-06  |  12KB  |  277 lines

  1. description = [[
  2. Attempts to enumerate the users on a remote Windows system, with as much
  3. information as possible, through two different techniques (both over MSRPC,
  4. which uses port 445 or 139; see <code>smb.lua</code>). The goal of this script
  5. is to discover all user accounts that exist on a remote system. This can be
  6. helpful for administration, by seeing who has an account on a server, or for 
  7. penetration testing or network footprinting, by determining which accounts 
  8. exist on a system. 
  9.  
  10. A penetration tester who is examining servers may wish to determine the
  11. purpose of a server. By getting a list of who has access to it, the tester
  12. might get a better idea (if financial people have accounts, it probably 
  13. relates to financial information). Additionally, knowing which accounts
  14. exist on a system (or on multiple systems) allows the pen-tester to build a
  15. dictionary of possible usernames for bruteforces, such as a SMB bruteforce
  16. or a Telnet bruteforce. These accounts may be helpful for other purposes, 
  17. such as using the accounts in Web applications on this or other servers. 
  18.  
  19. From a pen-testers perspective, retrieving the list of users on any 
  20. given server creates endless possibilities. 
  21.  
  22. Users are enumerated in two different ways:  using SAMR enumeration or 
  23. LSA bruteforcing. By default, both are used, but they have specific
  24. advantages and disadvantages. Using both is a great default, but in certain
  25. circumstances it may be best to give preference to one. 
  26.  
  27. Advantages of using SAMR enumeration:
  28. * 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). 
  29. * More information is returned (more than just the username).
  30. * Every account will be found, since they're being enumerated with a function that's designed to enumerate users.
  31.  
  32. Advantages of using LSA bruteforcing:
  33. * More accounts are returned (system accounts, groups, and aliases are returned, not just users).
  34. * Requires a lower-level account to run on Windows XP and higher (a 'guest' account can be used, whereas SAMR enumeration requires a 'user' account; especially useful when only guest access is allowed, or when an account has a blank password (which effectively gives it guest access)). 
  35.  
  36. SAMR enumeration is done with the  <code>QueryDisplayInfo</code> function. 
  37. If this succeeds, it will return a detailed list of users, along with descriptions,
  38. types, and full names. This can be done anonymously against Windows 2000, and 
  39. with a user-level account on other Windows versions (but not with a guest-level account). 
  40.  
  41. To perform this test, the following functions are used:
  42. * <code>Bind</code>: bind to the SAMR service.
  43. * <code>Connect4</code>: get a connect_handle.
  44. * <code>EnumDomains</code>: get a list of the domains.
  45. * <code>QueryDomain</code>: get the sid for the domain.
  46. * <code>OpenDomain</code>: get a handle for each domain.
  47. * <code>QueryDisplayInfo</code>: get the list of users in the domain.
  48. * <code>Close</code>: Close the domain handle.
  49. * <code>Close</code>: Close the connect handle.
  50. The advantage of this technique is that a lot of details are returned, including
  51. the full name and description; the disadvantage is that it requires a user-level
  52. account on every system except for Windows 2000. Additionally, it only pulls actual
  53. user accounts, not groups or aliases. 
  54.  
  55. Regardless of whether this succeeds, a second technique is used to pull
  56. user accounts, called LSA bruteforcing. LSA bruteforcing can be done anonymously
  57. against Windows 2000, and requires a guest account or better on other systems. 
  58. It has the advantage of running with less permission, and will also find more 
  59. account types (i.e., groups, aliases, etc.). The disadvantages is that it returns 
  60. less information, and that, because it's a brute-force guess, it's possible to miss
  61. accounts. It's also extremely noisy. 
  62.  
  63. This isn't a brute-force technique in the common sense, however: it's a brute-forcing of users' 
  64. RIDs. A user's RID is a value (generally 500, 501, or 1000+) that uniquely identifies
  65. a user on a domain or system. An LSA function is exposed which lets us convert the RID
  66. (say, 1000) to the username (say, "Ron"). So, the technique will essentially try
  67. converting 1000 to a name, then 1001, 1002, etc., until we think we're done. 
  68.  
  69. To do this, the script breaks users into groups of RIDs based on the <code>LSA_GROUPSIZE</code>
  70. constant. All members of this group are checked simultaneously, and the responses recorded. 
  71. When a series of empty groups are found (<code>LSA_MINEMPTY</code> groups, specifically), 
  72. the scan ends. As long as you are getting a few groups with active accounts, the scan will
  73. continue. 
  74.  
  75. Before attempting this conversion, the SID of the server has to be determined. 
  76. The SID is determined by doing the reverse operation; that is, by converting a name into 
  77. its RID. The name is determined by looking up any name present on the system. 
  78. We try:
  79. * The computer name and domain name, returned in <code>SMB_COM_NEGOTIATE</code>;
  80. * An nbstat query to get the server name and the user currently logged in; and
  81. * Some common names: "administrator", "guest", and "test".
  82.  
  83. In theory, the computer name should be sufficient for this to always work, and
  84. it has so far has in my tests, but I included the rest of the names for good measure. It 
  85. doesn't hurt to add more. 
  86.  
  87. The names and details from both of these techniques are merged and displayed.
  88. If the output is verbose, then extra details are shown. The output is ordered alphabetically. 
  89.  
  90. Credit goes out to the enum.exe, sid2user.exe, and user2sid.exe programs, 
  91. the code I wrote for this is largely based on the techniques used by them.
  92. ]]
  93.  
  94. ---
  95. -- @usage
  96. -- nmap --script smb-enum-users.nse -p445 <host>
  97. -- sudo nmap -sU -sS --script smb-enum-users.nse -p U:137,T:139 <host>
  98. --
  99. -- @output
  100. -- Host script results:
  101. -- |  smb-enum-users:
  102. -- |_ TESTBOX\Administrator, EXTERNAL\DnsAdmins, TESTBOX\Guest, 
  103. --    EXTERNAL\HelpServicesGroup, EXTERNAL\PARTNERS$, TESTBOX\SUPPORT_388945a0
  104. -- 
  105. -- Host script results:
  106. -- |  smb-enum-users:
  107. -- |  Administrator
  108. -- |    |_ Type: User
  109. -- |    |_ Domain: LOCALSYSTEM
  110. -- |    |_ Full name: Built-in account for administering the computer/domain
  111. -- |    |_ Flags: Normal account, Password doesn't expire
  112. -- |  DnsAdmins
  113. -- |    |_ Type: Alias
  114. -- |    |_ Domain: EXTRANET
  115. -- |  EventViewer
  116. -- |    |_ Type: User
  117. -- |    |_ Domain: SHARED
  118. -- |  ProxyUsers
  119. -- |    |_ Type: Group
  120. -- |    |_ Domain: EXTRANET
  121. -- |  ComputerAccounts
  122. -- |    |_ Type: Group
  123. -- |    |_ Domain: EXTRANET
  124. -- |  Helpdesk
  125. -- |    |_ Type: Group
  126. -- |    |_ Domain: EXTRANET
  127. -- |  Guest
  128. -- |    |_ Type: User
  129. -- |    |_ Domain: LOCALSYSTEM
  130. -- |    |_ Full name: Built-in account for guest access to the computer/domain
  131. -- |    |_ Flags: Normal account, Disabled, Password not required, Password doesn't expire
  132. -- |  Staff
  133. -- |    |_ Type: Alias
  134. -- |    |_ Domain: LOCALSYSTEM
  135. -- |  Students
  136. -- |    |_ Type: Alias
  137. -- |_   |_ Domain: LOCALSYSTEM
  138. -- 
  139. -- @args lsaonly If set, script will only enumerate using an LSA bruteforce (requires less
  140. --       access than samr). Only set if you know what you're doing, you'll get better results
  141. --       by using the default options. 
  142. -- @args samronly If set, script will only query a list of users using a SAMR lookup. This is 
  143. --       much quieter than LSA lookups, so enable this if you want stealth. Generally, however,
  144. --       you'll get better results by using the default options. 
  145. -----------------------------------------------------------------------
  146.  
  147. author = "Ron Bowes"
  148. copyright = "Ron Bowes"
  149. license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
  150. categories = {"discovery","intrusive"}
  151.  
  152. require 'msrpc'
  153. require 'smb'
  154. require 'stdnse'
  155.  
  156. hostrule = function(host)
  157.     return smb.get_port(host) ~= nil
  158. end
  159.  
  160. action = function(host)
  161.  
  162.     local i, j
  163.     local samr_status = false
  164.     local lsa_status  = false
  165.     local samr_result = "Didn't run"
  166.     local lsa_result  = "Didn't run"
  167.     local names = {}
  168.     local name_strings = {}
  169.     local response = " \n"
  170.     local samronly = nmap.registry.args.samronly
  171.     local lsaonly  = nmap.registry.args.lsaonly
  172.     local do_samr  = samronly ~= nil or (samronly == nil and lsaonly == nil)
  173.     local do_lsa   = lsaonly  ~= nil or (samronly == nil and lsaonly == nil)
  174.  
  175.     -- Try enumerating through LSA first. Since LSA provides less information, we want the
  176.     -- SAMR result to overwrite it. 
  177.     if(do_lsa) then
  178.         lsa_status, lsa_result  = msrpc.lsa_enum_users(host)
  179.         if(lsa_status == false) then
  180.             if(nmap.debugging() > 0) then
  181.                 response = response .. "ERROR: Couldn't enumerate through LSA: " .. lsa_result .. "\n"
  182.             end
  183.         else
  184.             -- Copy the returned array into the names[] table, using the name as the key
  185.             stdnse.print_debug(2, "EnumUsers: Received %d names from LSA", #lsa_result)
  186.             for i = 1, #lsa_result, 1 do
  187.                 if(lsa_result[i]['name'] ~= nil) then
  188.                     names[string.upper(lsa_result[i]['name'])] = lsa_result[i]
  189.                 end
  190.             end
  191.         end
  192.     end
  193.  
  194.     -- Try enumerating through SAMR
  195.     if(do_samr) then
  196.         samr_status, samr_result = msrpc.samr_enum_users(host)
  197.         if(samr_status == false) then
  198.             if(nmap.debugging() > 0) then
  199.                 response = response .. "ERROR: Couldn't enumerate through SAMR: " .. samr_result .. "\n"
  200.             end
  201.         else
  202.             -- Copy the returned array into the names[] table, using the name as the key
  203.             stdnse.print_debug(2, "EnumUsers: Received %d names from SAMR", #samr_result)
  204.             for i = 1, #samr_result, 1 do
  205.                 names[string.upper(samr_result[i]['name'])] = samr_result[i]
  206.             end
  207.         end
  208.     end
  209.  
  210.     -- Check if both failed
  211.     if(samr_status == false and lsa_status == false) then
  212.         if(nmap.debugging() > 0) then
  213.             return response
  214.         else
  215.             return nil
  216.         end
  217.     end
  218.  
  219.     -- Put the names into an array of strings, so we can sort them
  220.     for name, details in pairs(names) do
  221.         name_strings[#name_strings + 1] = names[name]['name']
  222.     end
  223.     -- Sort them
  224.     table.sort(name_strings, function (a, b) return string.lower(a) < string.lower(b) end)
  225.  
  226.     -- Check if we actually got any names back
  227.     if(#name_strings == 0) then
  228.         response = response .. "Couldn't find any account names anonymously, sorry!"
  229.     else
  230.         -- If we're not verbose, just print out the names. Otherwise, print out everything we can
  231.         if(nmap.verbosity() < 1) then
  232.             local response_array = {}
  233.             for i = 1, #name_strings, 1 do
  234.                 local name = string.upper(name_strings[i])
  235.                 response_array[#response_array + 1] = (names[name]['domain'] .. "\\" .. names[name]['name'])
  236.             end
  237.                 
  238.             response = response .. stdnse.strjoin(", ", response_array)
  239.         else
  240.             for i = 1, #name_strings, 1 do
  241.                 local name = string.upper(name_strings[i])
  242.                 response = response .. string.format("%s\n", names[name]['name'])
  243.  
  244.                 if(names[name]['typestr'] ~= nil)     then response = response .. string.format("  |_ Type: %s\n",        names[name]['typestr'])     end
  245.                 if(names[name]['domain'] ~= nil)      then response = response .. string.format("  |_ Domain: %s\n",      names[name]['domain'])      end
  246.                 if(nmap.verbosity() > 1) then
  247.                     if(names[name]['rid'] ~= nil)         then response = response .. string.format("  |_ RID: %s\n",         names[name]['rid'])         end
  248.                 end
  249.                 if(names[name]['fullname'] ~= nil)    then response = response .. string.format("  |_ Full name: %s\n",   names[name]['fullname'])    end
  250.                 if(names[name]['description'] ~= nil) then response = response .. string.format("  |_ Description: %s\n", names[name]['description']) end
  251.  
  252.                 if(names[name]['flags'] ~= nil)       then response = response .. string.format("  |_ Flags: %s\n",       stdnse.strjoin(", ", names[name]['flags'])) end
  253.  
  254.                 if(nmap.verbosity() > 1) then
  255.                     if(names[name]['source'] ~= nil)      then response = response .. string.format("  |_ Source: %s\n",      names[name]['source']) end
  256.                 end
  257.             end
  258.         end
  259.     end
  260.  
  261.     return response
  262. end
  263.  
  264. --real_action = action
  265. --
  266. -- function action (...)
  267. --     local t = {n = select("#", ...), ...};
  268. --     local status, ret = xpcall(function() return real_action(unpack(t, 1, t.n)) end, debug.traceback)
  269. -- 
  270. --     if not status then 
  271. --         error(ret) 
  272. --     end
  273. -- 
  274. --     return ret
  275. -- end
  276.  
  277.