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

  1. description = [[
  2. Check for vulnerabilities:
  3. * MS08-067, a Windows RPC vulnerability
  4. * Conficker, an infection by the Conficker worm
  5. * Unnamed regsvc DoS, a denial-of-service vulnerability I accidentically found in Windows 2000
  6.  
  7. WARNING: These checks are dangerous, and are very likely to bring down a server. 
  8. These should not be run in a production environment unless you (and, more importantly,
  9. the business) understand the risks! 
  10.  
  11. As a system administrator, performing these kinds of checks is crucial, because 
  12. a lot more damage can be done by a worm or a hacker using this vulnerability than
  13. by a scanner. Penetration testers, on the other hand, might not want to use this
  14. script -- crashing services is not generally a good way of sneaking through a 
  15. network. 
  16.  
  17. If you set the script parameter 'unsafe', then scripts will run that are almost 
  18. (or totally) guaranteed to crash a vulnerable system; do NOT specify <code>unsafe</code>
  19. in a production environment! And that isn't to say that non-unsafe scripts will 
  20. not crash a system, they're just less likely to. 
  21.  
  22. If you set the script parameter 'safe', then script will run that rarely or never
  23. crash a vulnerable system. No promises, though. 
  24.  
  25. MS08-067 -- Checks if a host is vulnerable to MS08-067, a Windows RPC vulnerability that
  26. can allow remote code execution.  Checking for MS08-067 is very dangerous, as the check 
  27. is likely to crash systems. On a fairly wide scan conducted by Brandon Enright, we determined
  28. that on average, a vulnerable system is more likely to crash than to survive
  29. the check. Out of 82 vulnerable systems, 52 crashed. 
  30.  
  31. At the same time, MS08-067 is extremely critical to fix. Metasploit has a working and
  32. stable exploit for it, and any system vulnerable can very easily be compromised. 
  33.  
  34. Conficker -- Checks if a host is infected with a known Conficker strain. This check
  35. is based on the simple conficker scanner found on this page:
  36. http://iv.cs.uni-bonn.de/wg/cs/applications/containing-conficker
  37. Thanks to the folks who wrote that scanner!
  38.  
  39. regsvc DoS -- Checks if a host is vulnerable to a crash in regsvc, caused 
  40. by a null pointer dereference. I inadvertently discovered this crash while working
  41. on <code>smb-enum-sessions</code>, and discovered that it was repeatable. It's been 
  42. reported to Microsoft (case #MSRC8742). 
  43.  
  44. This check WILL crash the service, if it's vulnerable, and requires a guest account
  45. or higher to work. It is considered <code>unsafe</code>. 
  46.  
  47. (Note: if you have other SMB/MSRPC vulnerability checks you'd like to see added, and
  48. you can show me a tool with a license that is compatible with Nmap's, post a request 
  49. on the Nmap-dev mailing list and I'll add it to my list [Ron Bowes]). 
  50. ]]
  51. ---
  52. --@usage
  53. -- nmap --script smb-check-vulns.nse -p445 <host>
  54. -- sudo nmap -sU -sS --script smb-check-vulns.nse -p U:137,T:139 <host>
  55. --
  56. --@output
  57. -- Host script results:
  58. -- |  smb-check-vulns:
  59. -- |  MS08-067: FIXED
  60. -- |  Conficker: Likely INFECTED
  61. -- |_ regsvc DoS: VULNERABLE
  62. --
  63. -- @args unsafe If set, this script will run checks that, if the system isn't
  64. --       patched, are basically guaranteed to crash something. Remember that
  65. --       non-unsafe checks aren't necessarily safe either)
  66. -- @args safe   If set, this script will only run checks that are known (or at
  67. --       least suspected) to be safe. 
  68. -----------------------------------------------------------------------
  69.  
  70. author = "Ron Bowes"
  71. copyright = "Ron Bowes"
  72. license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
  73. categories = {"intrusive"}
  74. -- Set the runlevel to >2 so this runs last (so if it DOES crash something, it doesn't
  75. -- till other scans have had a chance to run)
  76. runlevel = 2
  77.  
  78. require 'msrpc'
  79. require 'smb'
  80. require 'stdnse'
  81.  
  82. hostrule = function(host)
  83.     return smb.get_port(host) ~= nil
  84. end
  85.  
  86. local VULNERABLE = 1
  87. local PATCHED    = 2
  88. local UNKNOWN    = 3
  89. local NOTRUN     = 4
  90. local INFECTED   = 5
  91. local INFECTED2  = 6
  92. local CLEAN      = 7
  93.  
  94. ---Check if the server is patched for MS08-067. This is done by calling NetPathCompare with an 
  95. -- illegal string. If the string is accepted, then the server is vulnerable; if it's rejected, then
  96. -- you're safe (for now). 
  97. --
  98. -- Based on a packet cap of this script, thanks go out to the author:
  99. -- http://labs.portcullis.co.uk/download/ms08-067_check.py 
  100. --
  101. -- If there's a licensing issue, please let me (Ron Bowes) know so I can 
  102. --
  103. -- NOTE: This CAN crash stuff (ie, crash svchost and force a reboot), so beware! In about 20 
  104. -- tests I did, it crashed once. This is not a guarantee. 
  105. --
  106. --@param host The host object. 
  107. --@return (status, result) If status is false, result is an error code; otherwise, result is either 
  108. --        <code>VULNERABLE</code> for vulnerable, <code>PATCHED</code> for not vulnerable, 
  109. --        <code>UNKNOWN</code> if there was an error (likely vulnerable), <code>NOTRUN</code>
  110. --        if this check was disabled, and <code>INFECTED</code> if it was patched by Conficker. 
  111. function check_ms08_067(host)
  112.     if(nmap.registry.args.safe ~= nil) then
  113.         return true, NOTRUN
  114.     end
  115.     local status, smbstate
  116.     local bind_result, netpathcompare_result
  117.  
  118.     -- Create the SMB session
  119.     status, smbstate = msrpc.start_smb(host, "\\\\BROWSER")
  120.     if(status == false) then
  121.         return false, smbstate
  122.     end
  123.  
  124.     -- Bind to SRVSVC service
  125.     status, bind_result = msrpc.bind(smbstate, msrpc.SRVSVC_UUID, msrpc.SRVSVC_VERSION, nil)
  126.     if(status == false) then
  127.         msrpc.stop_smb(smbstate)
  128.         return false, bind_result
  129.     end
  130.  
  131.     -- Call netpathcanonicalize
  132. --    status, netpathcanonicalize_result = msrpc.srvsvc_netpathcanonicalize(smbstate, host.ip, "\\a", "\\test\\")
  133.     
  134.     local path1 = "\\AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\\..\\n"
  135.     local path2 = "\\n"
  136.     status, netpathcompare_result = msrpc.srvsvc_netpathcompare(smbstate, host.ip, path1, path2, 1, 0)
  137.  
  138.     -- Stop the SMB session
  139.     msrpc.stop_smb(smbstate)
  140.  
  141.     if(status == false) then
  142.         if(string.find(netpathcompare_result, "UNKNOWN_57") ~= nil) then
  143.             return true, INFECTED
  144.         elseif(string.find(netpathcompare_result, "INVALID_NAME") ~= nil) then
  145.             return true, PATCHED
  146.         else
  147.             return true, UNKNOWN, netpathcompare_result
  148.         end
  149.     end
  150.  
  151.  
  152.     return true, VULNERABLE
  153. end
  154.  
  155. -- Help messages for the more common errors seen by the Conficker check.
  156. CONFICKER_ERROR_HELP = {
  157.     ["NT_STATUS_BAD_NETWORK_NAME"] =
  158. [[UNKNOWN; Network name not found (required service has crashed). (Error NT_STATUS_BAD_NETWORK_NAME)]],
  159.     -- http://seclists.org/nmap-dev/2009/q1/0918.html "non-Windows boxes (Samba on Linux/OS X, or a printer)"
  160.     -- http://www.skullsecurity.org/blog/?p=209#comment-156
  161.     --   "That means either it isnΓÇÖt a Windows machine, or the service is
  162.     --    either crashed or not running. That may indicate a failed (or
  163.     --    successful) exploit attempt, or just a locked down system.
  164.     --    NT_STATUS_OBJECT_NAME_NOT_FOUND can be returned if the browser
  165.     --    service is disabled. There are at least two ways that can happen:
  166.     --    1) The service itself is disabled in the services list.
  167.     --    2) The registry key HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Browser\Parameters\MaintainServerList
  168.     --       is set to Off/False/No rather than Auto or yes.
  169.     --    On these systems, if you reenable the browser service, then the
  170.     --    test will complete."
  171.     ["NT_STATUS_OBJECT_NAME_NOT_FOUND"] = 
  172. [[UNKNOWN; not Windows, or Windows with disabled browser service (CLEAN); or Windows with crashed browser service (possibly INFECTED).
  173. |  If you know the remote system is Windows, try rebooting it and scanning
  174. |_ again. (Error NT_STATUS_OBJECT_NAME_NOT_FOUND)]],
  175.     -- http://www.skullsecurity.org/blog/?p=209#comment-100
  176.     --   "That likely means that the server has been locked down, so we
  177.     --    donΓÇÖt have access to the necessary pipe. Fortunately, that means
  178.     --    that neither does Conficker ΓÇö NT_STATUS_ACCESS_DENIED probably
  179.     --    means youΓÇÖre ok."
  180.     ["NT_STATUS_ACCESS_DENIED"] =
  181. [[Likely CLEAN; access was denied.
  182. |  If you have a login, try using --script-args=smbuser=xxx,smbpass=yyy
  183. |  (replace xxx and yyy with your username and password). Also try
  184. |_ smbdomain=zzz if you know the domain. (Error NT_STATUS_ACCESS_DENIED)]],
  185.     -- The cause of these two is still unknown.
  186.     -- ["NT_STATUS_NOT_SUPPORTED"] =
  187.     -- [[]]
  188.     -- http://thatsbroken.com/?cat=5 (doesn't seem common)
  189.     -- ["NT_STATUS_REQUEST_NOT_ACCEPTED"] =
  190.     -- [[]]
  191. }
  192.  
  193. ---Check if the server is infected with Conficker. This can be detected by a modified MS08-067 patch, 
  194. -- which rejects a different illegal string than the official patch rejects. 
  195. --
  196. -- Based loosely on the Simple Conficker Scanner, found here:
  197. -- http://iv.cs.uni-bonn.de/wg/cs/applications/containing-conficker/
  198. --
  199. -- If there's a licensing issue, please let me (Ron Bowes) know so I can fix it
  200. --
  201. --@param host The host object. 
  202. --@return (status, result) If status is false, result is an error code; otherwise, result is either 
  203. --        <code>INFECTED</code> for infected or <code>CLEAN</code> for not infected.
  204. function check_conficker(host)
  205.     local status, smbstate
  206.     local bind_result, netpathcompare_result
  207.  
  208.     -- Create the SMB session
  209.     status, smbstate = msrpc.start_smb(host, "\\\\BROWSER", true)
  210.     if(status == false) then
  211.         return false, smbstate
  212.     end
  213.  
  214.     -- Bind to SRVSVC service
  215.     status, bind_result = msrpc.bind(smbstate, msrpc.SRVSVC_UUID, msrpc.SRVSVC_VERSION, nil)
  216.     if(status == false) then
  217.         msrpc.stop_smb(smbstate)
  218.         return false, bind_result
  219.     end
  220.  
  221.     -- Try checking a valid string to find Conficker.D
  222.     status, netpathcanonicalize_result, error_result = msrpc.srvsvc_netpathcanonicalize(smbstate, host.ip, "\\")
  223.     if(status == true and netpathcanonicalize_result['can_path'] == 0x5c45005c) then
  224.         msrpc.stop_smb(smbstate)
  225.         return true, INFECTED2
  226.     end
  227.  
  228.     -- Try checking an illegal string ("\..\") to find Conficker.C and earlier
  229.     local error_result
  230.     status, netpathcanonicalize_result, error_result = msrpc.srvsvc_netpathcanonicalize(smbstate, host.ip, "\\..\\")
  231.  
  232.     if(status == false) then
  233.         if(string.find(netpathcanonicalize_result, "INVALID_NAME")) then
  234.             msrpc.stop_smb(smbstate)
  235.             return true, CLEAN
  236.         elseif(string.find(netpathcanonicalize_result, "UNKNOWN_57") ~= nil) then
  237.             msrpc.stop_smb(smbstate)
  238.             return true, INFECTED
  239.         else
  240.             msrpc.stop_smb(smbstate)
  241.             return false, netpathcanonicalize_result
  242.         end
  243.     end
  244.  
  245.     -- Stop the SMB session
  246.     msrpc.stop_smb(smbstate)
  247.  
  248.     return true, CLEAN
  249. end
  250.  
  251. ---While writing <code>smb-enum-sessions</code> I discovered a repeatable null-pointer dereference 
  252. -- in regsvc. I reported it to Microsoft, but because it's a simple DoS (and barely even that, because
  253. -- the service automatically restarts), and because it's only in Windows 2000, it isn't likely that they'll
  254. -- fix it. This function checks for that crash (by crashing the process). 
  255. --
  256. -- The crash occurs when the string sent to winreg_enumkey() function is null. 
  257. --
  258. --@param host The host object. 
  259. --@return (status, result) If status is false, result is an error code; otherwise, result is either 
  260. --        <code>VULNERABLE</code> for vulnerable or <code>PATCHED</code> for not vulnerable. If the check
  261. --        was skipped, <code>NOTRUN</code> is returned. 
  262. function check_winreg_Enum_crash(host)
  263.     if(nmap.registry.args.safe ~= nil) then
  264.         return true, NOTRUN
  265.     end
  266.     if(nmap.registry.args.unsafe == nil) then
  267.         return true, NOTRUN
  268.     end
  269.  
  270.     local i, j
  271.     local elements = {}
  272.  
  273.     -- Create the SMB session
  274.     status, smbstate = msrpc.start_smb(host, msrpc.WINREG_PATH)
  275.     if(status == false) then
  276.         return false, smbstate
  277.     end
  278.  
  279.     -- Bind to WINREG service
  280.     status, bind_result = msrpc.bind(smbstate, msrpc.WINREG_UUID, msrpc.WINREG_VERSION, nil)
  281.     if(status == false) then
  282.         msrpc.stop_smb(smbstate)
  283.         return false, bind_result
  284.     end
  285.  
  286.     status, openhku_result = msrpc.winreg_openhku(smbstate)
  287.     if(status == false) then
  288.         msrpc.stop_smb(smbstate)
  289.         return false, openhku_result
  290.     end
  291.  
  292.     -- Loop through the keys under HKEY_USERS and grab the names
  293.     status, enumkey_result = msrpc.winreg_enumkey(smbstate, openhku_result['handle'], 0, nil)
  294.     msrpc.stop_smb(smbstate)
  295.  
  296.     if(status == false) then
  297.         return true, VULNERABLE
  298.     end
  299.  
  300.     return true, PATCHED
  301. end
  302.  
  303. ---Returns the appropriate text to display, if any. 
  304. --
  305. --@param check The name of the check; for example, 'ms08-067'.
  306. --@param message The message to display, such as 'VULNERABLE' or 'PATCHED'.
  307. --@param description [optional] Extra details about the message. nil for a blank message. 
  308. --@param minimum_verbosity The minimum verbosity level required before the message is displayed.
  309. --@param minimum_debug [optional] The minimum debug level required before the message is displayed (default: 0).
  310. --@return A string with a textual representation of the error (or empty string, if it was determined that the message shouldn't be displayed). 
  311. local function get_response(check, message, description, minimum_verbosity, minimum_debug)
  312.     if(minimum_debug == nil) then
  313.         minimum_debug = 0
  314.     end
  315.  
  316.     -- Check if we have appropriate verbosity/debug
  317.     if(nmap.verbosity() >= minimum_verbosity and nmap.debugging() >= minimum_debug) then
  318.         if(description == nil or description == '') then
  319.             return string.format("%s: %s\n", check, message)
  320.         else
  321.             return string.format("%s: %s (%s)\n", check, message, description)
  322.         end
  323.     else
  324.         return ''
  325.     end
  326. end
  327.  
  328. action = function(host)
  329.  
  330.     local status, result, message
  331.     local response = ""
  332.  
  333.     -- Check for ms08-067
  334.     status, result, message = check_ms08_067(host)
  335.     if(status == false) then
  336.         response = response .. get_response("MS08-067", "ERROR", result, 0, 1)
  337.     else
  338.         if(result == VULNERABLE) then
  339.             response = response .. get_response("MS08-067", "VULNERABLE",        nil,                               0)
  340.         elseif(result == UNKNOWN) then
  341.             response = response .. get_response("MS08-067", "LIKELY VULNERABLE", "host stopped responding",         1) -- TODO: this isn't very accurate
  342.         elseif(result == NOTRUN) then
  343.             response = response .. get_response("MS08-067", "CHECK DISABLED",    "remove 'safe=1' argument to run", 1)
  344.         elseif(result == INFECTED) then
  345.             response = response .. get_response("MS08-067", "FIXED",             "likely by Conficker",             0)
  346.         else
  347.             response = response .. get_response("MS08-067", "FIXED", nil, 1)
  348.         end
  349.     end
  350.  
  351.     -- Check for Conficker
  352.     status, result = check_conficker(host)
  353.     if(status == false) then
  354.         local msg = CONFICKER_ERROR_HELP[result] or "UNKNOWN; got error " .. result
  355.         response = response .. get_response("Conficker", msg, nil, 1) -- Only set verbosity for this, since it might be an error or it might be UNKNOWN
  356.     else
  357.         if(result == CLEAN) then
  358.             response = response .. get_response("Conficker", "Likely CLEAN",    nil,                        1)
  359.         elseif(result == INFECTED) then
  360.             response = response .. get_response("Conficker", "Likely INFECTED", "by Conficker.C or lower",  0)
  361.         elseif(result == INFECTED2) then
  362.             response = response .. get_response("Conficker", "Likely INFECTED", "by Conficker.D or higher", 0)
  363.         else
  364.             response = response .. get_response("Conficker", "UNKNOWN",         result,                     0, 1)
  365.         end
  366.     end
  367.  
  368.     -- Check for a winreg_Enum crash
  369.     status, result = check_winreg_Enum_crash(host)
  370.     if(status == false) then
  371.         response = response .. get_response("regsvc DoS", "ERROR", result, 0, 1)
  372.     else
  373.         if(result == VULNERABLE) then
  374.             response = response .. get_response("regsvc DoS", "VULNERABLE", nil, 0)
  375.         elseif(result == NOTRUN) then
  376.             response = response .. get_response("regsvc DoS", "CHECK DISABLED", "add '--script-args=unsafe=1' to run", 1)
  377.         else
  378.             response = response .. get_response("regsvc DoS", "FIXED", "add '--script-args=unsafe=1' to run", 1)
  379.         end
  380.     end
  381.  
  382.     -- If we got a response, add a linefeed
  383.     if(response ~= "") then
  384.         response = " \n" .. response
  385.     else
  386.         response = nil
  387.     end
  388.  
  389.     return response
  390. end
  391.  
  392.  
  393.  
  394.