--- -- Implements the Server Message Block (SMB) protocol version 2 and 3. -- -- The implementation extends smb.lua to support SMB dialects 2.02, 2.10, 3.0, -- 3.02 and 3.11. This is a work in progress and not all commands are -- implemented yet. Features/functionality will be added as the scripts -- get updated. I tried to be consistent with the current implementation of -- smb.lua but some fields may have changed name or don't exist anymore. -- -- @author Paulino Calderon -- @copyright Same as Nmap--See https://nmap.org/book/man-legal.html --- local string = require "string" local stdnse = require "stdnse" local nmap = require "nmap" local table = require "table" local match = require "match" local os = require "os" _ENV = stdnse.module("smb2", stdnse.seeall) local TIMEOUT = 10000 local command_names = {} local command_codes = { SMB2_COM_NEGOTIATE = 0x0000, SMB2_COM_SESSION_SETUP = 0x0001, SMB2_COM_LOGOFF = 0x0002, SMB2_COM_TREE_CONNECT = 0x0003, SMB2_COM_TREE_DISCONNECT = 0x0004, SMB2_COM_CREATE = 0x0005, SMB2_COM_CLOSE = 0x0006, SMB2_COM_FLUSH = 0x0007, SMB2_COM_READ = 0x0008, SMB2_COM_WRITE = 0x0009, SMB2_COM_LOCK = 0x000A, SMB2_COM_IOCTL = 0x000B, SMB2_COM_CANCEL = 0x000C, SMB2_COM_ECHO = 0x000D, SMB2_COM_QUERY_DIRECTORY = 0x000E, SMB2_COM_CHANGE_NOTIFY = 0x000F, SMB2_COM_QUERY_INFO = 0x0010, SMB2_COM_SET_INFO = 0x0011, SMB2_COM_OPLOCK_BREAK = 0x0012 } local smb2_values_codes = {} local smb2_values = { -- Security Mode SMB2_NEGOTIATE_SIGNING_ENABLED = 0x0001, SMB2_NEGOTIATE_SIGNING_REQUIRED = 0x0002, -- Capabilities SMB2_GLOBAL_CAP_DFS = 0x00000001, SMB2_GLOBAL_CAP_LEASING = 0x00000002, SMB2_GLOBAL_CAP_LARGE_MTU = 0x00000004, SMB2_GLOBAL_CAP_MULTI_CHANNEL = 0x00000008, SMB2_GLOBAL_CAP_PERSISTENT_HANDLES = 0x00000010, SMB2_GLOBAL_CAP_DIRECTORY_LEASING = 0x00000020, SMB2_GLOBAL_CAP_ENCRYPTION = 0x00000040, -- Context Types SMB2_ENCRYPTION_CAPABILITIES = 0x0002, SMB2_PREAUTH_INTEGRITY_CAPABILITIES = 0x0001 } for i, v in pairs(command_codes) do command_names[v] = i end for i, v in pairs(smb2_values) do smb2_values_codes[v] = i end --- -- Creates a SMB2 SYNC header packet. -- -- SMB2 Packet Header - SYNC: -- * https://msdn.microsoft.com/en-us/library/cc246529.aspx -- -- @param smb The SMB object associated with the connection. -- @param command The SMB2 command to execute. -- @param overrides Overrides table. -- @return header The encoded SMB2 SYNC header. --- function smb2_encode_header_sync(smb, command, overrides) overrides = overrides or {} local sig = "\xFESMB" -- SMB2 packet local structureSize = 64 -- SYNC header structure size local flags = 0 -- TODO: Set flags that will work for all dialects -- Increase the message id if smb['MessageId'] then smb['MessageId'] = smb['MessageId'] + 1 end -- Header structure local header = string.pack("smb_encode_sync_header. -- @param data The data. -- @param overrides Overrides table. -- @return Boolean Status. -- @return An error message if status is false. --- function smb2_send(smb, header, data, overrides) overrides = overrides or {} local body = header .. data local attempts = 5 local status, err local out = string.pack(">II", netbios_data) if(netbios_length == nil) then return false, "SMB2: ERROR:Server returned less data than it was supposed to" end -- Make the length 24 bits netbios_length = netbios_length & 0x00FFFFFF -- The total length is the netbios_length, plus 4 (for the length itself) length = netbios_length + 4 local attempts = 5 local smb_data repeat attempts = attempts - 1 status, smb_data = smb['socket']:receive_buf(match.numbytes(netbios_length), true) until(status or (attempts == 0)) -- Make sure the connection is still alive if(status ~= true) then return false, "SMB2: Failed to receive bytes after 5 attempts: " .. smb_data end local result = netbios_data .. smb_data if(#result ~= length) then stdnse.debug1("SMB2: ERROR: Received wrong number of bytes, there will likely be issues (received %d, expected %d)", #result, length) return false, string.format("SMB2: ERROR: Didn't receive the expected number of bytes; received %d, expected %d. This will almost certainly cause some errors.", #result, length) end -- The header is 64 bytes. if (pos + 64 > #result) then stdnse.debug2("SMB2: SMB2 packet too small. Size needed to be at least '%d' but we got '%d' bytes", pos+64, #result) return false, "SMB2: ERROR: Header packet too small." end header, pos = string.unpack(" 3.11 local total_data = 0 -- Data counter local padding_data = "" -- Padding string to align contexts local context_data -- Holds Context data local is_0311 = false -- Flag for SMB 3.11 local status, err if not( overrides['Dialects'] ) then -- Set 2.02 as default dialect if user didn't select one overrides['Dialects'] = {0x0202} end header = smb2_encode_header_sync(smb, command_codes['SMB2_COM_NEGOTIATE'], overrides) -- We construct the first block that works for dialects 2.02 up to 3.11. data = string.pack(" 3.x GUID -- 16 bytes: ClientGuid ) -- The next block gets interpreted in different ways depending on the dialect if stdnse.contains(overrides['Dialects'], 0x0311) then is_0311 = true end -- If we are dealing with 3.11 we need to set the following fields: -- NegotiateContextOffset, NegotiateContextCount, and Reserved2 if is_0311 then total_data = #header + #data + (DialectCount*2) padding_data = string.rep("\0", (8 - total_data % 8) % 8) total_data = total_data + #padding_data data = data .. string.pack("