[NSE] SMB2/SMB3 library and scripts smb-protocols, smb2-capabilities and smb2-security-mode
Paulino Calderon <[email protected]>
| Newsgroups | gmane.comp.security.nmap.devel |
|---|---|
| Message-ID | <[email protected]> |
Hi list, I've been working on the NSE library for handling SMB2/SMB3 packets and although it is still a work in progress, there are some modules ready! Besides a new version of smb.lua and obviously smb2.lua, I'm attaching some new scripts: * smb-protocols: Lists supported SMB1/SMB2/SMB3 protocols and dialects * smb2-capabilities: Lists the capabilities of SMB2/SMB3 servers * smb2-security-mode: Reads the message signing configuration in SMB2/SMB3 servers. My plan is to keep implementing all missing features as I go adding SMB2/SMB3 support to our existing SMB scripts. As this can take me some time, I'm planning on merging these new changes in the coming days if feedback does not bring up any important issues. I've tested this against all modern versions of Windows and Samba but I've found there are some strange implementations out there! Any comments or bug reports are greatly appreciated! smb-protocols.nse ============== description = [[ Attempts to list the supported protocols and dialects of a SMB server. The script attempts to initiate a connection using the dialects: * NT LM 0.12 (SMBv1) * 2.02 (SMBv2) * 2.10 (SMBv2) * 3.00 (SMBv3) * 3.02 (SMBv3) * 3.11 (SMBv3) Aditionally if SMBv1 is found enabled, it will mark it as insecure. ]] --- -- @usage nmap -p445 --script smb-protocols <target> -- @usage nmap -p139 --script smb-protocols <target> -- -- @output -- | smb-protocols: -- | dialects: -- | NT LM 0.12 (SMBv1) [dangerous, but default] -- | 2.02 -- | 2.10 -- | 3.00 -- | 3.02 -- |_ 3.11 smb2-capabilities.nse ================= description = [[ Attempts to list the supported capabilities in a SMBv2 server for each enabled dialect. The script sends a SMB2_COM_NEGOTIATE command and parses the response using the SMB dialects: * 2.02 * 2.10 * 3.00 * 3.02 * 3.11 References: * https://msdn.microsoft.com/en-us/library/cc246561.aspx ]] --- -- @usage nmap -p 445 --script smb2-capabilities <target> -- @usage nmap -p 139 --script smb2-capabilities <target> -- -- @output -- | smb2-capabilities: -- | 2.02: -- | Distributed File System -- | 2.10: -- | Distributed File System -- | Leasing -- | Multi-credit operations smb2-security-mode.nse ================= description = [[ Determines the message signing configuration in SMBv2 servers for all supported dialects. The script sends a SMB2_COM_NEGOTIATE request for each SMB2/SMB3 dialect and parses the security mode field to determine the message signing configuration of the SMB server. References: * https://msdn.microsoft.com/en-us/library/cc246561.aspx ]] --- -- @usage nmap -p 445 --script smb2-security-mode <target> -- @usage nmap -p 139 --script smb2-security-mode <target> -- -- @output -- | smb2-security-mode: -- | 3.11: -- |_ Message signing enabled but not required Links ====== smb2.lua: https://github.com/cldrn/nmap/blob/smbv2/nselib/smb2.lua smb.lua: https://github.com/cldrn/nmap/blob/smbv2/nselib/smb.lua smb-protocols: https://github.com/cldrn/nmap/blob/smbv2/scripts/smb-protocols.nse smb2-capabilities: https://github.com/cldrn/nmap/blob/smbv2/scripts/smb2-capabilities.nse smb2-security-mode: https://github.com/cldrn/nmap/blob/smbv2/scripts/smb2-security-mode.nse smb2 branch: https://github.com/cldrn/nmap/tree/smbv2 Paulino Calderon Pale || @calderpwn on Twitter || http://www.calderonpale.com _______________________________________________ Sent through the dev mailing list https://nmap.org/mailman/listinfo/dev Archived at http://seclists.org/nmap-dev/
smb.lua.patch
(application/octet-stream, 5.9 KB)
Index: nselib/smb.lua
===================================================================
--- nselib/smb.lua (revision 36802)
+++ nselib/smb.lua (working copy)
@@ -136,6 +136,7 @@
local string = require "string"
local table = require "table"
local unicode = require "unicode"
+local smb2 = require "smb2"
_ENV = stdnse.module("smb", stdnse.seeall)
-- These arrays are filled in with constants at the bottom of this file
@@ -942,8 +943,9 @@
return true, header, parameters, data
end
---- Sends out <code>SMB_COM_NEGOTIATE</code>, which is typically the first SMB packet sent out.
---
+---
+-- Negotiates SMBv1 connections
+--
-- Sends the following:
-- * List of known protocols
--
@@ -974,37 +976,30 @@
-- * 'server_challenge' A random string used for challenge/response
-- * 'domain' The server's primary domain or workgroup
-- * 'server' The server's name
-function negotiate_protocol(smb, overrides)
+-- @param smb The SMB object associated with the connection.
+-- @param overrides Overrides table.
+-- @return (status, dialect) If status is true, the negotiated dialect in human readable form is returned as the second value.
+-- If status is false, the error message is returned.
+---
+function negotiate_v1(smb, overrides)
local header, parameters, data
- local pos
- local header1, header2, header3, header4, command, status, flags, flags2, pid_high, signature, unused, pid, mid
+ local result, err
+ local pos, header1, header2, header3, header4, command, status, flags, flags2, pid_high, signature, unused, pid, uid, tid, mid
- header = smb_encode_header(smb, command_codes['SMB_COM_NEGOTIATE'], overrides)
-
+ header = smb_encode_header(smb, command_codes['SMB_COM_NEGOTIATE'], overrides)
-- Make sure we have overrides
overrides = overrides or {}
-- Parameters are blank
parameters = ""
+ data = bin.pack("<CzCz", 2, (overrides['dialect'] or "NT LM 0.12"), 2, "")
- -- Data is a list of strings, terminated by a blank one.
- if(overrides['dialects'] == nil) then
- data = bin.pack("<CzCz", 2, (overrides['dialect'] or "NT LM 0.12"), 2, "")
- else
- data = ""
- for _, v in ipairs(overrides['dialects']) do
- data = data .. bin.pack("<Cz", 2, v)
- end
- data = data .. bin.pack("Cz", 2, "")
- end
-
-- Send the negotiate request
stdnse.debug2("SMB: Sending SMB_COM_NEGOTIATE")
- local result, err = smb_send(smb, header, parameters, data, overrides)
+ result, err = smb_send(smb, header, parameters, data, overrides)
if(status == false) then
return false, err
end
-
-- Read the result
status, header, parameters, data = smb_read(smb)
if(status ~= true) then
@@ -1012,7 +1007,6 @@
end
-- Parse out the header
- local uid, tid
pos, header1, header2, header3, header4, command, status, flags, flags2, pid_high, signature, unused, tid, pid, uid, mid = bin.unpack("<CCCCCICSSlSSSSS", header)
-- Get the protocol version
@@ -1043,6 +1037,7 @@
end
-- Check if the server didn't like our requested protocol
if(smb['dialect'] ~= 0) then
+ stdnse.debug2("Server negotiated an unknown protocol (#%d) -- aborting", smb['dialect'])
return false, string.format("Server negotiated an unknown protocol (#%d) -- aborting", smb['dialect'])
end
@@ -1118,10 +1113,78 @@
end
end
- return true
+ stdnse.debug2("SMB_COM_NEGOTIATE got status:%s", status)
+ if status == 0 then
+ return true, overrides['dialect'] or "NT LM 0.12"
+ end
end
+---
+-- Wrapper function to negotiate the protocol to use in the SMB connection.
+-- By default it attempts to negotiate with using following dialects:
+-- * NT LM 12.0 (SMBv1)
+---
+function negotiate_protocol(smb, overrides)
+ local status, dialect
+ status, dialect = negotiate_v1(smb, overrides)
+ if status then
+ return true
+ else
+ stdnse.debug1("Couldn't negotiate a SMBv1 connection:%s", dialect)
+ -- TODO: Try SMB2/SMB3 dialects if SMBv1 failed.
+ return false, string.format("Could not negotiate a connection:%s", dialect)
+ end
+end
+---
+-- Returns list of supported dialects for SMBv1, SMBv2 and SMBv3.
+-- @param host The SMB host to connect to.
+-- @param overrides [optional] Overrides for various fields.
+-- @return (status, result) If status is false, result is an error message. Otherwise, result is table of dialects
+---
+function list_dialects(host, overrides)
+ local smb2_dialects = {0x0202, 0x0210, 0x0300, 0x0302, 0x0311}
+ local supported_dialects = {}
+ local status, smb1_dialects
+ local smbstate
+
+ -- Check for SMBv1 first
+ stdnse.debug2("Checking if SMBv1 is supported")
+ status, smbstate = start(host)
+ if(status == false) then
+ return false, smbstate
+ end
+
+ status, smb1_dialects = negotiate_v1(smbstate, overrides)
+ if status then --Add SMBv1 as a dialect
+ table.insert(supported_dialects, smb1_dialects)
+ end
+ stop(smbstate)
+ status = false -- Finish SMBv1 and close connection
+
+ -- Check SMB2 and SMB3 dialects
+ for i, dialect in pairs(smb2_dialects) do
+ local dialect_human = stdnse.tohex(dialect, {separator = ".", group = 2})
+ -- we need a clean connection for each negotiate request
+ status, smbstate = start(host)
+ if(status == false) then
+ return false, smbstate
+ end
+ stdnse.debug2("Checking if dialect '%s' is supported", dialect_human)
+ overrides['Dialects'] = {dialect}
+ status, dialect = smb2.negotiate_v2(smbstate, overrides)
+ if status then
+ stdnse.debug2("SMB2: Dialect '%s' is supported", dialect_human)
+ table.insert(supported_dialects, dialect_human)
+ end
+ --clean smb connection
+ stop(smbstate)
+ status = false
+ end
+
+ return true, supported_dialects
+end
+
--- This is an internal function and should not be called externally. Use
-- the start_session() function instead.
local function start_session_basic(smb, log_errors, overrides)
smb2-capabilities.nse
(application/octet-stream, 3.5 KB) - not displayed
smb-protocols.nse
(application/octet-stream, 1.8 KB) - not displayed
smb.lua
(application/octet-stream, 177.4 KB) - not displayed
smb2.lua
(application/octet-stream, 16.4 KB) - not displayed
smb2-security-mode.nse
(application/octet-stream, 3.1 KB) - not displayed