1---
2-- Implements the Server Message Block (SMB) protocol version 2 and 3.
3--
4-- The implementation extends smb.lua to support SMB dialects 2.02, 2.10, 3.0,
5--  3.02 and 3.11. This is a work in progress and not all commands are
6--  implemented yet. Features/functionality will be added as the scripts
7--  get updated. I tried to be consistent with the current implementation of
8--  smb.lua but some fields may have changed name or don't exist anymore.
9--
10-- @author Paulino Calderon <paulino@calderonpale.com>
11-- @copyright Same as Nmap--See https://nmap.org/book/man-legal.html
12---
13
14local datetime = require "datetime"
15local string = require "string"
16local stdnse = require "stdnse"
17local table = require "table"
18local tableaux = require "tableaux"
19local match = require "match"
20
21_ENV = stdnse.module("smb2", stdnse.seeall)
22
23local TIMEOUT = 10000
24local command_names = {}
25local command_codes =
26{
27  SMB2_COM_NEGOTIATE              = 0x0000,
28  SMB2_COM_SESSION_SETUP          = 0x0001,
29  SMB2_COM_LOGOFF                 = 0x0002,
30  SMB2_COM_TREE_CONNECT           = 0x0003,
31  SMB2_COM_TREE_DISCONNECT        = 0x0004,
32  SMB2_COM_CREATE                 = 0x0005,
33  SMB2_COM_CLOSE                  = 0x0006,
34  SMB2_COM_FLUSH                  = 0x0007,
35  SMB2_COM_READ                   = 0x0008,
36  SMB2_COM_WRITE                  = 0x0009,
37  SMB2_COM_LOCK                   = 0x000A,
38  SMB2_COM_IOCTL                  = 0x000B,
39  SMB2_COM_CANCEL                 = 0x000C,
40  SMB2_COM_ECHO                   = 0x000D,
41  SMB2_COM_QUERY_DIRECTORY        = 0x000E,
42  SMB2_COM_CHANGE_NOTIFY          = 0x000F,
43  SMB2_COM_QUERY_INFO             = 0x0010,
44  SMB2_COM_SET_INFO               = 0x0011,
45  SMB2_COM_OPLOCK_BREAK           = 0x0012
46}
47local smb2_values_codes = {}
48local smb2_values = {
49  -- Security Mode
50  SMB2_NEGOTIATE_SIGNING_ENABLED      = 0x0001,
51  SMB2_NEGOTIATE_SIGNING_REQUIRED     = 0x0002,
52  -- Capabilities
53  SMB2_GLOBAL_CAP_DFS                 = 0x00000001,
54  SMB2_GLOBAL_CAP_LEASING             = 0x00000002,
55  SMB2_GLOBAL_CAP_LARGE_MTU           = 0x00000004,
56  SMB2_GLOBAL_CAP_MULTI_CHANNEL       = 0x00000008,
57  SMB2_GLOBAL_CAP_PERSISTENT_HANDLES  = 0x00000010,
58  SMB2_GLOBAL_CAP_DIRECTORY_LEASING   = 0x00000020,
59  SMB2_GLOBAL_CAP_ENCRYPTION          = 0x00000040,
60  -- Context Types
61  SMB2_ENCRYPTION_CAPABILITIES        = 0x0002,
62  SMB2_PREAUTH_INTEGRITY_CAPABILITIES = 0x0001
63}
64
65for i, v in pairs(command_codes) do
66  command_names[v] = i
67end
68for i, v in pairs(smb2_values) do
69  smb2_values_codes[v] = i
70end
71
72---
73-- Creates a SMB2 SYNC header packet.
74--
75-- SMB2 Packet Header - SYNC:
76-- * https://msdn.microsoft.com/en-us/library/cc246529.aspx
77--
78-- @param smb The SMB object associated with the connection.
79-- @param command The SMB2 command to execute.
80-- @param overrides Overrides table.
81-- @return header The encoded SMB2 SYNC header.
82---
83function smb2_encode_header_sync(smb, command, overrides)
84  overrides = overrides or {}
85
86  local sig = "\xFESMB" -- SMB2 packet
87  local structureSize = 64 -- SYNC header structure size
88  local flags = 0 -- TODO: Set flags that will work for all dialects
89
90  -- Increase the message id
91  if smb['MessageId'] then
92    smb['MessageId'] = smb['MessageId'] + 1
93  end
94
95  -- Header structure
96  local header = string.pack("<c4 I2 I2 I4 I2 I2 I4 I4 I8 I4 I4 I8 c16",
97    sig,                                -- 4 bytes: ProtocolId
98    structureSize,                      -- 2 bytes: StructureSize. Must be 64.
99    (overrides['CreditCharge'] or 0),   -- 2 bytes: CreditCharge.
100    (overrides['Status'] or 0),         -- 4 bytes: (ChannelSequence/Reserved)/Status.
101    command,                            -- 2 bytes: Command.
102    (overrides['CreditR'] or 0),        -- 2 bytes: CreditRequest/CreditResponse.
103    (overrides['Flags'] or flags),      -- 4 bytes: Flags. TODO
104    (overrides['NextCommand'] or 0),    -- 4 bytes: NextCommand.
105    (overrides['MessageId'] or smb['MessageId'] or 0),  -- 8 bytes: MessageId.
106    (overrides['Reserved'] or 0),              -- 4 bytes: Reserved.
107    (overrides['TreeId'] or smb['TreeId'] or 0),        -- 4 bytes: TreeId.
108    (overrides['SessionId'] or smb['SessionId'] or 0),  -- 8 bytes: SessionId.
109    (overrides['Signature'] or '1234567890123456')     -- 16 bytes: Signature.
110    )
111
112  return header
113end
114
115---
116-- Sends a SMB2 packet
117-- @param smb        The SMB object associated with the connection
118-- @param header     The header encoded with <code>smb_encode_sync_header</code>.
119-- @param data       The data.
120-- @param overrides  Overrides table.
121-- @return Boolean Status.
122-- @return An error message if status is false.
123---
124function smb2_send(smb, header, data, overrides)
125  overrides = overrides or {}
126  local body               = header .. data
127  local attempts           = 5
128  local status, err
129
130  local out = string.pack(">s4", body)
131  repeat
132    attempts = attempts - 1
133    stdnse.debug3("SMB: Sending SMB packet (len: %d, attempts remaining: %d)", #out, attempts)
134    status, err = smb['socket']:send(out)
135  until(status or (attempts == 0))
136
137  if(attempts == 0) then
138    stdnse.debug1("SMB: Sending packet failed after 5 tries! Giving up.")
139  end
140
141  return status, err
142end
143
144---
145-- Reads the next SMB2 packet from the socket, and parses it into the header and data.
146-- Netbios handling based taken from smb.lua.
147--
148-- @param smb The SMB object associated with the connection
149-- @param read_data [optional] Return data section. Set to false if you only need the header. Default: true
150-- @return (status, header, data) If status is true, the header,
151--         and data are all the raw arrays of bytes.
152--         If status is false, header contains an error message and data is undefined.
153---
154function smb2_read(smb, read_data)
155  stdnse.debug3("SMB2: Receiving SMB2 packet")
156
157  -- Receive the response -- we make sure to receive at least 4 bytes, the length of the NetBIOS length
158  smb['socket']:set_timeout(TIMEOUT)
159
160  -- attempt to read the Netbios header
161  local status, netbios_data = smb['socket']:receive_buf(match.numbytes(4), true);
162
163  -- Make sure the connection is still alive
164  if not status then
165    return false, "SMB2: Failed to receive bytes: " .. netbios_data
166  end
167
168  -- The length of the packet is 4 bytes of big endian (for our purposes).
169  -- The NetBIOS header is 24 bits, big endian
170  local netbios_length, pos = string.unpack(">I4", netbios_data)
171  -- Make the length 24 bits
172  netbios_length = netbios_length & 0x00FFFFFF
173
174  -- The total length is the netbios_length, plus 4 (for the length itself)
175  local length = netbios_length + 4
176
177  local status, smb_data = smb['socket']:receive_buf(match.numbytes(netbios_length), true)
178
179  -- Make sure the connection is still alive
180  if not status then
181    return false, "SMB2: Failed to receive bytes after 5 attempts: " .. smb_data
182  end
183
184  local result = netbios_data .. smb_data
185  if(#result ~= length) then
186    stdnse.debug1("SMB2: ERROR: Received wrong number of bytes, there will likely be issues (received %d, expected %d)", #result, length)
187    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)
188  end
189
190  -- The header is 64 bytes.
191  if (pos + 64 > #result) then
192    stdnse.debug2("SMB2: SMB2 packet too small. Size needed to be at least '%d' but we got '%d' bytes", pos+64, #result)
193    return false, "SMB2: ERROR: Header packet too small."
194  end
195  local header, pos = string.unpack("<c64", result, pos)
196
197  -- Read the data section or skip it if read_data is false.
198  local data
199  if(read_data == nil or read_data == true) then
200    data = result:sub(pos)
201  end
202
203  stdnse.debug3("SMB2: smb2_read() received %d bytes", #result)
204  return true, header, data
205end
206
207---
208-- Sends SMB2_COM_NEGOTIATE command for a SMB2/SMB3 connection.
209-- This function works for dialects 2.02, 2.10, 3.0, 3.02 and 3.11.
210--
211-- Packet structure: https://msdn.microsoft.com/en-us/library/cc246543.aspx
212--
213-- @param smb The associated SMB connection object.
214-- @param overrides Overrides table.
215-- @return (status, dialect) If status is true, the negotiated dialect is returned as the second value.
216--                            Otherwise if status is false, the error message is returned.
217function negotiate_v2(smb, overrides)
218  local StructureSize = 36 -- Must be set to 36.
219  local DialectCount
220  if overrides['Dialects'] then
221    DialectCount = #overrides['Dialects']
222  else
223    DialectCount = 1
224  end
225  -- The client MUST set SecurityMode bit to 0x01 if the SMB2_NEGOTIATE_SIGNING_REQUIRED bit is not set,
226  -- and MUST NOT set this bit if the SMB2_NEGOTIATE_SIGNING_REQUIRED bit is set.
227  -- The server MUST ignore this bit.
228  local SecurityMode = overrides["SecurityMode"] or smb2_values['SMB2_NEGOTIATE_SIGNING_ENABLED']
229  local Capabilities = overrides["Capabilities"] or 0 -- SMB 3.x dialect requires capabilities to be constructed
230  local GUID = overrides["GUID"] or "1234567890123456"
231  local ClientStartTime = overrides["ClientStartTime"] or 0 -- ClientStartTime only used in dialects > 3.11
232  local total_data = 0  -- Data counter
233  local padding_data = "" -- Padding string to align contexts
234  local context_data -- Holds Context data
235  local is_0311 = false -- Flag for SMB 3.11
236  local status, err
237
238  if not( overrides['Dialects'] ) then -- Set 2.02 as default dialect if user didn't select one
239    overrides['Dialects'] = {0x0202}
240  end
241
242  local header = smb2_encode_header_sync(smb, command_codes['SMB2_COM_NEGOTIATE'], overrides)
243
244  -- We construct the first block that works for dialects 2.02 up to 3.11.
245  local data = string.pack("<I2 I2 I2 I2 I4 c16",
246    StructureSize,  -- 2 bytes: StructureSize
247    DialectCount,   -- 2 bytes: DialectCount
248    SecurityMode,   -- 2 bytes: SecurityMode
249    0,              -- 2 bytes: Reserved - Must be 0
250    Capabilities,   -- 4 bytes: Capabilities - 0 for dialects > 3.x
251    GUID            -- 16 bytes: ClientGuid
252  )
253
254  -- The next block gets interpreted in different ways depending on the dialect
255  if tableaux.contains(overrides['Dialects'], 0x0311) then
256    is_0311 = true
257  end
258
259  -- If we are dealing with 3.11 we need to set the following fields:
260  -- NegotiateContextOffset, NegotiateContextCount, and Reserved2
261  if is_0311 then
262    total_data = #header + #data + (DialectCount*2)
263    padding_data = string.rep("\0", (8 - total_data % 8) % 8)
264    total_data = total_data + #padding_data
265    data = data .. string.pack("<I4 I2 I2",
266                    total_data+8,   -- NegotiateContextOffset (4 bytes)
267                    0x2,            -- NegotiateContextCount (2 bytes)
268                    0x0             -- Reserved2 (2 bytes)
269                   )
270  else  -- If it's not 3.11, the bytes are the ClientStartTime (8 bytes)
271    data = data .. string.pack("<I8", ClientStartTime) -- If it is not 3.11, we set it to 0
272  end -- if is_0311
273
274  -- Now we build the Dialect list, 16 bit integers
275  if(overrides['Dialects'] == nil) then  -- If no custom dialect is defined, used the default 2.10
276    data = data .. string.pack("<I2", 0x0210)
277  else  -- Dialects are set in overrides table
278    for _, v in ipairs(overrides['Dialects']) do
279      data = data .. string.pack("<I2", v)
280    end
281  end
282
283  -- If 3.11, we now need to add some padding between the dialects and the NegotiateContextList
284  -- I was only able to get this to work using both NegotiateContexts:
285  -- * SMB2_PREAUTH_INTEGRITY_CAPABILITIES
286  -- * SMB2_ENCRYPTION_CAPABILITIES
287  if is_0311 then
288    data = data .. padding_data
289    local negotiate_context_list, context_data
290
291    -- We set SMB2_ENCRYPTION_CAPABILITIES first
292    context_data = string.pack("<I2 I2 I2",
293                    0x2,      -- CipherCount (2 bytes): 2 ciphers available
294                    0x0002,   -- Ciphers (2 bytes each): AES-128-GCM
295                    0x0001    -- Ciphers (2 bytes each): AES-128-CCM
296                  )
297    data = data .. string.pack("<I2 I2 I4",
298                    smb2_values['SMB2_ENCRYPTION_CAPABILITIES'],-- ContextType (2 bytes)
299                    #context_data,                              -- DataLength (2 bytes)
300                    0x0                                         -- Reserved (4 bytes)
301                  ) .. context_data                             -- Data (SMB2_ENCRYPTION_CAPABILITIES)
302
303    -- We now add SMB2_PREAUTH_INTEGRITY_CAPABILITIES
304    -- We add the padding between contexts so they are 8 byte aligned
305    total_data = #header + #data
306    padding_data = string.rep("\0", (8 - total_data % 8) % 8)
307    data = data .. padding_data
308    context_data = context_data .. string.pack("<I2 I2 I2 I16 I16",
309                                    0x1,  -- HashAlgorithmCount (2 bytes)
310                                    0x20, -- SaltLength (2 bytes)
311                                    0x0001,  -- HashAlgorithms (2 bytes each): SHA-512
312                                    0x0,      -- Salt
313                                    0x1       -- Salt
314    )
315    data = data .. string.pack("<I2 I2 I4",
316                    smb2_values['SMB2_PREAUTH_INTEGRITY_CAPABILITIES'], -- ContextType (2 bytes)
317                    #context_data,                                      -- DataLength (2 bytes)
318                    0x0                                                -- Reserved (4 bytes)
319    ) .. context_data
320
321  end
322
323  status, err = smb2_send(smb, header, data)
324  if not status then
325    return false, err
326  end
327  status, header, data = smb2_read(smb)
328
329  local protocol_version, structure_size, credit_charge, status = string.unpack("<c4 I2 I2 I4", header)
330  -- Get the protocol version
331  if(protocol_version ~= ("\xFESMB") or structure_size ~= 64) then
332    return false, "SMB: Server returned an invalid SMBv2 packet"
333  end
334  stdnse.debug2("SMB2_COM_NEGOTIATE returned status '%s'", status)
335
336  if status ~= 0 then
337    stdnse.debug2("SMB2_COM_NEGOTIATE command failed: Dialect not supported.")
338    return false, "SMB2: Dialect is not supported. Exiting."
339  end
340
341  local parameters_format = "<I2 I2 I2 I2 c16 I4 I4 I4 I4 I8 I8"
342  if #data < string.packsize(parameters_format) then
343    -- smb.lua can tolerate missing time/timezone, but it's less likely any
344    -- SMB2 implementations will not have them. If this becomes a problem, we
345    -- can shorten this unpack format like in smb.negotiate_v1
346    return false, "SMB2: ERROR: Server returned less data than it was supposed to (one or more fields are missing)"
347  end
348
349  local data_structure_size, security_mode, negotiate_context_count
350  data_structure_size, smb['security_mode'], smb['dialect'],
351    negotiate_context_count, smb['server_guid'], smb['capabilities'],
352    smb['max_trans'], smb['max_read'], smb['max_write'], smb['time'],
353    smb['start_time'] = string.unpack(parameters_format, data)
354
355  if(data_structure_size ~= 65) then
356    return false, string.format("Server returned an unknown structure size in SMB2 NEGOTIATE response")
357  end
358
359  -- Convert the time and timezone to human readable values (taken from smb.lua)
360  smb['time'] = (smb['time'] // 10000000) - 11644473600
361  smb['date'] = datetime.format_timestamp(smb['time'])
362
363  -- Samba does not report the boot time
364  if smb['start_time'] ~= 0 then
365    smb['start_time'] = (smb['start_time'] // 10000000) - 11644473600
366    smb['start_date'] = datetime.format_timestamp(smb['start_time'])
367  else
368    smb['start_date'] = "N/A"
369  end
370
371  local security_buffer_offset, security_buffer_length, neg_context_offset
372  security_buffer_offset, security_buffer_length, neg_context_offset = string.unpack("<I2 I2 I4", data)
373  if status == 0 then
374    return true, overrides['Dialects']
375  else
376    return false, string.format("Status error code:%s",status)
377  end
378end
379
380return _ENV;
381