diff --git a/nselib/msrpc.lua b/nselib/msrpc.lua index ea59a9e28..414ebc423 100644 --- a/nselib/msrpc.lua +++ b/nselib/msrpc.lua @@ -778,6 +778,79 @@ function srvsvc_netpathcompare(smbstate, server, path1, path2, pathtype, pathfla end +---Call the NetPathCanonicalize() function, which is the target of ms08-067. +-- +--@param smbstate The SMB state table +--@param server The IP or Hostname of the server (seems to be ignored but it's a good idea to have it) +--@param path The path to canonicalize +--@return (status, result, error_result) If status is false, result is an error message and error_result is +-- the result table. Otherwise, result is a table of values. +function srvsvc_netpathcanonicalize(smbstate, server, path) + local i, j + local status, result + local arguments + local pos, align + + stdnse.print_debug(2, "MSRPC: Calling NetPathCanonicalize(%s) [%s]", path, smbstate['ip']) + +-- [in] [string,charset(UTF16)] uint16 *server_unc, + arguments = msrpctypes.marshall_unicode_ptr(server, true) +-- [in] [string,charset(UTF16)] uint16 path[], + arguments = arguments .. msrpctypes.marshall_unicode(path, true) +-- [out] [size_is(maxbuf)] uint8 can_path[], +-- [in] uint32 maxbuf, + arguments = arguments .. msrpctypes.marshall_int32(2) + +-- [in] [string,charset(UTF16)] uint16 prefix[], + arguments = arguments .. msrpctypes.marshall_unicode("\\", true) + +-- [in,out] uint32 pathtype, + arguments = arguments .. msrpctypes.marshall_int32(1) +-- [in] uint32 pathflags + arguments = arguments .. msrpctypes.marshall_int32(1) + + + -- Do the call + status, result = call_function(smbstate, 0x1F, arguments) + if(status ~= true) then + return false, result + end + + stdnse.print_debug(3, "MSRPC: NetPathCanonicalize() returned successfully") + + -- Make arguments easier to use + arguments = result['arguments'] + pos = 1 + +-- [in] [string,charset(UTF16)] uint16 *server_unc, +-- [in] [string,charset(UTF16)] uint16 path[], +-- [out] [size_is(maxbuf)] uint8 can_path[],A +-- [in] uint32 maxbuf, +-- [in] [string,charset(UTF16)] uint16 prefix[], +-- [in,out] uint32 pathtype, +-- [in] uint32 pathflags + + -- NOTE: This isn't being done correctly.. due to Wireshark's broken parsing, + -- and Samba's possibly-broken definition, I'm not sure how this is supposed + -- to be parsed. + pos, result['max_count'] = msrpctypes.unmarshall_int32(arguments, pos) + pos, result['can_path'] = msrpctypes.unmarshall_int32(arguments, pos) + pos, result['type'] = msrpctypes.unmarshall_int32(arguments, pos) + pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos) + + if(result['return'] == nil) then + return false, "Read off the end of the packet (srvsvc.netpathcanonicalize)" + end + if(result['return'] ~= 0) then + return false, smb.get_status_name(result['return']) .. " (srvsvc.netpathcanonicalize)", result + end + + return true, result + +end + + + ---A proxy to a msrpctypes function that converts a PasswordProperties to an english string. diff --git a/nselib/smb.lua b/nselib/smb.lua index d08b205bb..0c2ee4a76 100644 --- a/nselib/smb.lua +++ b/nselib/smb.lua @@ -2283,6 +2283,7 @@ status_codes = NT_STATUS_OK = 0x0000, NT_STATUS_WERR_BADFILE = 0x00000002, NT_STATUS_WERR_ACCESS_DENIED = 0x00000005, + NT_STATUS_WERR_UNKNOWN_57 = 0x00000057, NT_STATUS_WERR_INVALID_NAME = 0x0000007b, NT_STATUS_WERR_UNKNOWN_LEVEL = 0x0000007c, NT_STATUS_WERR_MORE_DATA = 0x000000ea, diff --git a/scripts/smb-check-vulns.nse b/scripts/smb-check-vulns.nse index c1bf001e0..f38e1f18a 100644 --- a/scripts/smb-check-vulns.nse +++ b/scripts/smb-check-vulns.nse @@ -1,7 +1,8 @@ description = [[ -Checks if a host is vulnerable to MS08-067, a Windows RPC vulnerability that -can allow remote code execution. This script will be expanded to check for more -vulnerabilities in the future. +Check for vulnerabilities: +* MS08-067, a Windows RPC vulnerability +* Connficker, an infection by the Connficker worm +* Unnamed regsvc DoS, a denial-of-service vulnerability I accidentically found in Windows 2003 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, @@ -18,6 +19,9 @@ If you set the script parameter 'unsafe', then scripts will run that are almost 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. + MS08-067 -- Checks if a host is vulnerable to MS08-067, a Windows RPC vulnerability that can allow remote code execution. Checking for MS08-067 is very dangerous, as the check is likely to crash systems. On a fairly wide scan conducted by Brandon Enright, we determined @@ -27,6 +31,11 @@ the check. Out of 82 vulnerable systems, 52 crashed. At the same time, MS08-067 is extremely critical to fix. Metasploit has a working and stable exploit for it, and any system vulnerable can very easily be compromised. +Connficker -- Checks if a host is infected with a known Connficker strain. This check +is based on the simple connficker scanner found on this page: +http://iv.cs.uni-bonn.de/wg/cs/applications/containing-conficker +Thanks to the folks who wrote that scanner! + 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 @@ -39,10 +48,6 @@ or higher to work. It is considered unsafe. you can show me a tool with a license that is compatible with Nmap's, post a request on the Nmap-dev mailing list and I'll add it to my list [Ron Bowes]). ]] --- Currently, this script checks if a host is vulnerable to ms08-067. I'd like to add --- checks for more vulnerabilities, but I'm worried about licensing/copyright issues --- (since I'd be basing them on non-free tools). - --- --@usage -- nmap --script smb-check-vulns.nse -p445 @@ -52,11 +57,14 @@ on the Nmap-dev mailing list and I'll add it to my list [Ron Bowes]). -- Host script results: -- | smb-check-vulns: -- | MS08-067: FIXED +-- | Connficker: Likely INFECTED -- |_ regsvc DoS: 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" @@ -94,9 +102,13 @@ local NOTRUN = 4 -- --@param host The host object. --@return (status, result) If status if alse, result is an error code; otherwise, result is either --- VULNERABLE for vulnerable, PATCHED for not vulnerable, or --- UNKNOWN if there was an error (likely vulnerable). +-- VULNERABLE for vulnerable, PATCHED for not vulnerable, +-- UNKNOWN if there was an error (likely vulnerable), and NOTRUN +-- if this check was disabled. function check_ms08_067(host) + if(nmap.registry.args.safe ~= nil) then + return true, NOTRUN + end local status, smbstate local bind_result, netpathcompare_result @@ -135,6 +147,59 @@ function check_ms08_067(host) return true, VULNERABLE end + +---Check if the server is infected with Connficker. This can be detected by a modified MS08-067 patch, +-- which rejects a different illegal string than the official patch rejects. +-- +-- Based loosely on the Simple Connficker Scanner, found here: +-- http://iv.cs.uni-bonn.de/wg/cs/applications/containing-conficker/ +-- +-- If there's a licensing issue, please let me (Ron Bowes) know so I can fix it +-- +--@param host The host object. +--@return (status, result) If status is false, result is an error code; otherwise, result is either +-- VULNERABLE for infected or PATCHED for not infected. +function check_connficker(host) + local status, smbstate + local bind_result, netpathcompare_result + + -- Create the SMB session + status, smbstate = msrpc.start_smb(host, "\\\\BROWSER") + if(status == false) then + return false, smbstate + end + + -- Bind to SRVSVC service + status, bind_result = msrpc.bind(smbstate, msrpc.SRVSVC_UUID, msrpc.SRVSVC_VERSION, nil) + if(status == false) then + msrpc.stop_smb(smbstate) + return false, bind_result + end + + -- Call netpathcanonicalize +-- status, netpathcanonicalize_result = msrpc.srvsvc_netpathcanonicalize(smbstate, host.ip, "\\a", "\\test\\") + + local path = "\\..\\" + local error_result + status, netpathcanonicalize_result, error_result = msrpc.srvsvc_netpathcanonicalize(smbstate, host.ip, path) + + -- Stop the SMB session + msrpc.stop_smb(smbstate) + + if(status == false) then + if(string.find(netpathcanonicalize_result, "INVALID_NAME")) then + return true, PATCHED + elseif(string.find(netpathcanonicalize_result, "UNKNOWN_57") ~= nil and error_result['can_path'] == 0x5c450000) then + return true, VULNERABLE + else + return false, "Unexpected error: " .. netpathcanonicalize_result + end + end + + + return true, PATCHED +end + ---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 @@ -147,13 +212,16 @@ end -- VULNERABLE for vulnerable or PATCHED for not vulnerable. If the check -- was skipped, NOTRUN is returned. function check_winreg_Enum_crash(host) - local i, j - local elements = {} - + 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 = {} + -- Create the SMB session status, smbstate = msrpc.start_smb(host, msrpc.WINREG_PATH) if(status == false) then @@ -195,9 +263,7 @@ action = function(host) status, result = check_ms08_067(host) if(status == false) then if(nmap.debugging() > 0) then - return "MS08-067: ERROR: " .. result - else - return nil + response = response .. "MS08-067: ERROR: " .. result .. "\n" end end if(result == VULNERABLE) then @@ -205,31 +271,51 @@ action = function(host) found = true elseif(result == UNKNOWN) then response = response .. "MS08-067: LIKELY VULNERABLE (host stopped responding)\n" + elseif(result == NOTRUN) then + response = response .. "MS08-067: NOT RUN\n" else if(nmap.verbosity() > 0) then response = response .. "MS08-067: FIXED\n" end end + -- Check for Connficker + status, result = check_connficker(host) + if(status == false) then + if(nmap.debugging() > 0) then + if(result == "NT_STATUS_BAD_NETWORK_NAME") then + response = response .. "Connficker: ERROR: Network name not found (required service has crashed)\n" + else + response = response .. "Connficker: ERROR: " .. result .. "\n" + end + end + else + if(result == PATCHED) then + response = response .. "Connficker: Likely CLEAN\n" + else + response = response .. "Connficker: Likely INFECTED\n" + found = true + end + end + -- Check for a winreg_Enum crash status, result = check_winreg_Enum_crash(host) if(status == false) then if(nmap.debugging() > 0) then - return response .. "regsvc DoS: ERROR: " .. result - else - return nil - end - end - if(result == VULNERABLE) then - response = response .. "regsvc DoS: VULNERABLE\n" - found = true - elseif(result == NOTRUN) then - if(nmap.verbosity() > 0) then - response = response .. "regsvc DoS: NOT RUN (add --script-args=unsafe=1 to run)\n" + response = response .. "regsvc DoS: ERROR: " .. result .. "\n" end else - if(nmap.verbosity() > 0) then - response = response .. "regsvc DoS: FIXED\n" + if(result == VULNERABLE) then + response = response .. "regsvc DoS: VULNERABLE\n" + found = true + elseif(result == NOTRUN) then + if(nmap.verbosity() > 0) then + response = response .. "regsvc DoS: NOT RUN (add --script-args=unsafe=1 to run)\n" + end + else + if(nmap.verbosity() > 0) then + response = response .. "regsvc DoS: FIXED\n" + end end end