From 82ce5f2a92541daaacb1f45c525b8cca83e8f4f9 Mon Sep 17 00:00:00 2001 From: ron Date: Mon, 10 Nov 2008 16:23:12 +0000 Subject: [PATCH] Merge from /nmap-exp/ron/ms08-067-test --- nselib/msrpc.lua | 187 +++++++++++++++++++++++++++++++++++++ nselib/smb.lua | 129 ++++++++++++++++++++----- scripts/smb-checkvulns.nse | 140 +++++++++++++++++++++++++++ 3 files changed, 433 insertions(+), 23 deletions(-) create mode 100644 scripts/smb-checkvulns.nse diff --git a/nselib/msrpc.lua b/nselib/msrpc.lua index 86254b840..23fe656e4 100644 --- a/nselib/msrpc.lua +++ b/nselib/msrpc.lua @@ -1016,6 +1016,193 @@ function srvsvc_netservergetstatistics(smbstate, server) end +---This calls NetPathCanonicalize(), which was the target of ms08-067. I haven't gotten this +-- working, yet, I trigger it indirectly through NetPathCompare(). +--function srvsvc_netpathcanonicalize(smbstate, server, prefix, path) +-- local i, j +-- local status, result +-- local arguments +-- local pos, align +-- +-- local response = {} +-- +-- stdnse.print_debug(2, "MSRPC: Calling NetPathCanonicalize(%s) [%s]", path, smbstate['ip']) +-- +---- [in] [string,charset(UTF16)] uint16 *server_unc, +-- arguments = bin.pack("ERROR_INVALID_PARAMETER. +-- +-- Note that the srvsvc.exe process occasionally crashes when attempting this. +-- +--@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 path1 The first path to compare +--@param path2 The second path to compare +--@param pathtype The pathtype to pass to the function (I always use '1') +--@param pathflags The pathflags to pass to the function (I always use '0') +--@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values containing +-- 'return'. +function srvsvc_netpathcompare(smbstate, server, path1, path2, pathtype, pathflags) + local i, j + local status, result + local arguments + local pos, align + + local response = {} + + stdnse.print_debug(2, "MSRPC: Calling NetPathCompare(%s, %s) [%s]", path1, path2, smbstate['ip']) + +-- [in] [string,charset(UTF16)] uint16 *server_unc, + arguments = bin.pack("connect4 function, to obtain a "connect handle". This must be done before calling many -- of the SAMR functions. -- diff --git a/nselib/smb.lua b/nselib/smb.lua index df09441f0..37a11fd45 100644 --- a/nselib/smb.lua +++ b/nselib/smb.lua @@ -248,7 +248,7 @@ function start(host) stdnse.print_debug(2, "SMB: Starting SMB session for %s (%s)", host.name, host.ip) if(port == nil) then - return false, "Couldn't find a valid port to check" + return false, "SMB: Couldn't find a valid port to check" end stdnse.print_debug(3, "SMB: Attempting to lock SMB mutex") @@ -261,7 +261,7 @@ function start(host) if(status == false) then stdnse.print_debug(3, "SMB: Attempting to release SMB mutex (1)") mutex "done" - stdnse.print_debug(3, "SMB: SMB mutex released (1)") + stdnse.print_debug(3, "SMB: mutex released (1)") end return status, state @@ -281,7 +281,7 @@ function start(host) mutex "done" stdnse.print_debug(3, "SMB: SMB mutex released (3)") - return false, "Couldn't find a valid port to check" + return false, "SMB: Couldn't find a valid port to check" end --- Kills the SMB connection, closes the socket, and releases the mutex. Because of the mutex @@ -312,7 +312,7 @@ function stop(smb) local status, err = smb['socket']:close() if(status == false) then - return false, "Failed to close socket: " .. err + return false, "SMB: Failed to close socket: " .. err end end @@ -333,7 +333,7 @@ function start_raw(host, port) status, err = socket:connect(host.ip, port, "tcp") if(status == false) then - return false, "Failed to connect to host: " .. err + return false, "SMB: Failed to connect to host: " .. err end return true, socket @@ -439,7 +439,7 @@ function start_netbios(host, port, name) status, err = socket:connect(host.ip, port, "tcp") if(status == false) then socket:close() - return false, "Failed to connect: " .. err + return false, "SMB: Failed to connect: " .. err end -- Send the session request @@ -447,7 +447,7 @@ function start_netbios(host, port, name) status, err = socket:send(session_request) if(status == false) then socket:close() - return false, "Failed to send: " .. err + return false, "SMB: Failed to send: " .. err end socket:set_timeout(5000) @@ -456,7 +456,7 @@ function start_netbios(host, port, name) status, result = socket:receive_bytes(4); if(status == false) then socket:close() - return false, "Failed to close socket: " .. result + return false, "SMB: Failed to close socket: " .. result end pos, result, flags, length = bin.unpack(">CCS", result) @@ -477,7 +477,7 @@ function start_netbios(host, port, name) -- We reached the end of our names list stdnse.print_debug(1, "SMB: None of the NetBIOS names worked!") - return false, "Couldn't find a NetBIOS name that works for the server. Sorry!" + return false, "SMB: Couldn't find a NetBIOS name that works for the server. Sorry!" end ---Generate the Lanman v1 hash (LMv1). The generated hash is incredibly easy to reverse, because the input @@ -489,7 +489,7 @@ end --@return (status, hash) If status is true, the hash is returned; otherwise, an error message is returned. function lm_create_hash(password) if(have_ssl ~= true) then - return false, "OpenSSL not present" + return false, "SMB: OpenSSL not present" end local str1, str2 @@ -525,7 +525,7 @@ end --@return (status, hash) If status is true, the hash is returned; otherwise, an error message is returned. function ntlm_create_hash(password) if(have_ssl ~= true) then - return false, "OpenSSL not present" + return false, "SMB: OpenSSL not present" end local i @@ -547,7 +547,7 @@ end --@return (status, response) If status is true, the response is returned; otherwise, an error message is returned. function lm_create_response(lanman, challenge) if(have_ssl ~= true) then - return false, "OpenSSL not present" + return false, "SMB: OpenSSL not present" end local str1, str2, str3 @@ -593,7 +593,7 @@ end --@return (status, response) If status is true, the response is returned; otherwise, an error message is returned. function ntlmv2_create_hash(ntlm, username, domain) if(have_ssl ~= true) then - return false, "OpenSSL not present" + return false, "SMB: OpenSSL not present" end local unicode = "" @@ -641,7 +641,7 @@ end -- it on every Windows system from Windows 2000 to Windows Vista, and it has always worked. function ntlmv2_create_response(ntlm, username, domain, challenge, client_challenge_length) if(have_ssl ~= true) then - return false, "OpenSSL not present" + return false, "SMB: OpenSSL not present" end local client_challenge = openssl.rand_bytes(client_challenge_length) @@ -785,12 +785,15 @@ function smb_read(smb) -- Make sure the connection is still alive if(status ~= true) then - return false, "Failed to receive bytes: " .. result + return false, "SMB: Failed to receive bytes: " .. result end -- The length of the packet is 4 bytes of big endian (for our purposes). -- The NetBIOS header is 4 bytes, big endian pos, netbios_length = bin.unpack(">I", result) + if(netbios_length == nil) then + return false, "SMB: Malformed packet received" + end -- The total length is the netbios_length, plus 4 (for the length itself) length = netbios_length + 4 @@ -804,7 +807,7 @@ function smb_read(smb) -- Make sure the connection is still alive if(status ~= true) then - return false, "Failed to receive bytes: " .. result + return false, "SMB: Failed to receive bytes: " .. result end -- Append the new data to the old stuff @@ -819,14 +822,33 @@ function smb_read(smb) -- The header is 32 bytes. pos, header = bin.unpack(" +-- sudo nmap -sU -sS --script smb-checkvulns.nse -p U:137,T:139 +-- +--@output +-- Host script results: +-- |_ smb-checkvulns: This host is vulnerable to ms08-067 +-- +-- @args smb* This script supports the smbusername, +-- smbpassword, smbhash, smbguest, and +-- smbtype script arguments of the smb module. +----------------------------------------------------------------------- + +author = "Ron Bowes" +copyright = "Ron Bowes" +license = "Same as Nmap--See http://nmap.org/book/man-legal.html" +categories = {"discovery","intrusive"} + +require 'msrpc' +require 'smb' +require 'stdnse' + +hostrule = function(host) + + local port = smb.get_port(host) + + if(port == nil) then + return false + else + return true + end + +end + +---Check if the server is patched for ms08-067. This is done by calling NetPathCompare() with an +-- illegal string. If the string is accepted, then the server is vulnerable; if it's rejected, then +-- you're safe (for now). +-- +-- Based on a packet cap of this script, thanks go out to the author: +-- http://labs.portcullis.co.uk/download/ms08-067_check.py +-- +-- If there's a licensing issue, please let me (Ron Bowes) know so I can +-- +-- NOTE: This CAN crash stuff (ie, crash svchost.exe and force a reboot), so beware! In about 20 +-- tests I did, it crashed once. This is not a guarantee. +-- +--@param host The hose 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 (likey vulnerable). +local VULNERABLE = 1 +local PATCHED = 2 +local UNKNOWN = 3 +function check_ms08_067(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 path1 = "\\AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\\..\\n" + local path2 = "\\n" + status, netpathcompare_result = msrpc.srvsvc_netpathcompare(smbstate, host.ip, path1, path2, 1, 0) + + -- Stop the SMB session + msrpc.stop_smb(smbstate) + + if(status == false) then + if(string.find(netpathcompare_result, "INVALID_NAME") == nil) then + return true, UNKNOWN + else + return true, PATCHED + end + end + + + return true, VULNERABLE +end + + +action = function(host) + local status, result + + status, result = check_ms08_067(host) + + if(status == false) then + if(nmap.debugging() > 0) then + return "ERROR: " .. result + else + return nil + end + end + + if(result == VULNERABLE) then + response = "This host is vulnerable to ms08-067" + elseif(result == UNKNOWN) then + response = "This host is likely vulnerable to ms08-067 (it stopped responding during the test)" + else + if(nmap.verbosity() > 0) then + response = "This host is patched for ms08-067" + else + response = nil + end + end + + return response +end + + +