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

  1. description = [[
  2. Attempts to list shares using the <code>srvsvc.NetShareEnumAll</code> MSRPC function and
  3. retrieve more information about them using <code>srvsvc.NetShareGetInfo</code>. If access
  4. to those functions is denied, a list of common share names are checked. 
  5.  
  6. Finding open shares is useful to a penetration tester because there may be private files
  7. shared, or, if it's writable, it could be a good place to drop a Trojan or to infect a file
  8. that's already there. Knowing where the share is could make those kinds of tests more useful, 
  9. except that determiing where the share is requires administrative privileges already. 
  10.  
  11. Running <code>NetShareEnumAll</code> will work anonymously against Windows 2000, and 
  12. requires a user-level account on any other Windows version. Calling <code>NetShareGetInfo</code> 
  13. requires an administrator account on all versions of Windows up to 2003, as well as Windows Vista
  14. and Windows 7, if UAC is turned down. 
  15.  
  16. Even if <code>NetShareEnumAll</code> is restricted, attempting to connect to a share will always
  17. reveal its existence. So, if <code>NetShareEnumAll</code> fails, a pre-generated list of shares,
  18. based on a large test network, are used. If any of those succeed, they are recorded. 
  19.  
  20. After a list of shares is found, the script attempts to connect to each of them anonymously, 
  21. which divides them into "anonymous", for shares that the NULL user can connect to, or "restricted",
  22. for shares that require a user account. 
  23. ]]
  24.  
  25. ---
  26. --@usage
  27. -- nmap --script smb-enum-shares.nse -p445 <host>
  28. -- sudo nmap -sU -sS --script smb-enum-shares.nse -p U:137,T:139 <host>
  29. --
  30. --@output
  31. -- Standard:
  32. -- |  smb-enum-shares:
  33. -- |  Anonymous shares: IPC$
  34. -- |_ Restricted shares: F$, ADMIN$, C$
  35. --
  36. -- Verbose:
  37. -- Host script results:
  38. -- |  smb-enum-shares: 
  39. -- |  Anonymous shares:
  40. -- |     IPC$
  41. -- |     |_ Type: STYPE_IPC_HIDDEN
  42. -- |     |_ Comment: Remote IPC
  43. -- |     |_ Users: 1, Max: <unlimited>
  44. -- |     |_ Path:
  45. -- |     test
  46. -- |     |_ Type: STYPE_DISKTREE
  47. -- |     |_ Comment: This is a test share, with a maximum of 7 users
  48. -- |     |_ Users: 0, Max: 7
  49. -- |     |_ Path: C:\Documents and Settings\Ron\Desktop\test
  50. -- |  Restricted shares:
  51. -- |     ADMIN$
  52. -- |     |_ Type: STYPE_DISKTREE_HIDDEN
  53. -- |     |_ Comment: Remote Admin
  54. -- |     |_ Users: 0, Max: <unlimited>
  55. -- |     |_ Path: C:\WINNT
  56. -- |     C$
  57. -- |     |_ Type: STYPE_DISKTREE_HIDDEN
  58. -- |     |_ Comment: Default share
  59. -- |     |_ Users: 0, Max: <unlimited>
  60. -- |_    |_ Path: C:\
  61. -----------------------------------------------------------------------
  62.  
  63. author = "Ron Bowes"
  64. copyright = "Ron Bowes"
  65. license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
  66. categories = {"discovery","intrusive"}
  67.  
  68. require 'msrpc'
  69. require 'smb'
  70. require 'stdnse'
  71.  
  72. hostrule = function(host)
  73.     return smb.get_port(host) ~= nil
  74. end
  75.  
  76. ---Attempts to connect to a list of shares as the anonymous user, returning which ones
  77. -- it has and doesn't have access to. 
  78. --
  79. --@param host   The host object.
  80. --@param shares An array of shares to check.
  81. --@return List of shares we're allowed to access.
  82. --@return List of shares that exist but are denied to us.
  83. function check_shares(host, shares)
  84.     local smbstate
  85.     local i
  86.     local allowed_shares = {}
  87.     local denied_shares = {}
  88.  
  89.     -- Begin the SMB session
  90.     status, smbstate = smb.start(host)
  91.     if(status == false) then
  92.         return false, smbstate
  93.     end
  94.  
  95.     -- Negotiate the protocol
  96.     status, err = smb.negotiate_protocol(smbstate)
  97.     if(status == false) then
  98.         smb.stop(smbstate)
  99.         return false, err
  100.     end
  101.  
  102.     -- Start up a null session
  103.     status, err = smb.start_session(smbstate, "", "", "", "", "LM")
  104.     if(status == false) then
  105.         smb.stop(smbstate)
  106.         return false, err
  107.     end
  108.  
  109.     -- Check for hosts that accept any share by generating a totally random name (we don't use a set
  110.     -- name because then hosts could potentially fool us. Perhaps I'm in a paranoid mood today)
  111.     local set = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"
  112.     local share = ""
  113.     math.randomseed(os.time())
  114.     for i = 1, 16, 1 do
  115.         local random = math.random(#set)
  116.         share = share .. string.sub(set, random, random)
  117.     end
  118.  
  119.     share = string.format("%s", share)
  120.     stdnse.print_debug(2, "EnumShares: Trying a random share to see if server responds properly: %s", share)
  121.     status, err = smb.tree_connect(smbstate, share)
  122.     if(status == false) then
  123.         if(err == 0xc0000022 or err == 'NT_STATUS_ACCESS_DENIED') then
  124.             return false, "Server doesn't return proper value for non-existent shares (returns ACCESS_DENIED)"
  125.         end
  126.     else
  127.         -- If we were actually able to connect to this share, then there's probably a serious issue
  128.         smb.tree_disconnect(smbstate)
  129.         return false, "Server doesn't return proper value for non-existent shares (accepts the connection)"
  130.     end
  131.  
  132.     -- Connect to the shares
  133.     stdnse.print_debug(2, "EnumShares: Testing %d shares", #shares)
  134.     for i = 1, #shares, 1 do
  135.  
  136.         -- Change the share to the '\\ip\share' format
  137.         local share = string.format("%s", shares[i])
  138.  
  139.         -- Try connecting to the tree
  140.         stdnse.print_debug(3, "EnumShares: Testing share %s", share)
  141.         status, err = smb.tree_connect(smbstate, share)
  142.         -- If it fails, checkwhy
  143.         if(status == false) then
  144.             -- If the result was ACCESS_DENIED, record it
  145.             if(err == 0xc0000022 or err == 'NT_STATUS_ACCESS_DENIED') then
  146.                 stdnse.print_debug(3, "EnumShares: Access was denied")
  147.                 denied_shares[#denied_shares + 1] = shares[i]
  148.             else
  149.                 -- If we're here, an error that we weren't prepared for came up. 
  150. --                smb.stop(smbstate)
  151. --                return false, string.format("Error while checking shares: %s", err)
  152.             end
  153.         else
  154.             -- Add it to allowed shares
  155.             stdnse.print_debug(3, "EnumShares: Access was granted")
  156.             allowed_shares[#allowed_shares + 1] = shares[i]
  157.             smb.tree_disconnect(smbstate)
  158.         end
  159.     end
  160.  
  161.     -- Log off the user
  162.     smb.stop(smbstate)
  163.  
  164.     return true, allowed_shares, denied_shares
  165. end
  166.  
  167. action = function(host)
  168.  
  169.     local enum_result
  170.     local result, shared
  171.     local response = " \n"
  172.     local shares = {}
  173.     local allowed, denied
  174.  
  175.     -- Try and do this the good way, make a MSRPC call to get the shares
  176.     enum_result, shares = msrpc.enum_shares(host)
  177.  
  178.     -- If that failed, try doing it with brute force. This almost certainly won't find everything, but it's the
  179.     -- best we can do. 
  180.     if(enum_result == false) then
  181.         if(nmap.debugging() > 0) then
  182.             response = response .. string.format("ERROR: Couldn't enum all shares, checking for common ones (%s)\n", shares)
  183.         end
  184.  
  185.         -- Take some common share names I've seen
  186.         shares = {"IPC$", "ADMIN$", "TEST", "TEST$", "HOME", "HOME$", "PORN", "PR0N", "PUBLIC", "PRINT", "PRINT$", "GROUPS", "USERS", "MEDIA", "SOFTWARE", "XSERVE", "NETLOGON", "INFO", "PROGRAMS", "FILES", "WWW", "STMP", "TMP", "DATA", "BACKUP", "DOCS", "HD", "WEBSERVER", "WEB DOCUMENTS", "SHARED"}
  187.  
  188.         -- Try every alphabetic share, with and without a trailing '$'
  189.         for i = string.byte("A", 1), string.byte("Z", 1), 1 do
  190.             shares[#shares + 1] = string.char(i)
  191.             shares[#shares + 1] = string.char(i) .. "$"
  192.         end
  193.     end
  194.  
  195.     -- Break them into anonymous/authenticated shares
  196.     status, allowed, denied = check_shares(host, shares)
  197.  
  198.     if(status == false) then
  199.         if(enum_result == false) then
  200.             -- At this point, we have nothing
  201.             if(nmap.debugging() > 0) then
  202.                 return "ERROR: " .. allowed
  203.             else
  204.                 return nil
  205.             end
  206.         else
  207.             -- If we're here, we have a valid list of shares, but couldn't check them
  208.             if(nmap.debugging() > 0) then
  209.                 return "ERROR: " .. allowed .. "\nShares found: " .. stdnse.strjoin(", ", shares)
  210.             else
  211.                 return stdnse.strjoin(", ", shares)
  212.             end
  213.         end
  214.     end
  215.  
  216.     if(result == false or nmap.verbosity() == 0) then
  217.         return response .. string.format("Anonymous shares: %s\nRestricted shares: %s\n", stdnse.strjoin(", ", allowed), stdnse.strjoin(", ", denied))
  218.     else
  219.         response = response .. string.format("Anonymous shares:\n")
  220.         for i = 1, #allowed, 1 do
  221.             local status, info = msrpc.get_share_info(host, allowed[i])
  222.  
  223.             response = response .. string.format("   %s\n", allowed[i])
  224.  
  225.             if(status == false) then
  226.                 stdnse.print_debug(2, "ERROR: Couldn't get information for share %s: %s", allowed[i], info)
  227.             else
  228.                 info = info['info']
  229.  
  230.                 if(info['max_users'] == 0xFFFFFFFF) then
  231.                     info['max_users'] = "<unlimited>"
  232.                 end
  233.  
  234.                 response = response .. string.format("   |_ Type: %s\n",           msrpc.srvsvc_ShareType_tostr(info['sharetype']))
  235.                 response = response .. string.format("   |_ Comment: %s\n",        info['comment'])
  236.                 response = response .. string.format("   |_ Users: %s, Max: %s\n", info['current_users'], info['max_users'])
  237.                 response = response .. string.format("   |_ Path: %s\n",           info['path'])
  238.             end
  239.         end
  240.  
  241.         response = response .. string.format("Restricted shares:\n")
  242.         for i = 1, #denied, 1 do
  243.             local status, info = msrpc.get_share_info(host, denied[i])
  244.  
  245.             response = response .. string.format("   %s\n", denied[i])
  246.  
  247.             if(status == false) then
  248.                 stdnse.print_debug(2, "ERROR: Couldn't get information for share %s: %s", denied[i], info)
  249.             else
  250.                 info = info['info']
  251.                 if(info['max_users'] == 0xFFFFFFFF) then
  252.                     info['max_users'] = "<unlimited>"
  253.                 end
  254.  
  255.                 response = response .. string.format("   |_ Type: %s\n",           msrpc.srvsvc_ShareType_tostr(info['sharetype']))
  256.                 response = response .. string.format("   |_ Comment: %s\n",        info['comment'])
  257.                 response = response .. string.format("   |_ Users: %s, Max: %s\n", info['current_users'], info['max_users'])
  258.                 response = response .. string.format("   |_ Path: %s\n",           info['path'])
  259.             end
  260.         end
  261.  
  262.         return response
  263.     end
  264. end
  265.  
  266.  
  267.