diff --git a/CHANGELOG b/CHANGELOG
index 25e9ec575..c78a27faa 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,5 +1,12 @@
# Nmap Changelog ($Id$); -*-text-*-
+o Complete re-write of the marshalling logic for Microsoft RPC calls.
+ [Ron Bowes]
+
+o Added vulnerability checks for MS08-067 as well as an unfixed
+ denial of service in the Windows 2000 registry service.
+ [Ron Bowes]
+
o Zenmap now runs ndiff to do its "Compare Results" function. This
completely replaces the old diff view. ndiff is now required to do
comparisons in Zenmap. [David]
diff --git a/nselib/msrpc.lua b/nselib/msrpc.lua
index 23fe656e4..ac314b596 100644
--- a/nselib/msrpc.lua
+++ b/nselib/msrpc.lua
@@ -1,31 +1,40 @@
---- Call various MSRPC functions.
---
--- This library gives support for calling various MSRPC functions, making heavy
--- use of the smb library.
--- The functions used here can be accessed over TCP ports 445 and 139,
+--- By making heavy use of the 'smb' library, this library will call various MSRPC
+-- functions. The functions used here can be accessed over TCP ports 445 and 139,
-- with an established session. A NULL session (the default) will work for some
-- functions and operating systems (or configurations), but not for others.
--
-- To make use of these function calls, a SMB session with the server has to be
-- established. This can be done manually with the smb library, or the function
--- start_smb can be called.
+-- start_smb can be called. A session has to be created, then the IPC$
+-- tree opened.
--
--- Next, the interface has to be bound. The bind function will take care of that.
+-- Next, the interface has to be bound. The bind() function will take care of that.
--
-- After that, you're free to call any function that's part of that interface. In
-- other words, if you bind to the SAMR interface, you can only call the samr_
--- functions, for lsa_ functions, bind to the LSA interface, etc.
---
--- Although functions can be called in any order, many functions depend on the
+-- functions, for lsa_ functions, bind to the LSA interface, etc. Although functions
+-- can technically be called in any order, many functions depend on the
-- value returned by other functions. I indicate those in the function comments,
--- so keep an eye out.
+-- so keep an eye out. SAMR functions, for example, require a call to
+-- connect4.
--
-- Something to note is that these functions, for the most part, return a whole ton
--- of stuff in a table. I basically wrote them to return every possible value
--- they get their hands on. I don't expect that most of them will be used, and I'm
--- not going to document them all in the function header; rather, I will document
--- the elements in the table that are more useful, you'll have to look at the actual
--- code (or the table) to see what else is available.
+-- of stuff in a table; basically, everything that is returned by the function.
+-- Generally, if you want to know exactly what you have access to, either display the
+-- returned data with a print_table-type function, or check the documentation (Samba 4.0's
+-- .idl files (in samba_4.0/source/librpc/idl; see below for link) are what I based
+-- the names on).
+--
+-- The parameters for each function are converted to a string of bytes in a process
+-- called "marshalling". Functions here take arguments that match what a user would
+-- logically want to send. These are marshalled by using functions in the
+-- msrpctypes module. Those functions require a table of values that
+-- isn't very use friendly; as such, it's generated, when possible, in the functions
+-- in this module. The value returned, on the other hand, is returned directly to the
+-- user; I don't want to limit what data they can use, and it's difficult to rely on
+-- servers to format it consistently (sometimes a null is returned, and
+-- other times an empty array or blank string), so I put the onus on the scripts to
+-- deal with the returned values.
--
-- When implementing this, I used Wireshark's output significantly, as well as Samba's
-- "idl" files for reference:
@@ -35,12 +44,13 @@
-- remove references to Samba.
--
--@author Ron Bowes
---@copyright Same as Nmap--See http://nmap.org/book/man-legal.html
+--@copyright See nmap's COPYING for licence
-----------------------------------------------------------------------
module(... or "msrpc", package.seeall)
require 'bit'
require 'bin'
+require 'msrpctypes'
require 'netbios'
require 'smb'
require 'stdnse'
@@ -71,176 +81,13 @@ TRANSFER_SYNTAX = string.char(0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11, 0x
-- The 'referent_id' value is ignored, as far as I can tell, so this value is passed for it. No, it isn't random. :)
REFERENT_ID = 0x50414d4e
---- Convert a string to fake unicode (ascii with null characters between them), optionally add a null terminator,
--- and optionally align it to 4-byte boundaries. This is frequently used in MSRPC calls, so I put it here, but
--- it might be a good idea to move this function (and the converse one below) into a separate library.
---
---@param string The string to convert.
---@param do_null [optional] Add a null-terminator to the unicode string. Default false.
---@param do_align [optional] Align the string to a multiple of 4 bytes. Default false.
---@return The unicode version of the string.
-local function string_to_unicode(string, do_null, do_align)
- local i
- local result = ""
-
- if(do_null == nil) then
- do_null = false
- end
- if(do_align == nil) then
- do_align = false
- end
-
- -- Loop through the string, adding each character followed by a char(0)
- for i = 1, string.len(string), 1 do
- result = result .. string.sub(string, i, i) .. string.char(0)
- end
-
- -- Add a null, if the caller requestd it
- if(do_null == true) then
- result = result .. string.char(0) .. string.char(0)
- end
-
- -- Align it to a multiple of 4, if necessary
- if(do_align) then
- if(string.len(result) % 4 ~= 0) then
- result = result .. string.char(0) .. string.char(0)
- end
- end
-
- return result
-end
-
---- Read a unicode string from a buffer, similar to how bin.unpack would, optionally eat the null terminator,
--- and optionally align it to 4-byte boundaries.
---
---@param buffer The buffer to read from, typically the full 'arguments' value for MSRPC
---@param pos The position in the buffer to start (just like bin.unpack)
---@param length The number of ascii characters that will be read (including the null, if do_null is set).
---@param do_null [optional] Remove a null terminator from the string as the last character. Default false.
---@param do_align [optional] Ensure that the number of bytes removed is a multiple of 4.
---@return (pos, string) The new position and the string read, again imitating bin.unpack. If there was an
--- attempt to read off the end of the string, then 'nil' is returned for both parameters.
-local function unicode_to_string(buffer, pos, length, do_null, do_align)
- local i, ch, dummy
- local string = ""
-
- stdnse.print_debug(3, "MSRPC: Entering unicode_to_string(pos = %d, length = %d)", pos, length)
-
- if(do_null == nil) then
- do_null = false
- end
- if(do_align == nil) then
- do_align = false
- end
-
- if(do_null == true) then
- length = length - 1
- end
-
- for j = 1, length, 1 do
-
- pos, ch, dummy = bin.unpack("--).
---
---@param sid A SID object.
---@return A string representing the SID.
-function sid_to_string(sid)
- local i
- local str
-
- local authority = bit.bor(bit.lshift(sid['authority_high'], 32), sid['authority'])
-
- str = string.format("S-%u-%u", sid['revision'], sid['authority'])
-
- for i = 1, sid['count'], 1 do
- str = str .. string.format("-%u", sid['subauthorities'][i])
- end
-
- return str
-end
-
----Convert a SID string in the standard representation to the equivalent table.
---
---This code is sensible, but a little obtuse -- I'm open to suggestions on how to improve it.
---
---@param sid A SID string
---@return A table representing the SID
-function string_to_sid(sid)
- local i
- local pos, pos_next
- local result = {}
-
-
- if(string.find(sid, "^S-") == nil) then
- return nil
- end
- if(string.find(sid, "-%d+$") == nil) then
- return nil
- end
-
- pos = 3
-
- pos_next = string.find(sid, "-", pos)
- result['revision'] = string.sub(sid, pos, pos_next - 1)
-
- pos = pos_next + 1
- pos_next = string.find(sid, "-", pos)
- result['authority_high'] = bit.rshift(string.sub(sid, pos, pos_next - 1), 32)
- result['authority'] = bit.band(string.sub(sid, pos, pos_next - 1), 0xFFFFFFFF)
-
- result['subauthorities'] = {}
- i = 1
- repeat
- pos = pos_next + 1
- pos_next = string.find(sid, "-", pos)
- if(pos_next == nil) then
- result['subauthorities'][i] = string.sub(sid, pos)
- else
- result['subauthorities'][i] = string.sub(sid, pos, pos_next - 1)
- end
- i = i + 1
- until pos_next == nil
- result['count'] = i - 1
-
- return result
-end
-
-
--- This is a wrapper around the SMB class, designed to get SMB going quickly for MSRPC calls. This will
-- connect to the SMB server, negotiate the protocol, open a session, connect to the IPC$ share, and
-- open the named pipe given by 'path'. When this successfully returns, the 'smbstate' table can be immediately
-- used for MSRPC (the bind function should be called right after).
--
-- Note that the smbstate table is the same one used in the SMB files (obviously), so it will contain
--- the various responses/information places in there by SMB functions.
+-- the various results/information places in there by SMB functions.
--
--@param host The host object.
--@param path The path to the named pipe; for example, msrpc.SAMR_PATH or msrpc.SRVSVC_PATH.
@@ -291,7 +138,7 @@ end
--- A wrapper around the smb.stop function. I only created it to add symmetry, so client code
-- doesn't have to call both msrpc and smb functions.
--
---@param state The SMB state table.
+--@param smbstate The SMB state table.
function stop_smb(state)
smb.stop(state)
end
@@ -315,7 +162,7 @@ function bind(smbstate, interface_uuid, interface_version, transfer_syntax)
local status, result
local parameters, data
local pos, align
- local response = {}
+ local result
stdnse.print_debug(2, "MSRPC: Sending Bind() request")
@@ -358,60 +205,60 @@ function bind(smbstate, interface_uuid, interface_version, transfer_syntax)
return false, result
end
- stdnse.print_debug(3, "MSRPC: Received Bind() response")
+ stdnse.print_debug(3, "MSRPC: Received Bind() result")
-- Make these easier to access.
parameters = result['parameters']
data = result['data']
-- Extract the first part from the resposne
- pos, response['version_major'], response['version_minor'], response['packet_type'], response['packet_flags'], response['data_representation'], response['frag_length'], response['auth_length'], response['call_id'] = bin.unpack("IIBind response. Pull out some more parameters.
- pos, response['max_transmit_frag'], response['max_receive_frag'], response['assoc_group'], response['secondary_address_length'] = bin.unpack("SSIS", data, pos)
+ -- If we made it this far, then we have a valid Bind() result. Pull out some more parameters.
+ pos, result['max_transmit_frag'], result['max_receive_frag'], result['assoc_group'], result['secondary_address_length'] = bin.unpack("SSIS", data, pos)
-- Read the secondary address
- pos, response['secondary_address'] = bin.unpack(string.format("SMB_COM_TRANSACTION packet, and parses the response. Once the SMB stuff has been stripped off the response, it's
+-- out a SMB_COM_TRANSACTION packet, and parses the result. Once the SMB stuff has been stripped off the result, it's
-- passed down here, cleaned up some more, and returned to the caller.
--
-- There's a reason that SMB is sometimes considered to be between layer 4 and 7 on the OSI model. :)
@@ -434,7 +281,7 @@ local function call_function(smbstate, opnum, arguments)
local status, result
local parameters, data
local pos, align
- local response = {}
+ local result
data = bin.pack("IIImsrpctypes function that converts a ShareType to an english string.
+-- I implemented this as a proxy so scripts don't have to make direct calls to msrpctypes
+-- functions.
+--
+--@param val The value to convert.
+--@return A string that can be displayed to the user.
+function srvsvc_ShareType_tostr(val)
+ return msrpctypes.srvsvc_ShareType_tostr(val)
+end
+
---Call the MSRPC function netshareenumall on the remote system. This function basically returns a list of all the shares
-- on the system.
--
@@ -513,37 +370,23 @@ function srvsvc_netshareenumall(smbstate, server)
local level
local ctr, referent, count, max_count
- local response = {}
-
stdnse.print_debug(2, "MSRPC: Calling NetShareEnumAll() [%s]", smbstate['ip'])
- server = "\\\\" .. server
-- [in] [string,charset(UTF16)] uint16 *server_unc
- arguments = bin.pack("netsharegetinfo on the remote system. This function retrieves extra information about a share
@@ -619,29 +442,14 @@ function srvsvc_netsharegetinfo(smbstate, server, share, level)
local arguments
local pos, align
- local response = {}
-
- server = "\\\\" .. server
-
-- [in] [string,charset(UTF16)] uint16 *server_unc,
- arguments = bin.pack(""
- end
-
-
- pos, ptr_comment = bin.unpack("NetSessEnum function, which gets a list of active sessions on the host. For this function,
-- a session is defined as a connection to a file share.
--
@@ -797,42 +501,29 @@ function srvsvc_netsessenum(smbstate, server)
local arguments
local pos, align
- local response = {}
-
stdnse.print_debug(2, "MSRPC: Calling NetSessEnum() [%s]", smbstate['ip'])
-- [in] [string,charset(UTF16)] uint16 *server_unc,
- arguments = bin.pack("NetServerGetStatistics function, which grabs a bunch of statistics on the server.
@@ -935,7 +597,7 @@ end
-- * 'syserrors' The number of server system errors.
-- * 'bytessent' The number of server bytes sent to the network.
-- * 'bytesrcvd' The number of server bytes received from the network.
--- * 'avresponse' The average server response time (in milliseconds).
+-- * 'avresult' The average server result time (in milliseconds).
-- * 'reqbufneed' The number of times the server required a request buffer but failed to allocate one. This value indicates that the server parameters may need adjustment.
-- * 'bigbufneed' The number of times the server required a big buffer but failed to allocate one. This value indicates that the server parameters may need adjustment.
function srvsvc_netservergetstatistics(smbstate, server)
@@ -944,34 +606,21 @@ function srvsvc_netservergetstatistics(smbstate, server)
local arguments
local pos, align
- local response = {}
-
local service = "SERVICE_SERVER"
stdnse.print_debug(2, "MSRPC: Calling NetServerGetStatistics() [%s]", smbstate['ip'])
-- [in] [string,charset(UTF16)] uint16 *server_unc,
- arguments = bin.pack("msrpctypes function that converts a PasswordProperties to an english string.
+-- I implemented this as a proxy so scripts don't have to make direct calls to msrpctypes
+-- functions.
+--
+--@param val The value to convert.
+--@return A string that can be displayed to the user.
+function samr_PasswordProperties_tostr(val)
+ return msrpctypes.samr_PasswordProperties_tostr(val)
+end
+
+---A proxy to a msrpctypes function that converts a AcctFlags to an english string.
+-- I implemented this as a proxy so scripts don't have to make direct calls to msrpctypes
+-- functions.
+--
+--@param val The value to convert.
+--@return A string that can be displayed to the user.
+function samr_AcctFlags_tostr(val)
+ return msrpctypes.samr_AcctFlags_tostr(val)
+end
+
---Call the connect4 function, to obtain a "connect handle". This must be done before calling many
-- of the SAMR functions.
--
@@ -1216,26 +764,16 @@ function samr_connect4(smbstate, server)
local arguments
local pos, align
- local response = {}
-
stdnse.print_debug(2, "MSRPC: Calling Connect4() [%s]", smbstate['ip'])
- server = "\\\\" .. server
-
-- [in,string,charset(UTF16)] uint16 *system_name,
- arguments = bin.pack("enumdomains function, which returns a list of all domains in use by the system.
--
---@param smbstate The SMB state table.
---@param connect_handle The connect_handle, returned by samr_connect4.
+--@param smbstate The SMB state table
+--@param connect_handle The connect_handle, returned by samr_connect4()
--@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values, the most
-- useful one being 'domains', which is a list of the domains.
function samr_enumdomains(smbstate, connect_handle)
@@ -1278,17 +818,17 @@ function samr_enumdomains(smbstate, connect_handle)
local result
local pos, align
- local response = {}
-
stdnse.print_debug(2, "MSRPC: Calling EnumDomains() [%s]", smbstate['ip'])
-- [in,ref] policy_handle *connect_handle,
- arguments = bin.pack("LookupDomain function, which converts a domain's name into its sid, which is
@@ -1359,23 +881,14 @@ function samr_lookupdomain(smbstate, connect_handle, domain)
local pos, align
local referent_id
- local response = {}
-
stdnse.print_debug(2, "MSRPC: Calling LookupDomain(%s) [%s]", domain, smbstate['ip'])
-- [in,ref] policy_handle *connect_handle,
- arguments = bin.pack("SI<", arguments)
- response['sid']['subauthorities'] = {}
- for i = 1, response['sid']['count'], 1 do
- pos, response['sid']['subauthorities'][i] = bin.unpack("OpenDomain, which returns a handle to the domain identified by the given sid.
@@ -1427,22 +934,16 @@ function samr_opendomain(smbstate, connect_handle, sid)
local arguments
local pos, align
- local response = {}
-
- stdnse.print_debug(2, "MSRPC: Calling OpenDomain(%s) [%s]", sid_to_string(sid), smbstate['ip'])
+ stdnse.print_debug(2, "MSRPC: Calling OpenDomain(%s) [%s]", sid, smbstate['ip'])
-- [in,ref] policy_handle *connect_handle,
- arguments = bin.pack("SI<", sid['count'], sid['revision'], sid['count'], sid['authority_high'], sid['authority'])
- for i = 1, sid['count'], 1 do
- arguments = arguments .. bin.pack("EnumDomainUsers, which returns a list of users only. To get more information about the users, the
--- QueryDisplayInfo function can be used.
+-- QueryDisplayInfo() function can be used.
--
--@param smbstate The SMB state table
--@param domain_handle The domain_handle, returned by samr_opendomain
@@ -1487,21 +990,19 @@ function samr_enumdomainusers(smbstate, domain_handle)
local arguments
local pos, align
- local response = {}
-
stdnse.print_debug(2, "MSRPC: Calling EnumDomainUsers() [%s]", smbstate['ip'])
-- [in,ref] policy_handle *domain_handle,
- arguments = bin.pack("index = 0, and increment until you stop getting
+-- an error indicator in result['return'].
--
--@param smbstate The SMB state table
--@param domain_handle The domain handle, returned by samr_opendomain
+--@param index The index of the user to check; the first user is 0, next is 1, etc.
--@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values, the most
-- useful ones being 'names', a list of all the usernames, and 'details', a further list of tables with the elements
-- 'name', 'fullname', and 'description' (note that any of them can be nil if the server didn't return a value). Finally,
-- 'flags' is the numeric flags for the user, while 'flags_list' is an array of strings, representing the flags.
-function samr_querydisplayinfo(smbstate, domain_handle)
+function samr_querydisplayinfo(smbstate, domain_handle, index)
local i, j
local status, result
local arguments
local pos, align
- local function_return
-
- local response = {}
- response['names'] = {}
- response['details'] = {}
-- This loop is because, in my testing, if I asked for all the results at once, it would blow up (ERR_BUFFER_OVERFLOW). So, instead,
-- I put a little loop here and grab the names individually.
- i = 0
- repeat
- stdnse.print_debug(2, "MSRPC: Calling QueryDisplayInfo(%d) [%s]", i, smbstate['ip'])
+ stdnse.print_debug(2, "MSRPC: Calling QueryDisplayInfo(%d) [%s]", index, smbstate['ip'])
-- [in,ref] policy_handle *domain_handle,
- arguments = bin.pack("QueryDomainInfo2, which grabs various data about a domain.
@@ -1721,7 +1135,7 @@ end
--@param domain_handle The domain_handle, returned by samr_opendomain
--@param level The level, which determines which type of information to query for. See the @return section
-- for details.
---@param response [optional] A 'result' to add the entries to. This lets us call this function multiple times,
+--@param result [optional] A 'result' to add the entries to. This lets us call this function multiple times,
-- for multiple levels, and keep the results in one place.
--@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values,
-- and the values that are returned are dependent on the 'level' settings:
@@ -1739,23 +1153,19 @@ end
-- 'lockout_duration' (in minutes)
-- 'lockout_window' (in minutes)
-- 'lockout_threshold' (in attempts)
-function samr_querydomaininfo2(smbstate, domain_handle, level, response)
+function samr_querydomaininfo2(smbstate, domain_handle, level)
local i, j
local status, result
local arguments
local pos, align
- if(response == nil) then
- response = {}
- end
-
stdnse.print_debug(2, "MSRPC: Calling QueryDomainInfo2(%d) [%s]", level, smbstate['ip'])
-- [in,ref] policy_handle *domain_handle,
- arguments = bin.pack("close function, which closes a handle of any type (for example, domain_handle or connect_handle)
@@ -1844,13 +1210,12 @@ function samr_close(smbstate, handle)
local status, result
local arguments
local pos, align
- local response = {}
stdnse.print_debug(2, "MSRPC: Calling Close() [%s]", smbstate['ip'])
-- [in,out,ref] policy_handle *handle
- arguments = bin.pack("LsarOpenPolicy2 function, to obtain a "policy handle". This must be done before calling many
@@ -1890,36 +1256,16 @@ function lsa_openpolicy2(smbstate, server)
local arguments
local pos, align
- local response = {}
-
stdnse.print_debug(2, "MSRPC: Calling LsarOpenPolicy2() [%s]", smbstate['ip'])
-- [in,unique] [string,charset(UTF16)] uint16 *system_name,
-
- arguments = bin.pack("LsarLookupNames2 function, to convert the server's name into a sid.
@@ -1965,58 +1314,33 @@ function lsa_lookupnames2(smbstate, policy_handle, names)
local result
local pos, align
- local response = {}
-
stdnse.print_debug(2, "MSRPC: Calling LsarLookupNames2(%s) [%s]", stdnse.strjoin(", ", names), smbstate['ip'])
-- [in] policy_handle *handle,
- arguments = bin.pack("SI<", arguments, pos)
- sid['subauthorities'] = {}
- for i = 1, sid['count'], 1 do
- pos, sid['subauthorities'][i] = bin.unpack("LsarLookupSids2 function, to convert a list of SIDs to their names
--
--@param smbstate The SMB state table
--@param policy_handle The policy handle returned by lsa_openpolicy2
---@param sid The SID object for the server
---@param rids The RIDs of users to look up
+--@param sids The SIDs to look up (will probably be the server's SID with "-[rid]" appended
--@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values.
--- The element 'domains' is identical to the lookupnames2 element called 'domains'. The element 'names' is a
+-- The element 'domains' is identical to the lookupnames2() element called 'domains'. The element 'names' is a
-- list of strings, for the usernames (not necessary a 1:1 mapping with the RIDs), and the element 'details' is
-- a table containing more information about each name, even if the name wasn't found (this one is a 1:1 mapping
-- with the RIDs).
-function lsa_lookupsids2(smbstate, policy_handle, sid, rids)
+function lsa_lookupsids2(smbstate, policy_handle, sids)
local i, j
local status, result
local arguments
local result
local pos, align
- local response = {}
-
- stdnse.print_debug(2, "MSRPC: Calling LsarLookupSids2(%s, %s) [%s]", sid_to_string(sid), stdnse.strjoin(", ", rids), smbstate['ip'])
+ stdnse.print_debug(2, "MSRPC: Calling LsarLookupSids2(%s) [%s]", stdnse.strjoin(", ", sids), smbstate['ip'])
-- [in] policy_handle *handle,
- arguments = bin.pack("SI<", sid['count'] + 1, sid['revision'], sid['count'] + 1, sid['authority_high'], sid['authority'])
- for j = 1, sid['count'], 1 do
- arguments = arguments .. bin.pack("SI<", arguments, pos)
- sid['subauthorities'] = {}
- for i = 1, sid['count'], 1 do
- pos, sid['subauthorities'][i] = bin.unpack("", "Alias", "Well known group", "Deleted account", "", "Not found" }
-
- pos, count, referent_id = bin.unpack("msrpctypes function that converts a SidType to an english string.
+-- I implemented this as a proxy so scripts don't have to make direct calls to msrpctypes
+-- functions.
+--
+--@param val The value to convert.
+--@return A string that can be displayed to the user.
+function lsa_SidType_tostr(val)
+ return msrpctypes.lsa_SidType_tostr(val)
+end
+
+
---Call the OpenHKU function, to obtain a handle to the HKEY_USERS hive
--
--@param smbstate The SMB state table
@@ -2367,14 +1532,14 @@ function winreg_openhku(smbstate)
local arguments
local pos, align
- local response = {}
-
stdnse.print_debug(2, "MSRPC: Calling OpenHKU() [%s]", smbstate['ip'])
-- [in] uint16 *system_name,
- arguments = bin.pack("OpenHKCU function, to obtain a handle to the HKEY_CURRENT_USER hive
+--
+--@param smbstate The SMB state table
+--@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values, the most
+-- useful one being 'handle', which is required to call other winreg functions.
+function winreg_openhkcu(smbstate)
+ local i, j
+ local status, result
+ local arguments
+ local pos, align
+
+ stdnse.print_debug(2, "MSRPC: Calling OpenHKCU() [%s]", smbstate['ip'])
+
+-- [in] uint16 *system_name,
+ arguments = msrpctypes.marshall_int16_ptr(0x1337, true)
+
+-- [in] winreg_AccessMask access_mask,
+ arguments = arguments .. msrpctypes.marshall_winreg_AccessMask('MAXIMUM_ALLOWED_ACCESS')
+
+-- [out,ref] policy_handle *handle
+
+ -- Do the call
+ status, result = call_function(smbstate, 0x01, arguments)
+ if(status ~= true) then
+ return false, result
+ end
+
+ stdnse.print_debug(3, "MSRPC: OpenHKCU() returned successfully")
+
+ -- Make arguments easier to use
+ arguments = result['arguments']
+ pos = 1
+
+-- [in] uint16 *system_name,
+-- [in] winreg_AccessMask access_mask,
+-- [out,ref] policy_handle *handle
+ pos, result['handle'] = msrpctypes.unmarshall_policy_handle(arguments, pos)
+
+ pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos)
+ if(result['return'] == nil) then
+ return false, "Read off the end of the packet (winreg.openhkcu)"
+ end
+ if(result['return'] ~= 0) then
+ return false, smb.get_status_name(result['return']) .. " (winreg.openhkcu)"
+ end
+
+ return true, result
+
+end
+
+
+
---Calls the Windows registry function EnumKey, which returns a single key
-- under the given handle, at the index of 'index'.
--
@@ -2457,41 +1680,34 @@ end
--@param handle A handle to hive or key. winreg_openhku provides a useable key, for example.
--@param index The index of the key to return. Generally you'll start at 0 and increment until
-- an error is returned.
+--@param name The name buffer. This should be set to the empty string; however, setting to 'nil' can have
+-- interesting effects on Windows 2000 (I experienced crashes).
--@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values, the most
-- useful one being 'name', which is the name of the current key
-function winreg_enumkey(smbstate, handle, index)
+function winreg_enumkey(smbstate, handle, index, name)
local i, j
local status, result
local arguments
local pos, align
- local response = {}
-
stdnse.print_debug(2, "MSRPC: Calling EnumKey(%d) [%s]", index, smbstate['ip'])
-- [in,ref] policy_handle *handle,
- arguments = bin.pack(" 0) then
- pos, response['name'] = unicode_to_string(arguments, pos, response['length'], true, true)
- if(pos == nil) then
- return false, "Read off the end of the packet (winreg.enumkey)"
- end
- else
- response['name'] = "" -- Not sure why the name could have a 0 length, but who knows?
- end
- end
+ pos, result['name'] = msrpctypes.unmarshall_winreg_StringBuf(arguments, pos)
-- [in,out,unique] winreg_StringBuf *keyclass,
- pos, referent_id = bin.unpack("QueryInfoKey, which obtains information about an opened key.
@@ -2642,15 +1820,13 @@ function winreg_queryinfokey(smbstate, handle)
local arguments
local pos, align
- local response = {}
-
stdnse.print_debug(2, "MSRPC: Calling QueryInfoKey() [%s]", smbstate['ip'])
-- [in,ref] policy_handle *handle,
- arguments = bin.pack("<20A", handle)
+ arguments = msrpctypes.marshall_policy_handle(handle)
-- [in,out,ref] winreg_String *classname,
- arguments = arguments .. bin.pack("marshall_int32, marshall_int16, etc. will marshall the base types, and unmarshall_int32,
+-- unmarshall_int16, etc. will unmarshall them.
+--
+-- Strings are a little bit tricker. A string is preceded by three 32-bit values: the max length, the offset, and
+-- the length. Additionally, strings may or may not be null terminated, depending on where they're being used. For
+-- more information on strings, see the comments on marshall_unicode. The functions marshall_unicode
+-- and unmarshall_unicode can be used to mashall/unmarshall strings.
+--
+-- Pointers also have interesting properties. A pointer is preceeded by a 4-byte value called (at least by Wireshark)
+-- the "referent id". For a valid pointer, this can be anything except 0 (I use 'NMAP' for it). If it's '0', then
+-- it's a null pointer and the data doesn't actually follow. To help clarify, a pointer to the integer '4' could be
+-- marshalled as the hex string 78 56 34 12 04 00 00 00 (the referent_id is 0x12345678 and the integer
+-- itself is 0x00000004). If the integer is nil, then it's marshalled as 00 00 00 00, which is simply
+-- a referent_id of 0.
+--
+-- From the perspective of the program, pointers can be marshalled by using the "_ptr" versions of normal functions
+-- (for example, marshall_int32_ptr and unmarshall_unicode_ptr. From the perspective
+-- of functions within this module, especially functions for marshalling structs and arrays, the marshall_ptr
+-- and unmarshall_ptr functions should be used. These can marshall any data type; the marshalling function
+-- is passed as a parameter.
+--
+-- So far, this is fairly straight forward. Arrays are where everything falls apart.
+--
+-- An array of basic types is simply the types themselves, preceeded by the "max length" of the array (which can be
+-- longer than the actual length). When pointers are used in an array, however, things get hairy. The 'referent_id's
+-- of the pointers are all put at the start of the array, along with the base types. Then, the data is put at the
+-- end of the array, for all the referent_ids that aren't null. Let's say you have four strings, "abc", "def", null, and
+-- "jkl", in an array. The array would look like this:
+--
+-- 0x00200000 (referent_id for "abc")
+-- 0x00400000 (referent_id for "def")
+-- 0x00000000 (null referent_id)
+-- 0x00800000 (referent_id for "jkl")
+-- "abc" (note that this also has the standard string stuff, the max_length, offset, and actual_length)
+-- "def"
+-- "ghi"
+--
+--
+-- If you mix in a base type, it goes at the front along with the referent_ids. So, let's say you have a structure
+-- that contains two integers and a string. You have an array of these. It would encode like this:
+--
+-- 0x00200000 (referent_id for the string in the first struct)
+-- 0x00000001 (first integer in the first struct)
+-- 0x00000002 (second integer in the first struct)
+-- 0x00400000 (referent_id for the string in the second struct)
+-- 0x00000003 (first integer in the second struct)
+-- 0x00000004 (second integer in the second struct)
+-- "string1" (contains max_length, offset, and actual_length)
+-- "string2"
+--
+--
+-- From the perspective of the program, arrays shouldn't need to be marshalled/unmarshalled, this is tricky and should be
+-- left up to functions within this module. Functions within this module should use marshall_array and
+-- unmarshall_array to interact with arrays. These take callback functions for the datatype being stored
+-- in the array; these callback functions have to be in a particular format, so care should be taken when writing them.
+-- In particular, the first parameter has to be 'location', which is used to separate the header (the part with the
+-- referent_ids) and the body (the part with the pointer data). These are explained more thoroughly in the function headers.
+--
+-- Structs are handled the same as arrays -- the referent_ids and base types go at the top, and the values being pointed to
+-- go at the bottom. An array of struct, as has already been shown, will have all the base types and referent_ids for all the
+-- members at the top, and all the values for all the pointers at the bottom.
+--
+-- Structs tend to be custom functions. Sometimes, these functions are passed as the callback to marshall_ptr or
+-- marshall_array (and the equivalent unmarshall_ functions). This means that the custom struct
+-- functions have to be able to split themselves into the base types and the pointer data automatically. For an example, see
+-- the functions that have already been written.
+--
+-- In the case where you need to unmarshall the same struct from both an array and a pointer, there's an issue; they require
+-- different prototypes. There's really no way to directly fix this, at least, none that I could come up with, so I write
+-- a function called unmarshall_struct. unmarshall_struct basically calls a struct unmarshalling
+-- function the same way unmarshall_array would. This is a bit of a kludge, but it's the best I could come up
+-- with.
+--
+-- There are different sections in here, which correspond to "families" of types. I modelled these after Samba's .idl files.
+-- MISC corresponds to misc.idl, LSA to lsa.idl, etc. Each of these sections has possible dependencies; for example, SAMR
+-- functions use LSA strings, and everything uses SECURITY and MISC. So the order is important -- dependencies have to go
+-- above the module.
+--
+-- The datatypes used here are modelled after the datatypes used by Microsoft's functions. Each function that represents
+-- a struct will have the struct definition in its comment; and that struct (or the closest representation to it) will be
+-- returned. Often, this requires scripts to access something like result['names']['names'][0]['name'], which is
+-- rather unwieldy, but I decided that following Microsoft's definitions was the most usable way for many reasons. I find
+-- the best way to figure out how to work a function is to call a print_table()-style function on the result and look at
+-- how the response is laid out.
+--
+-- Many datatypes are automatically encoded when sent and decoded when received to make life easier for developers. Some
+-- examples are:
+-- * All absolute time values will be seconds from 1970
+-- * All relative time values will be in seconds (this includes the hyper datatype); when possible, the
+-- milliseconds/microseconds (as far down as we have access to) will be preserved as a decimal
+-- * All enumerations will be a string representing the constant (which can be converted to a user-readable string using
+-- one of the _tostr functions); what that means is, enumeration values are never used, only the names
+-- * SIDs will be converted to user-readable strings in the standard format (S-x-y-...)
+-- * GUIDs are stored as tables of values; however, I might change this to a string representation at some point
+
+module(... or "msrpctypes", package.seeall)
+
+require 'bit'
+require 'bin'
+require 'stdnse'
+
+local REFERENT_ID = 0x50414d4e
+local HEAD = 'HEAD'
+local BODY = 'BODY'
+local ALL = 'ALL'
+
+--- Convert a string to fake unicode (ascii with null characters between them), optionally add a null terminator,
+-- and optionally align it to 4-byte boundaries. This is frequently used in MSRPC calls, so I put it here, but
+-- it might be a good idea to move this function (and the converse one below) into a separate library.
+--
+--@param string The string to convert.
+--@param do_null [optional] Add a null-terminator to the unicode string. Default false.
+--@return The unicode version of the string.
+function string_to_unicode(string, do_null)
+ local i
+ local result = ""
+
+ stdnse.print_debug(4, string.format("MSRPC: Entering string_to_unicode(string = %s)", string))
+
+ if(do_null == nil) then
+ do_null = false
+ end
+
+ -- Loop through the string, adding each character followed by a char(0)
+ for i = 1, string.len(string), 1 do
+ result = result .. string.sub(string, i, i) .. string.char(0)
+ end
+
+ -- Add a null, if the caller requestd it
+ if(do_null == true) then
+ result = result .. string.char(0) .. string.char(0)
+ end
+
+ -- Align it to a multiple of 4, if necessary
+ if(string.len(result) % 4 ~= 0) then
+ result = result .. string.char(0) .. string.char(0)
+ end
+
+ stdnse.print_debug(4, "MSRPC: Leaving string_to_unicode()")
+
+ return result
+end
+
+--- Read a unicode string from a buffer, similar to how bin.unpack would, optionally eat the null terminator,
+-- and optionally align it to 4-byte boundaries.
+--
+--@param buffer The buffer to read from, typically the full 'arguments' value for MSRPC
+--@param pos The position in the buffer to start (just like bin.unpack)
+--@param length The number of ascii characters that will be read (including the null, if do_null is set).
+--@param do_null [optional] Remove a null terminator from the string as the last character. Default false.
+--@return (pos, string) The new position and the string read, again imitating bin.unpack. If there was an
+-- attempt to read off the end of the string, then 'nil' is returned for both parameters.
+function unicode_to_string(buffer, pos, length, do_null)
+ local i, j, ch, dummy
+ local string = ""
+
+ stdnse.print_debug(4, string.format("MSRPC: Entering unicode_to_string(pos = %d, length = %d)", pos, length))
+
+ if(do_null == nil) then
+ do_null = false
+ end
+
+ if(do_null == true and length > 0) then
+ length = length - 1
+ end
+
+ for j = 1, length, 1 do
+
+ pos, ch, dummy = bin.unpack("func is called, which is passed as
+-- a parameter, with the arguments args. This function has to return a marshalled
+-- parameter, but other than that it can be any marshalling function. The 'value' parameter
+-- simply determined whether or not it's a null pointer, and will probably be a repease of
+-- one of the arguments.
+--
+-- Note that the function func doesn't have to conform to any special prototype,
+-- as long as the args array matches what the function wants.
+--
+-- This can be used to marshall an int16 value of 0x1234 with padding like this:
+--
+-- marshall_ptr(ALL, marshall_int16, {0x1234, true}, 0x1234)
+--
+--
+-- And here's how a 'nil' string might be marshalled:
+--
+-- local str = nil
+-- marshall_ptr(ALL, marshall_unicode, {str, true}, str)
+--
+--
+--@param location The part of the pointer wanted, either HEAD (for the referent_id), BODY
+-- (for the pointer data), or ALL (for both together). Generally, unless the
+-- referent_id is split from the data (for example, in an array), you will want
+-- ALL.
+--@param func The function to call when encoding the body. Should convert the arguments passed
+-- in the args parameter to a string.
+--@param args An array of arguments that will be directly passed to the function func
+--@param value The value that's actually being encoded. This is simply used to determine whether or
+-- not the pointer is null.
+--@return A string representing the marshalled data.
+local function marshall_ptr(location, func, args, value)
+ local result = ""
+
+ stdnse.print_debug(4, string.format("MSRPC: Entering marshall_ptr(location = %s)", location))
+
+ -- If we're marshalling the HEAD section, add a REFERENT_ID.
+ if(location == HEAD or location == ALL) then
+ if(func == nil or args == nil or value == nil) then
+ result = result .. bin.pack("result
+-- parameter, it is the result from the first time this is called.
+--
+-- The function func has to conform to this format:
+--
+-- func(data, pos, )
+--
+--
+--@param location The part of the pointer being processed, either HEAD (for the referent_id), BODY
+-- (for the pointer data), or ALL (for both together). Generally, unless the
+-- referent_id is split from the data (for example, in an array), you will want
+-- ALL.
+--@param data The data packet being processed.
+--@param pos The position within data
+--@param func The function that's used to process the body data (only called if it isn't a null
+-- pointer). This function has to conform to a specific prototype, see above.
+--@param args The arguments that'll be passed to the function func, after the data
+-- array and the position.
+--@param result This is required when unmarshalling the BODY section, which always comes after
+-- unmarshalling the HEAD. It is the result returned for this parameter during the
+-- HEAD unmarshall. If the referent_id was '0', then this function doesn't unmarshall
+-- anything.
+--@return (pos, result) The new position along with the result. For HEAD the result is either true
+-- for valid pointers or false for null pointers. For BODY or ALL, the result is
+-- nil for null pointers, or the data for valid pointers.
+local function unmarshall_ptr(location, data, pos, func, args, result)
+ stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_ptr()"))
+ if(args == nil) then
+ args = {}
+ end
+ -- If we're unmarshalling the header, then pull off a referent_id.
+ if(location == HEAD or location == ALL) then
+ local referent_id
+ pos, referent_id = bin.unpack("marshall_ptr, except that this marshalls a type that isn't a pointer.
+-- It also understands pointers, in the sense that it'll only return data in the HEAD section, since
+-- basetypes are printed in the HEAD and not the BODY.
+--
+-- Using this isn't strictly necessary, but it cleans up functions for generating structs containing
+-- both pointers and basetypes (see marshall_srvsvc_NetShareInfo2).
+--
+-- Like marshall_ptr, the function doesn't have to match any prototype, as long as the
+-- proper arguments are passed to it.
+--
+--@param location The part of the pointer wanted, either HEAD (for the data itself), BODY
+-- (for nothing, since this isn't a pointer), or ALL (for the data). Generally, unless the
+-- referent_id is split from the data (for example, in an array), you will want
+-- ALL.
+--@param func The function to call when encoding the body. Should convert the arguments passed
+-- in the args parameter to a string.
+--@param args An array of arguments that will be directly passed to the function func
+--@return A string representing the marshalled data.
+local function marshall_basetype(location, func, args)
+ local result
+ stdnse.print_debug(4, string.format("MSRPC: Entering marshall_basetype()"))
+
+ if(location == HEAD or location == ALL) then
+ result = bin.pack("
+-- func(location, )
+--
+-- where "location" is the standard HEAD/BODY/ALL location used throughout the functions.
+--
+--@param array An array of tables. Each table contains 'func', a pointer to the marshalling
+-- function and 'args', the arguments to pass to the marshalling function after the
+-- 'location' variable.
+--@return A string representing the marshalled data.
+function marshall_array(array)
+ local i
+ local result = ""
+
+ stdnse.print_debug(4, string.format("MSRPC: Entering marshall_array()"))
+
+ -- The max count is always at the front of the array (at least, in my tests). It is possible that
+ -- this won't always hold true, so if you're having an issue that you've traced back to this function,
+ -- you might want to double-check my assumption.
+ result = result .. bin.pack("func has to conform to a very specific prototype:
+--
+-- func(location, data, pos, result, )
+--
+-- Where location is the standard HEAD/BODY location, data and pos
+-- are the packet and position within it, result is the result from the HEAD section (if
+-- it's nil, it isn't used), and args are arbitrary arguments passed to it.
+--
+-- I made the call to pass the same arguments to each function when it's called. This is, for example,
+-- whether or not to null-terminate a string, or whether or not to pad an int16. If different types are
+-- required, you're probably out of luck.
+--
+--@param data The data packet being processed.
+--@param pos The position within data.
+--@param count The number of elements in the array.
+--@param func The function to call to unmarshall each parameter. Has to match a specific prototype;
+-- see the function comment.
+--@param args Arbitrary arguments to pass to the function.
+--@return (pos, result) The new position and the result of unmarshalling this value.
+local function unmarshall_array(data, pos, count, func, args)
+ local i
+ local size
+ local result = {}
+
+ stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_array()"))
+
+ if(args == nil) then
+ args = {}
+ end
+
+ pos, max_count = bin.unpack("unmarshall_array. This allows the same
+-- struct to be used in unmarshall_array and in unmarshall_ptr. It is kind
+-- of a kludge, but it makes sense, and was the cleanest solution I could come up with to this problem
+-- (although I'm sure that there's a better one staring me in the face).
+--
+-- The func parameter, obviously, has to match the same prototype as strings being passed to
+-- unmarshall_array, which is:
+--
+-- func(location, data, pos, result, )
+--
+--
+--@param data The data packet being processed.
+--@param pos The position within data.
+--@param func The function to call to unmarshall each parameter. Has to match a specific prototype;
+-- see the function comment.
+--@param args Arbitrary arguments to pass to the function.
+--@return (pos, result) The new position and the result of unmarshalling this value.
+local function unmarshall_struct(data, pos, func, args)
+ local result
+
+ stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_struct()"))
+
+ if(args == nil) then
+ args = {}
+ end
+
+ pos, result = func(ALL, data, pos, nil, args)
+
+ stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_struct()"))
+
+ return pos, result
+end
+
+-------------------------------------
+-- BASE TYPES
+-- (dependencies: n/a)
+-------------------------------------
+
+--- Marshall a string that is in the format:
+-- [string,charset(UTF16)] uint16 *str
+--
+-- This has the max size of the buffer, the offset (I'm not sure what the offset does, I've
+-- never seen it used), the actual size, and the string itself. This will always align to
+-- the 4-byte boundary.
+--
+--@param str The string to insert. Cannot be nil.
+--@param do_null [optional] Appends a null to the end of the string. Default false.
+--@param max_length [optional] Sets a max length that's different than the string's length. Length
+-- is in characters, not bytes.
+--@return A string representing the marshalled data.
+function marshall_unicode(str, do_null, max_length)
+ local buffer_length
+ local result
+
+ stdnse.print_debug(4, string.format("MSRPC: Entering marshall_unicode()"))
+
+ if(do_null == nil) then
+ do_null = false
+ end
+
+ if(do_null) then
+ buffer_length = string.len(str) + 1
+ else
+ buffer_length = string.len(str)
+ end
+
+ if(max_length == nil) then
+ max_length = buffer_length
+ end
+
+ result = bin.pack("[string,charset(UTF16)] uint16 *str
+--
+-- See marshall_unicode for more information.
+--
+--@param data The data buffer.
+--@param pos The position in the data buffer.
+--@param do_null [optional] Discards the final character, the string terminator. Default false.
+--
+--@return (pos, str) The new position, and the string. The string may be nil.
+function unmarshall_unicode(data, pos, do_null)
+ local ptr, str
+ local max, offset, actual
+
+ stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_unicode()"))
+
+ if(do_null == nil) then
+ do_null = false
+ end
+
+ pos, max, offset, actual = bin.unpack("data.
+--@param do_null [optional] Assumes a null is at the end of the string. Default false.
+--@return (pos, result) The new position and the string.
+function unmarshall_unicode_ptr(data, pos, do_null)
+ local result
+
+ stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_unicode_ptr()"))
+ pos, result = unmarshall_ptr(ALL, data, pos, unmarshall_unicode, {do_null})
+ stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_unicode_ptr()"))
+
+ return pos, result
+end
+
+--- Marshall an int64. This is simply an 8-byte integer inserted into the buffer, nothing fancy.
+--@param int64 The integer to insert
+--@return A string representing the marshalled data.
+function marshall_int64(int64)
+ local result
+
+ stdnse.print_debug(4, string.format("MSRPC: Entering marshall_int64()"))
+ result = bin.pack(" [in] uint32 var
+--
+-- This is simply an integer inserted into the buffer, nothing fancy.
+--@param int32 The integer to insert
+--@return A string representing the marshalled data.
+function marshall_int32(int32)
+ local result
+
+ stdnse.print_debug(4, string.format("MSRPC: Entering marshall_int32()"))
+ result = bin.pack(" [in] uint16 var
+--
+-- This is simply an integer inserted into the buffer, nothing fancy.
+--@param int16 The integer to insert
+--@param pad [optional] If set, will align the insert on 4-byte boundaries. Default: true.
+--@return A string representing the marshalled data.
+function marshall_int16(int16, pad)
+ local result
+
+ stdnse.print_debug(4, string.format("MSRPC: Entering marshall_int16()"))
+
+ if(pad == false) then
+ return bin.pack(" [in] uint8 var
+--
+-- This is simply an integer inserted into the buffer, nothing fancy.
+--
+--@param int8 The integer to insert
+--@param pad [optional] If set, will align the insert on 4-byte boundaries. Default: true.
+--@return A string representing the marshalled data.
+function marshall_int8(int8, pad)
+ local result
+
+ stdnse.print_debug(4, string.format("MSRPC: Entering marshall_int8()"))
+
+ if(pad == false) then
+ return bin.pack("marshall_int64 for more information.
+--
+--@param data The data packet being processed.
+--@param pos The position within data.
+--@return (pos, int64) The new position, and the value.
+function unmarshall_int64(data, pos)
+ local value
+
+ stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_int64()"))
+ pos, value = bin.unpack("marshall_int32 for more information.
+--
+--@param data The data packet being processed.
+--@param pos The position within data.
+--@return (pos, int32) The new position, and the value.
+function unmarshall_int32(data, pos)
+ local value
+
+ stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_int32()"))
+ pos, value = bin.unpack("marshall_int16 for more information.
+--
+--@param data The data packet.
+--@param pos The position within the data.
+--@param pad [optional] If set, will remove extra bytes to align the packet, Default: true
+--@return (pos, int16) The new position, and the value.
+function unmarshall_int16(data, pos, pad)
+ local value
+
+ stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_int16()"))
+
+ pos, value = bin.unpack("marshall_int8 for more information.
+--
+--@param data The data packet.
+--@param pos The position within the data.
+--@param pad [optional] If set, will remove extra bytes to align the packet, Default: true
+--@return (pos, int8) The new position, and the value.
+function unmarshall_int8(data, pos, pad)
+ local value
+
+ stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_int8()"))
+
+ pos, value = bin.unpack(" [in,out] uint32 *ptr
+-- If the pointer is null, it simply marshalls the integer '0'. Otherwise,
+-- it uses a referent id followed by the integer.
+--
+--@param int32 The value of the integer pointer
+--@return A string representing the marshalled data.
+function marshall_int32_ptr(int32)
+ local result
+
+ stdnse.print_debug(4, string.format("MSRPC: Entering marshall_int32_ptr()"))
+ result = marshall_ptr(ALL, marshall_int32, {int32}, int32)
+ stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_int32_ptr()"))
+
+ return result
+end
+
+--- Marshall a pointer to an int16, which has the following format:
+-- [in,out] uint16 *ptr
+-- If the pointer is null, it simply marshalls the integer '0'. Otherwise,
+-- it uses a referent id followed by the integer.
+--
+--@param int16 The value of the integer pointer
+--@param pad [optional] If set, will align the insert on 4-byte boundaries. Default: true.
+--@return A string representing the marshalled data.
+function marshall_int16_ptr(int16, pad)
+ local result
+
+ stdnse.print_debug(4, string.format("MSRPC: Entering marshall_int16_ptr()"))
+ result = marshall_ptr(ALL, marshal_int16, {int16, pad}, int16)
+ stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_int16_ptr()"))
+
+ return result
+end
+
+--- Marshall a pointer to an int8, which has the following format:
+-- [in,out] uint8 *ptr
+-- If the pointer is null, it simply marshalls the integer '0'. Otherwise,
+-- it uses a referent id followed by the integer.
+--
+--@param int8 The value of the integer pointer
+--@param pad [optional] If set, will align the insert on 4-byte boundaries. Default: true.
+--@return A string representing the marshalled data.
+function marshall_int8_ptr(int8, pad)
+ local result
+
+ stdnse.print_debug(4, string.format("MSRPC: Entering marshall_int8_ptr()"))
+ result = marshall_ptr(ALL, marshal_int8, {int8, pad}, int8)
+ stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_int8_ptr()"))
+
+ return result
+end
+
+--- Unmarshall a pointer to an int32. See marshall_int32_ptr for more information.
+--
+--@param data The data packet.
+--@param pos The position within the data.
+--@return (pos, int32) The new position, and the value.
+function unmarshall_int32_ptr(data, pos)
+ local result
+
+ stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_int32_ptr()"))
+ pos, result = unmarshall_ptr(ALL, data, pos, unmarshall_int32, {})
+ stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_int32_ptr()"))
+
+ return pos, result
+end
+
+--- Unmarshall a pointer to an int16. See marshall_int16_ptr for more information.
+--
+--@param data The data packet.
+--@param pos The position within the data.
+--@param pad [optional] If set, will remove extra bytes to align the packet, Default: true
+--@return (pos, int16) The new position, and the value.
+function unmarshall_int16_ptr(data, pos, pad)
+ local result
+
+ stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_int16_ptr()"))
+ pos, result = unmarshall_ptr(ALL, data, pos, unmarshall_int16, {pad})
+ stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_int16_ptr()"))
+
+ return pos, result
+end
+
+--- Unmarshall a pointer to an int8. See marshall_int8_ptr for more information.
+--
+--@param data The data packet.
+--@param pos The position within the data.
+--@param pad [optional] If set, will remove extra bytes to align the packet, Default: true
+--@return (pos, int8) The new position, and the value.
+function unmarshall_int8_ptr(data, pos, pad)
+ local result
+
+ stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_int8_ptr()"))
+ pos, result = unmarshall_ptr(ALL, data, pos, unmarshall_int8, {pad})
+ stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_int8_ptr()"))
+
+ return pos, result
+end
+
+--- Marshall an array of int8s, with an optional max_length set.
+--
+--@param data The array to marshall, as a string. Cannot be nil.
+--@param max_length [optional] The maximum length of the buffer. Default: the length of
+-- data.
+--@return A string representing the marshalled data.
+function marshall_int8_array(data, max_length)
+ local result = ""
+
+ stdnse.print_debug(4, string.format("MSRPC: Entering marshall_int8_array()"))
+
+ if(max_length == nil) then
+ max_length = #data
+ end
+
+ result = result .. bin.pack("data.
+--@return A string representing the marshalled data.
+function marshall_int8_array_ptr(data, max_length)
+ local result
+ stdnse.print_debug(4, string.format("MSRPC: Entering marshall_int8_array_ptr()"))
+
+ result = marshall_ptr(ALL, marshall_int8_array, {data, max_length}, data)
+
+ stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_int8_array_ptr()"))
+ return result
+end
+
+--- Unmarshall a pointer to an array of int8s.
+--
+--@param data The data packet.
+--@param pos The position within the data.
+--@return (pos, str) The position, and the resulting string, which cannot be nil.
+function unmarshall_int8_array_ptr(data, pos)
+ local str
+ stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_int8_array_ptr()"))
+
+ pos, str = unmarshall_ptr(ALL, data, pos, unmarshall_int8_array, {})
+
+ stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_int8_array_ptr()"))
+ return pos, str
+end
+
+---Marshalls an NTTIME. This is sent as the number of 1/10 microseconds since 1601; however
+-- the internal representation is the number of seconds since 1970. Because doing conversions
+-- in code is annoying, the user will never have to understand anything besides seconds since
+-- 1970.
+--
+--@param time The time, in seconds since 1970.
+--@return A string representing the marshalled data.
+function marshall_NTTIME(time)
+ local result
+ stdnse.print_debug(4, string.format("MSRPC: Entering marshall_NTTIME()"))
+
+ if(time == 0) then
+ result = bin.pack("marshall_NTTIME for more information.
+--
+--@param data The data packet.
+--@param pos The position within the data.
+--@return (pos, time) The new position, and the time in seconds since 1970.
+function unmarshall_NTTIME(data, pos)
+ local time
+ stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_NTTIME()"))
+
+ pos, time = bin.unpack("NTTIME*.
+--
+--@param data The data packet.
+--@param pos The position within the data.
+--@return (pos, time) The new position, and the time in seconds since 1970.
+function unmarshall_NTTIME_ptr(data, pos)
+ local time
+ stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_NTTIME_ptr()"))
+
+ pos, time = unmarshall_ptr(ALL, data, pos, unmarshall_NTTIME, {})
+
+ stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_NTTIME_ptr()"))
+ return pos, time
+end
+
+---Unmarshalls a hyper. I have no idea what a hyper is, just that it seems
+-- to be a 64-bit data type used for measuring time, and that the units happen to be negative
+-- microseconds. This function converts the value to seconds and returns it.
+--
+--@param data The data packet.
+--@param pos The position within the data.
+--@return (pos, val) The new position, and the result in seconds.
+function unmarshall_hyper(data, pos)
+ local result
+ stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_hyper()"))
+
+ pos, result = unmarshall_int64(data, pos)
+ result = result / -10000000
+
+ stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_hyper()"))
+ return pos, result
+end
+
+---Marshall an entry in a table. Basically, converts the string to a number based on the entries in
+-- table before sending. Multiple values can be ORed together (like flags) by separating
+-- them with pipes ("|").
+--
+--@param val The value to look up. Can be multiple values with pipes between, eg, "A|B|C".
+--@param table The table to use for lookups. The keys should be the names, and the values should be
+-- the numbers.
+--@return A string representing the marshalled data.
+local function marshall_Enum32(val, table)
+ local result = 0
+ stdnse.print_debug(4, string.format("MSRPC: Entering marshall_Enum32()"))
+
+ local vals = stdnse.strsplit("|", val)
+ local i
+
+ for i = 1, #vals, 1 do
+ result = bit.bor(result, table[vals[i]])
+ end
+
+ result = marshall_int32(result)
+
+ stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_Enum32()"))
+ return result
+end
+
+---Unmarshall an entry in a table. Basically, converts the next int32 in the buffer to a string
+-- based on the entries in table before returning.
+--
+--@param data The data packet.
+--@param pos The position within the data.
+--@param table The table to use for lookups. The keys should be the names, and the values should be
+-- the numbers.
+--@param default The default value to return if the lookup was unsuccessful.
+--@return (pos, policy_handle) The new position, and a table representing the policy_handle.
+local function unmarshall_Enum32(data, pos, table, default)
+ local i, v
+ stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_Enum32()"))
+
+ if(default == nil) then
+ default = ""
+ end
+
+ pos, val = unmarshall_int32(data, pos)
+
+ for i, v in pairs(table) do
+ if(v == val) then
+ return pos, i
+ end
+ end
+
+ stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_Enum32()"))
+ return pos, default
+end
+
+---Unmarshall an entry in a table. Basically, converts the next int16 in the buffer to a string
+-- based on the entries in table before returning.
+--
+--@param data The data packet.
+--@param pos The position within the data.
+--@param table The table to use for lookups. The keys should be the names, and the values should be
+-- the numbers.
+--@param default The default value to return if the lookup was unsuccessful.
+--@param pad [optional] If set, will ensure that we end up on an even multiple of 4. Default: true.
+--@return (pos, policy_handle) The new position, and a table representing the policy_handle.
+local function unmarshall_Enum16(data, pos, table, default, pad)
+ local i, v
+ stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_Enum16()"))
+
+ if(default == nil) then
+ default = ""
+ end
+
+ pos, val = unmarshall_int16(data, pos, pad)
+
+ for i, v in pairs(table) do
+ if(v == val) then
+ return pos, i
+ end
+ end
+
+ stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_Enum16()"))
+ return pos, default
+end
+
+---Similar to unmarshall_Enum32, except it'll return every value that could be ANDed together to
+-- create the resulting value (except a 0 value). This is effective for parsing flag data types.
+--@param data The data packet.
+--@param pos The position within the data.
+--@param table The table to use for lookups. The keys should be the names, and the values should be
+-- the numbers.
+local function unmarshall_Enum32_array(data, pos, table)
+ local array = {}
+ local i, v
+ local val
+ stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_Enum32_array()"))
+
+ pos, val = unmarshall_int32(data, pos)
+
+ for i, v in pairs(table) do
+ if(bit.band(v, val) ~= 0) then
+ array[#array + 1] = i
+ end
+ end
+
+ stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_Enum32_array()"))
+ return pos, array
+end
+
+
+
+-------------------------------------
+-- MISC
+-- (dependencies: n/a)
+-------------------------------------
+
+---Marshalls a GUID, which looks like this:
+--
+--
+-- typedef [public,noprint,gensize,noejs] struct {
+-- uint32 time_low;
+-- uint16 time_mid;
+-- uint16 time_hi_and_version;
+-- uint8 clock_seq[2];
+-- uint8 node[6];
+-- } GUID;
+--
+--
+--@param guid A table representing the GUID.
+--@return A string representing the marshalled data.
+local function marshall_guid(guid)
+ local result
+ stdnse.print_debug(4, string.format("MSRPC: Entering marshall_guid()"))
+
+ result = bin.pack("marshall_guid for the structure.
+--
+--@param data The data packet.
+--@param pos The position within the data.
+--@return (pos, result) The new position in data, and a table representing the datatype.
+local function unmarshall_guid(data, pos)
+ local guid = {}
+ stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_guid()"))
+
+ pos, guid['time_low'], guid['time_high'], guid['time_hi_and_version'], guid['clock_seq'], guid['node'] = bin.unpack("
+-- typedef struct {
+-- uint32 handle_type;
+-- GUID uuid;
+-- } policy_handle;
+--
+--
+--@param policy_handle The policy_handle to marshall.
+--@return A string representing the marshalled data.
+function marshall_policy_handle(policy_handle)
+ local result
+ stdnse.print_debug(4, string.format("MSRPC: Entering marshall_policy_handle()"))
+
+ result = bin.pack("marshall_policy_handle for the structure.
+--
+--@param data The data packet.
+--@param pos The position within the data.
+--@return (pos, result) The new position in data, and a table representing the datatype.
+function unmarshall_policy_handle(data, pos)
+ local policy_handle = {}
+ stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_policy_handle()"))
+
+ pos, policy_handle['handle_type'] = bin.unpack("
+-- typedef [public,gensize,noprint,noejs,nosize] struct {
+-- uint8 sid_rev_num; /**< SID revision number */
+-- [range(0,15)] int8 num_auths; /**< Number of sub-authorities */
+-- uint8 id_auth[6]; /**< Identifier Authority */
+-- uint32 sub_auths[num_auths];
+-- } dom_sid;
+--
+--
+--@param data The data packet being processed.
+--@param pos The position within data.
+--@return (pos, result) The new position in data, and a table representing the datatype.
+function unmarshall_dom_sid2(data, pos)
+ local i
+ stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_dom_sid2()"))
+
+ -- Read the SID from the packet
+ local sid = {}
+ pos, sid['count'] = bin.unpack("SI", data, pos)
+ sid['authority'] = bit.bor(bit.lshift(sid['authority_high'], 32), sid['authority_low'])
+
+ sid['sub_auths'] = {}
+ for i = 1, sid['num_auths'], 1 do
+ pos, sid['sub_auths'][i] = bin.unpack("dom_sid2 struct. See the unmarshall_dom_sid2 function
+-- for more information.
+--
+--@param data The data packet being processed.
+--@param pos The position within data.
+--@return (pos, result) The new position in data, and a table representing the datatype.
+function unmarshall_dom_sid2_ptr(data, pos)
+ local result
+ stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_dom_sid2_ptr()"))
+
+ pos, result = unmarshall_ptr(ALL, data, pos, unmarshall_dom_sid2, {})
+
+ stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_dom_sid2_ptr()"))
+ return pos, result
+end
+
+---Marshall a struct with the following definition:
+--
+--
+-- typedef [public,gensize,noprint,noejs,nosize] struct {
+-- uint8 sid_rev_num; /**< SID revision number */
+-- [range(0,15)] int8 num_auths; /**< Number of sub-authorities */
+-- uint8 id_auth[6]; /**< Identifier Authority */
+-- uint32 sub_auths[num_auths];
+-- } dom_sid;
+--
+--
+--@return A string representing the marshalled data.
+function marshall_dom_sid2(sid)
+ local i
+ local pos_next
+ local sid_array = {}
+ local result = ""
+ stdnse.print_debug(4, string.format("MSRPC: Entering marshall_dom_sid2()"))
+
+
+ if(string.find(sid, "^S-") == nil) then
+ stdnse.print_debug(1, "MSRPC: ERROR: Invalid SID encountered: %s\n", sid)
+ return nil
+ end
+ if(string.find(sid, "-%d+$") == nil) then
+ stdnse.print_debug(1, "MSRPC: ERROR: Invalid SID encountered: %s\n", sid)
+ return nil
+ end
+
+ pos = 3
+
+ pos_next = string.find(sid, "-", pos)
+ sid_array['sid_rev_num'] = string.sub(sid, pos, pos_next - 1)
+
+ pos = pos_next + 1
+ pos_next = string.find(sid, "-", pos)
+ sid_array['authority_high'] = bit.rshift(string.sub(sid, pos, pos_next - 1), 32)
+ sid_array['authority_low'] = bit.band(string.sub(sid, pos, pos_next - 1), 0xFFFFFFFF)
+
+ sid_array['sub_auths'] = {}
+ i = 1
+ repeat
+ pos = pos_next + 1
+ pos_next = string.find(sid, "-", pos)
+ if(pos_next == nil) then
+ sid_array['sub_auths'][i] = string.sub(sid, pos)
+ else
+ sid_array['sub_auths'][i] = string.sub(sid, pos, pos_next - 1)
+ end
+ i = i + 1
+ until pos_next == nil
+ sid_array['num_auths'] = i - 1
+
+ result = bin.pack("SI", sid_array['sid_rev_num'], sid_array['num_auths'], sid_array['authority_high'], sid_array['authority_low'])
+ for i = 1, sid_array['num_auths'], 1 do
+ result = result .. bin.pack("lsa_String is a buffer that holds a non-null-terminated string. It can have a max size that's different
+-- from its actual size. I tagged this one as "internal" because I don't want the user to have to provide
+-- a "location".
+--
+-- This is the format:
+--
+--
+-- typedef [public,noejs] struct {
+-- [value(2*strlen_m(string))] uint16 length;
+-- [value(2*strlen_m(string))] uint16 size;
+-- [charset(UTF16),size_is(size/2),length_is(length/2)] uint16 *string;
+-- } lsa_String;
+--
+--
+--@param location The part of the pointer wanted, either HEAD (for the referent_id), BODY
+-- (for the pointer data), or ALL (for both together). Generally, unless the
+-- referent_id is split from the data (for example, in an array), you will want
+-- ALL.
+--@param str The string to marshall
+--@param max_length [optional] The maximum size of the buffer, in characters, including the null terminator.
+-- Defaults to the length of the string, including the null.
+--@return A string representing the marshalled data.
+local function marshall_lsa_String_internal(location, str, max_length)
+ local length
+ local result = ""
+ stdnse.print_debug(4, string.format("MSRPC: Entering marshall_lsa_String_internal()"))
+
+ -- Handle default max lengths
+ if(max_length == nil) then
+ if(str == nil) then
+ max_length = 0
+ else
+ max_length = string.len(str)
+ end
+ end
+
+ if(str == nil) then
+ length = 0
+ else
+ length = string.len(str)
+ end
+
+ if(location == HEAD or location == ALL) then
+ result = result .. bin.pack("lsa_String value. See marshall_lsa_String_internal for more information.
+--
+--@param location The part of the pointer wanted, either HEAD (for the data itself), BODY
+-- (for nothing, since this isn't a pointer), or ALL (for the data). Generally, unless the
+-- referent_id is split from the data (for example, in an array), you will want
+-- ALL.
+--@param data The data packet.
+--@param pos The position within the data.
+--@param result This is required when unmarshalling the BODY section, which always comes after
+-- unmarshalling the HEAD. It is the result returned for this parameter during the
+-- HEAD unmarshall. If the referent_id was '0', then this function doesn't unmarshall
+-- anything.
+--@return (pos, str) The new position, and the unmarshalled string.
+local function unmarshall_lsa_String_internal(location, data, pos, result)
+ local length, size
+ local str
+ stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_lsa_String_internal()"))
+
+ if(location == HEAD or location == ALL) then
+ pos, length, size = bin.unpack("marshall_lsa_String_internal -- see that function on that for more information.
+-- This version doesn't require a location, so it's suitable to be a public function.
+--
+--@param str The string to marshall
+--@param max_length [optional] The maximum size of the buffer, in characters, including the null terminator.
+-- Defaults to the length of the string, including the null.
+--@return A string representing the marshalled data.
+function marshall_lsa_String(str, max_length)
+ local result
+ stdnse.print_debug(4, string.format("MSRPC: Entering marshall_lsa_String()"))
+
+ result = marshall_lsa_String_internal(ALL, str, max_length)
+
+ stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_lsa_String()"))
+ return result
+end
+
+---Marshall an array of lsa_String objects. This is a perfect demonstration of how to use
+-- marshall_array.
+--
+--@param strings The array of strings to marshall
+--@return A string representing the marshalled data.
+function marshall_lsa_String_array(strings)
+ local array = {}
+ local result
+ stdnse.print_debug(4, string.format("MSRPC: Entering marshall_lsa_String_array()"))
+
+ for i = 1, #strings, 1 do
+ array[i] = {}
+ array[i]['func'] = marshall_lsa_String_internal
+ array[i]['args'] = {strings[i]}
+ end
+
+ result = marshall_array(array)
+
+ stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_lsa_String_array()"))
+ return result
+end
+
+---Table of SID types.
+local lsa_SidType =
+{
+ SID_NAME_USE_NONE = 0, -- NOTUSED
+ SID_NAME_USER = 1, -- user
+ SID_NAME_DOM_GRP = 2, -- domain group
+ SID_NAME_DOMAIN = 3, -- domain: don't know what this is
+ SID_NAME_ALIAS = 4, -- local group
+ SID_NAME_WKN_GRP = 5, -- well-known group
+ SID_NAME_DELETED = 6, -- deleted account: needed for c2 rating
+ SID_NAME_INVALID = 7, -- invalid account
+ SID_NAME_UNKNOWN = 8, -- oops.
+ SID_NAME_COMPUTER = 9 -- machine
+}
+---String versions of SID types
+local lsa_SidType_str =
+{
+ SID_NAME_USE_NONE = "n/a",
+ SID_NAME_USER = "User",
+ SID_NAME_DOM_GRP = "Domain group",
+ SID_NAME_DOMAIN = "Domain",
+ SID_NAME_ALIAS = "Local group",
+ SID_NAME_WKN_GRP = "Well known group",
+ SID_NAME_DELETED = "Deleted account",
+ SID_NAME_INVALID = "Invalid account",
+ SID_NAME_UNKNOWN = "Unknown account",
+ SID_NAME_COMPUTER = "Machine"
+}
+---Marshall a lsa_SidType. This datatype is tied to the table above with that
+-- name.
+--
+--@param sid_type The value to marshall, as a string
+--@return The marshalled integer representing the given value, or nil if it wasn't
+-- found.
+function marshall_lsa_SidType(sid_type)
+ local result
+ stdnse.print_debug(4, string.format("MSRPC: Entering marshall_lsa_SidType()"))
+
+ result = marshall_Enum32(sid_type, lsa_SidType)
+
+ stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_lsa_SidType()"))
+ return result
+end
+
+---Unmarshall a lsa_SidType. This datatype is tied to the table with that name.
+--
+--@param data The data packet.
+--@param pos The position within the data.
+--@return (pos, str) The new position, and the string representing the datatype.
+function unmarshall_lsa_SidType(data, pos)
+ local str
+ stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_lsa_SidType()"))
+
+ pos, str = unmarshall_Enum16(data, pos, lsa_SidType)
+
+ stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_lsa_SidType()"))
+ return pos, str
+end
+
+---Convert a lsa_SidType value to a string that can be shown to the user. This is
+-- based on the _str table.
+--
+--@param val The string value (returned by the unmarshall_ function) to convert.
+--@return A string suitable for displaying to the user, or nil if it wasn't found.
+function lsa_SidType_tostr(val)
+ local result
+ stdnse.print_debug(4, string.format("MSRPC: Entering lsa_SidType_tostr()"))
+
+ result = lsa_SidType_str[val]
+
+ stdnse.print_debug(4, string.format("MSRPC: Leaving lsa_SidType_tostr()"))
+ return result
+end
+
+---LSA name levels.
+local lsa_LookupNamesLevel =
+{
+ LOOKUP_NAMES_ALL = 1,
+ LOOKUP_NAMES_DOMAINS_ONLY = 2,
+ LOOKUP_NAMES_PRIMARY_DOMAIN_ONLY = 3,
+ LOOKUP_NAMES_UPLEVEL_TRUSTS_ONLY = 4,
+ LOOKUP_NAMES_FOREST_TRUSTS_ONLY = 5,
+ LOOKUP_NAMES_UPLEVEL_TRUSTS_ONLY2 = 6
+}
+---LSA name level strings.
+local lsa_LookupNamesLevel_str =
+{
+ LOOKUP_NAMES_ALL = "All",
+ LOOKUP_NAMES_DOMAINS_ONLY = "Domains only",
+ LOOKUP_NAMES_PRIMARY_DOMAIN_ONLY = "Primary domains only",
+ LOOKUP_NAMES_UPLEVEL_TRUSTS_ONLY = "Uplevel trusted domains only",
+ LOOKUP_NAMES_FOREST_TRUSTS_ONLY = "Forest trusted domains only",
+ LOOKUP_NAMES_UPLEVEL_TRUSTS_ONLY2 = "Uplevel trusted domains only (2)"
+}
+---Marshall a lsa_LookupNamesLevel. This datatype is tied to the table above with that
+-- name.
+--
+--@param names_level The value to marshall, as a string
+--@return The marshalled integer representing the given value, or nil if it wasn't
+-- found.
+function marshall_lsa_LookupNamesLevel(names_level)
+ local result
+ stdnse.print_debug(4, string.format("MSRPC: Entering marshall_lsa_LookupNamesLevel()"))
+
+ result = marshall_Enum32(names_level, lsa_LookupNamesLevel)
+
+ stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_lsa_LookupNamesLevel()"))
+ return result
+end
+
+---Unmarshall a lsa_LookupNamesLevel. This datatype is tied to the table with that name.
+--
+--@param data The data packet.
+--@param pos The position within the data.
+--@return (pos, str) The new position, and the string representing the datatype.
+function unmarshall_lsa_LookupNamesLevel(data, pos)
+ local str
+ stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_lsa_LookupNamesLevel()"))
+
+ pos, str = unmarshall_Enum32(data, pos, lsa_LookupNamesLevel)
+
+ stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_lsa_LookupNamesLevel()"))
+ return pos, str
+end
+
+---Convert a lsa_LookupNamesLevel value to a string that can be shown to the user. This is
+-- based on the _str table.
+--
+--@param val The string value (returned by the unmarshall_ function) to convert.
+--@return A string suitable for displaying to the user, or nil if it wasn't found.
+function lsa_LookupNamesLevel_tostr(val)
+ local result
+ stdnse.print_debug(4, string.format("MSRPC: Entering lsa_LookupNamesLevel_tostr()"))
+
+ result = lsa_LookupNamesLevel_str[val]
+
+ stdnse.print_debug(4, string.format("MSRPC: Leaving lsa_LookupNamesLevel_tostr()"))
+ return result
+end
+
+---Marshall a struct with the following definition:
+--
+--
+-- typedef struct {
+-- lsa_SidType sid_type;
+-- uint32 rid;
+-- uint32 sid_index;
+-- uint32 unknown;
+-- } lsa_TranslatedSid2;
+--
+--
+--@param location The part of the pointer wanted, either HEAD (for the data itself), BODY
+-- (for nothing, since this isn't a pointer), or ALL (for the data). Generally, unless the
+-- referent_id is split from the data (for example, in an array), you will want
+-- ALL.
+--@param sid_type The sid_type value (I don't know what this means)
+--@param rid The rid (a number representing the user)
+--@param sid_index The sid_index value (I don't know what this means, either)
+--@param unknown An unknown value (is normaly 0).
+--@return A string representing the marshalled data.
+local function marshall_lsa_TranslatedSid2(location, sid_type, rid, sid_index, unknown)
+ local result = ""
+ stdnse.print_debug(4, string.format("MSRPC: Entering marshall_lsa_TranslatedSid2()"))
+
+ -- Set some default values
+ if(sid_type == nil) then sid_type = "SID_NAME_USE_NONE" end
+ if(rid == nil) then rid = 0 end
+ if(sid_index == nil) then sid_index = 0 end
+ if(unknown == nil) then unknown = 0 end
+
+ if(location == HEAD or location == ALL) then
+ result = result .. marshall_lsa_SidType(sid_type)
+ result = result .. marshall_int32(rid)
+ result = result .. marshall_int32(sid_index)
+ result = result .. marshall_int32(unknown)
+ end
+
+ if(location == BODY or location == ALL) then
+ end
+
+ stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_lsa_TranslatedSid2()"))
+ return result
+end
+
+---Unmarshall a struct with the following definition:
+--
+--
+-- typedef struct {
+-- lsa_SidType sid_type;
+-- uint32 rid;
+-- uint32 sid_index;
+-- uint32 unknown;
+-- } lsa_TranslatedSid2;
+--
+--
+--@param location The part of the pointer wanted, either HEAD (for the data itself), BODY
+-- (for nothing, since this isn't a pointer), or ALL (for the data). Generally, unless the
+-- referent_id is split from the data (for example, in an array), you will want
+-- ALL.
+--@param data The data packet being processed.
+--@param pos The position within data.
+--@param result This is required when unmarshalling the BODY section, which always comes after
+-- unmarshalling the HEAD. It is the result returned for this parameter during the
+-- HEAD unmarshall. If the referent_id was '0', then this function doesn't unmarshall
+-- anything.
+--@return (pos, result) The new position in data, and a table representing the datatype.
+local function unmarshall_lsa_TranslatedSid2(location, data, pos, result)
+ stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_lsa_TranslatedSid2()"))
+ if(result == nil) then
+ result = {}
+ end
+
+ if(location == HEAD or location == ALL) then
+ pos, result['sid_type'] = unmarshall_lsa_SidType(data, pos)
+ pos, result['rid'] = unmarshall_int32(data, pos)
+ pos, result['sid_index'] = unmarshall_int32(data, pos)
+ pos, result['unknown'] = unmarshall_int32(data, pos)
+ end
+
+
+ if(location == BODY or location == ALL) then
+ end
+
+ stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_lsa_TranslatedSid2()"))
+ return pos, result
+end
+
+---Marshall a struct with the following definition:
+--
+--
+-- typedef struct {
+-- lsa_SidType sid_type;
+-- lsa_String name;
+-- uint32 sid_index;
+-- uint32 unknown;
+-- } lsa_TranslatedName2;
+--
+--
+--@param location The part of the pointer wanted, either HEAD (for the data itself), BODY
+-- (for nothing, since this isn't a pointer), or ALL (for the data). Generally, unless the
+-- referent_id is split from the data (for example, in an array), you will want
+-- ALL.
+--@param sid_type The sid_type value, as a string
+--@param name The name of the user
+--@param sid_index The sid_index (I don't know what this is)
+--@param unknown An unknown value, normally 0
+--@return A string representing the marshalled data.
+local function marshall_lsa_TranslatedName2(location, sid_type, name, sid_index, unknown)
+ local result = ""
+ stdnse.print_debug(4, string.format("MSRPC: Entering marshall_lsa_TranslatedName2()"))
+
+ -- Set some default values
+ if(sid_type == nil) then sid_type = "SID_NAME_USE_NONE" end
+ if(name == nil) then name = "" end
+ if(sid_index == nil) then sid_index = 0 end
+ if(unknown == nil) then unknown = 0 end
+
+ if(location == HEAD or location == ALL) then
+ result = result .. marshall_lsa_SidType(sid_type)
+ result = result .. marshall_lsa_String_internal(HEAD, name)
+ result = result .. marshall_int32(sid_index)
+ result = result .. marshall_int32(unknown)
+ end
+
+ if(location == BODY or location == ALL) then
+ result = result .. marshall_lsa_String_internal(BODY, name)
+ end
+
+ stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_lsa_TranslatedName2()"))
+ return result
+end
+
+--@param location The part of the pointer wanted, either HEAD (for the data itself), BODY
+-- (for nothing, since this isn't a pointer), or ALL (for the data). Generally, unless the
+-- referent_id is split from the data (for example, in an array), you will want
+-- ALL.
+--@param data The data packet being processed.
+--@param pos The position within data.
+--@param result This is required when unmarshalling the BODY section, which always comes after
+-- unmarshalling the HEAD. It is the result returned for this parameter during the
+-- HEAD unmarshall. If the referent_id was '0', then this function doesn't unmarshall
+-- anything.
+--@return (pos, result) The new position in data, and a table representing the datatype.
+local function unmarshall_lsa_TranslatedName2(location, data, pos, result)
+ stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_lsa_TranslatedName2()"))
+ if(result == nil) then
+ result = {}
+ end
+
+ if(location == HEAD or location == ALL) then
+ pos, result['sid_type'] = unmarshall_lsa_SidType(data, pos)
+ pos, result['name'] = unmarshall_lsa_String_internal(HEAD, data, pos)
+ pos, result['sid_index'] = unmarshall_int32(data, pos)
+ pos, result['unknown'] = unmarshall_int32(data, pos)
+ end
+
+
+ if(location == BODY or location == ALL) then
+ pos, result['name'] = unmarshall_lsa_String_internal(BODY, data, pos, result['name'])
+ end
+
+ stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_lsa_TranslatedName2()"))
+ return pos, result
+end
+
+
+---Marshall a struct with the following definition:
+--
+--
+-- typedef struct {
+-- [range(0,1000)] uint32 count;
+-- [size_is(count)] lsa_TranslatedSid2 *sids;
+-- } lsa_TransSidArray2;
+--
+--
+--@param sids An array of SIDs to translate (as strings)
+--@return A string representing the marshalled data.
+function marshall_lsa_TransSidArray2(sids)
+ local result = ""
+ local array = {}
+ stdnse.print_debug(4, string.format("MSRPC: Entering marshall_lsa_TransSidArray2()"))
+
+ result = result .. marshall_int32(#sids)
+
+ for i = 1, #sids, 1 do
+ array[i] = {}
+ array[i]['func'] = marshall_lsa_TranslatedSid2
+ array[i]['args'] = {sids[i]['sid_type'], sids[i]['rid'], sids[i]['sid_index'], sids[i]['unknown']}
+ end
+
+ result = result .. marshall_ptr(ALL, marshall_array, {array}, array)
+
+ stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_lsa_TransSidArray2()"))
+ return result
+end
+
+---Marshall a struct with the following definition:
+--
+--
+-- typedef [public] struct {
+-- [value(2*strlen_m(string))] uint16 length;
+-- [value(2*(strlen_m(string)+1))] uint16 size;
+-- [charset(UTF16),size_is(size/2),length_is(length/2)] uint16 *string;
+-- } lsa_StringLarge;
+--
+--
+--@param location The part of the pointer wanted, either HEAD (for the data itself), BODY
+-- (for nothing, since this isn't a pointer), or ALL (for the data). Generally, unless the
+-- referent_id is split from the data (for example, in an array), you will want
+-- ALL.
+--@param data The data packet being processed.
+--@param pos The position within data.
+--@param result This is required when unmarshalling the BODY section, which always comes after
+-- unmarshalling the HEAD. It is the result returned for this parameter during the
+-- HEAD unmarshall. If the referent_id was '0', then this function doesn't unmarshall
+-- anything.
+--@return (pos, result) The new position in data, and the string value.
+local function unmarshall_lsa_StringLarge(location, data, pos, result)
+ local length, size
+ local str
+ stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_lsa_StringLarge()"))
+
+ if(location == HEAD or location == ALL) then
+ pos, length, size = bin.unpack("
+-- typedef struct {
+-- lsa_StringLarge name;
+-- dom_sid2 *sid;
+-- } lsa_DomainInfo;
+--
+--
+--@param location The part of the pointer wanted, either HEAD (for the data itself), BODY
+-- (for nothing, since this isn't a pointer), or ALL (for the data). Generally, unless the
+-- referent_id is split from the data (for example, in an array), you will want
+-- ALL.
+--@param data The data packet being processed.
+--@param pos The position within data.
+--@param result This is required when unmarshalling the BODY section, which always comes after
+-- unmarshalling the HEAD. It is the result returned for this parameter during the
+-- HEAD unmarshall. If the referent_id was '0', then this function doesn't unmarshall
+-- anything.
+--@return (pos, result) The new position in data, and a table representing the datatype.
+local function unmarshall_lsa_DomainInfo(location, data, pos, result)
+ stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_lsa_DomainInfo()"))
+ if(result == nil) then
+ result = {}
+ end
+
+ if(location == HEAD or location == ALL) then
+ pos, result['name'] = unmarshall_lsa_StringLarge(HEAD, data, pos)
+ pos, result['sid'] = unmarshall_ptr(HEAD, data, pos, unmarshall_dom_sid2)
+ end
+
+ if(location == BODY or location == ALL) then
+ pos, result['name'] = unmarshall_lsa_StringLarge(BODY, data, pos, result['name'])
+ pos, result['sid'] = unmarshall_ptr(BODY, data, pos, unmarshall_dom_sid2, {}, result['sid'])
+ end
+
+ stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_lsa_DomainInfo()"))
+ return pos, result
+end
+
+---Unmarshall a struct with the following definition:
+--
+--
+-- typedef struct {
+-- [range(0,1000)] uint32 count;
+-- [size_is(count)] lsa_DomainInfo *domains;
+-- uint32 max_size;
+-- } lsa_RefDomainList;
+--
+--
+--@param data The data packet being processed.
+--@param pos The position within data.
+--@return (pos, result) The new position in data, and a table representing the datatype.
+function unmarshall_lsa_RefDomainList(data, pos)
+ local result = {}
+ stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_lsa_RefDomainList()"))
+
+ -- Head
+ pos, result['count'] = unmarshall_int32(data, pos)
+ pos, result['domains'] = unmarshall_ptr(HEAD, data, pos, unmarshall_array, {result['count'], unmarshall_lsa_DomainInfo, {}})
+ pos, result['max_size'] = unmarshall_int32(data, pos)
+
+ -- Body
+ pos, result['domains'] = unmarshall_ptr(BODY, data, pos, unmarshall_array, {result['count'], unmarshall_lsa_DomainInfo, {}}, result['domains'])
+
+ stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_lsa_RefDomainList()"))
+ return pos, result
+end
+
+---Unmarshall a pointer to a lsa_RefDomainList. See the unmarshall_lsa_RefDomainList function
+-- for more information.
+--
+--@param data The data packet being processed.
+--@param pos The position within data.
+--@return (pos, result) The new position in data, and a table representing the datatype.
+function unmarshall_lsa_RefDomainList_ptr(data, pos)
+ local result
+ stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_lsa_RefDomainList_ptr()"))
+
+ pos, result = unmarshall_ptr(ALL, data, pos, unmarshall_lsa_RefDomainList, nil)
+
+ stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_lsa_RefDomainList_ptr()"))
+ return pos, result
+end
+
+---Unmarshall a struct with the following definition:
+--
+--
+-- typedef struct {
+-- [range(0,1000)] uint32 count;
+-- [size_is(count)] lsa_TranslatedSid2 *sids;
+-- } lsa_TransSidArray2;
+--
+--
+--@param data The data packet being processed.
+--@param pos The position within data.
+--@return (pos, result) The new position in data, and a table representing the datatype.
+function unmarshall_lsa_TransSidArray2(data, pos)
+ local result = {}
+ stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_lsa_TransSidArray2()"))
+
+ pos, result['count'] = unmarshall_int32(data, pos)
+ pos, result['sid'] = unmarshall_ptr(ALL, data, pos, unmarshall_array, {result['count'], unmarshall_lsa_TranslatedSid2, {}})
+
+ stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_lsa_TransSidArray2()"))
+ return pos, result
+end
+
+---Marshall a struct with the following definition:
+--
+--
+-- typedef struct {
+-- uint32 len; /* ignored */
+-- uint16 impersonation_level;
+-- uint8 context_mode;
+-- uint8 effective_only;
+-- } lsa_QosInfo;
+--
+--
+-- I didn't bother letting the user specify values, since I don't know what any of them do. The
+-- defaults seem to work really well.
+--
+--@return A string representing the marshalled data.
+function marshall_lsa_QosInfo()
+ local result = ""
+ stdnse.print_debug(4, string.format("MSRPC: Entering marshall_lsa_QosInfo()"))
+
+ result = result .. marshall_int32(12)
+ result = result .. marshall_int16(2, false)
+ result = result .. marshall_int8(1, false)
+ result = result .. marshall_int8(0, false)
+
+ stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_lsa_QosInfo()"))
+ return result
+end
+
+---Marshall a struct with the following definition:
+--
+--
+-- typedef struct {
+-- uint32 len; /* ignored */
+-- uint8 *root_dir;
+-- [string,charset(UTF16)] uint16 *object_name;
+-- uint32 attributes;
+-- security_descriptor *sec_desc;
+-- lsa_QosInfo *sec_qos;
+-- } lsa_ObjectAttribute;
+--
+--
+-- I didn't bother letting the user specify values, since I don't know what any of them do. The
+-- defaults seem to work really well.
+--
+--@return A string representing the marshalled data.
+function marshall_lsa_ObjectAttribute()
+ local result = ""
+ stdnse.print_debug(4, string.format("MSRPC: Entering marshall_lsa_ObjectAttribute()"))
+
+ result = result .. marshall_int32(24)
+ result = result .. marshall_int32(0) -- Null'ing out these pointers for now. Maybe we'll need them in the future...
+ result = result .. marshall_int32(0)
+ result = result .. marshall_int32(0)
+ result = result .. marshall_int32(0)
+ result = result .. marshall_ptr(ALL, marshall_lsa_QosInfo, {})
+
+ stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_lsa_ObjectAttribute()"))
+ return result
+end
+
+---Marshall a struct with the following definition:
+--
+--
+-- typedef struct {
+-- dom_sid2 *sid;
+-- } lsa_SidPtr;
+--
+--
+--@param location The part of the pointer wanted, either HEAD (for the data itself), BODY
+-- (for nothing, since this isn't a pointer), or ALL (for the data). Generally, unless the
+-- referent_id is split from the data (for example, in an array), you will want
+-- ALL.
+--@param sid The SID to marshall (as a string).
+--@return A string representing the marshalled data.
+local function marshall_lsa_SidPtr(location, sid)
+ local result
+ stdnse.print_debug(4, string.format("MSRPC: Entering marshall_lsa_SidPtr()"))
+
+ result = marshall_ptr(location, marshall_dom_sid2, {sid}, sid)
+
+ stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_lsa_SidPtr()"))
+ return result
+end
+
+---Marshall a struct with the following definition:
+--
+--
+-- typedef [public] struct {
+-- [range(0,1000)] uint32 num_sids;
+-- [size_is(num_sids)] lsa_SidPtr *sids;
+-- } lsa_SidArray;
+--
+--
+--@param sids The array of SIDs to marshall (as strings).
+--@return A string representing the marshalled data.
+function marshall_lsa_SidArray(sids)
+ local result = ""
+ local array = {}
+ stdnse.print_debug(4, string.format("MSRPC: Entering marshall_lsa_SidArray()"))
+
+ result = result .. marshall_int32(#sids)
+
+ for i = 1, #sids, 1 do
+ array[i] = {}
+ array[i]['func'] = marshall_lsa_SidPtr
+ array[i]['args'] = {sids[i]}
+ end
+
+ result = result .. marshall_ptr(ALL, marshall_array, {array}, array)
+
+ stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_lsa_SidArray()"))
+ return result
+end
+
+
+---Marshall a struct with the following definition:
+--
+--
+-- typedef struct {
+-- [range(0,1000)] uint32 count;
+-- [size_is(count)] lsa_TranslatedName2 *names;
+-- } lsa_TransNameArray2;
+--
+--
+--@param names An array of names to translate.
+--@return A string representing the marshalled data.
+function marshall_lsa_TransNameArray2(names)
+ local result = ""
+ local array = {}
+ stdnse.print_debug(4, string.format("MSRPC: Entering marshall_lsa_TransNameArray2()"))
+
+ if(names == nil) then
+ result = result .. marshall_int32(0)
+ array = nil
+ else
+ result = result .. marshall_int32(#names)
+
+ for i = 1, #names, 1 do
+ array[i] = {}
+ array[i]['func'] = marshall_lsa_TranslatedName2
+ array[i]['args'] = {names[i]['sid_type'], names[i]['name'], names[i]['sid_index'], names[i]['unknown']}
+ end
+ end
+
+ result = result .. marshall_ptr(ALL, marshall_array, {array}, array)
+
+ stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_lsa_TransNameArray2()"))
+ return result
+end
+
+---Unmarshall a lsa_TransNameArray2 structure. See the marshall_lsa_TransNameArray2 for more
+-- information.
+--
+--@param data The data packet being processed.
+--@param pos The position within data.
+--@return (pos, result) The new position in data, and a table representing the datatype.
+function unmarshall_lsa_TransNameArray2(data, pos)
+ local result = {}
+ stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_lsa_TransNameArray2()"))
+
+ pos, result['count'] = unmarshall_int32(data, pos)
+ pos, result['names'] = unmarshall_ptr(ALL, data, pos, unmarshall_array, {result['count'], unmarshall_lsa_TranslatedName2, {}})
+
+ stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_lsa_TransNameArray2()"))
+ return pos, result
+end
+
+
+
+-------------------------------------
+-- WINREG
+-- (dependencies: LSA, INITSHUTDOWN, SECURITY)
+-------------------------------------
+--- Access masks for Windows registry calls
+local winreg_AccessMask =
+{
+ DELETE_ACCESS = 0x00010000,
+ READ_CONTROL_ACCESS = 0x00020000,
+ WRITE_DAC_ACCESS = 0x00040000,
+ WRITE_OWNER_ACCESS = 0x00080000,
+ SYNCHRONIZE_ACCESS = 0x00100000,
+ ACCESS_SACL_ACCESS = 0x00800000,
+ SYSTEM_SECURITY_ACCESS = 0x01000000,
+ MAXIMUM_ALLOWED_ACCESS = 0x02000000,
+ GENERIC_ALL_ACCESS = 0x10000000,
+ GENERIC_EXECUTE_ACCESS = 0x20000000,
+ GENERIC_WRITE_ACCESS = 0x40000000,
+ GENERIC_READ_ACCESS = 0x80000000
+}
+--- String versions of access masks for Windows registry calls
+local winreg_AccessMask_str =
+{
+ DELETE_ACCESS = "Delete",
+ READ_CONTROL_ACCESS = "Read",
+ WRITE_DAC_ACCESS = "Write",
+ WRITE_OWNER_ACCESS = "Write (owner)",
+ SYNCHRONIZE_ACCESS = "Synchronize",
+ ACCESS_SACL_ACCESS = "Access SACL",
+ SYSTEM_SECURITY_ACCESS = "System security",
+ MAXIMUM_ALLOWED_ACCESS = "Maximum allowed access",
+ GENERIC_ALL_ACCESS = "All access",
+ GENERIC_EXECUTE_ACCESS = "Execute access",
+ GENERIC_WRITE_ACCESS = "Write access",
+ GENERIC_READ_ACCESS = "Read access"
+}
+
+---Marshall a winreg_AccessMask.
+--
+--@param accessmask The access mask as a string (see the winreg_AccessMask
+-- table)
+--@return A string representing the marshalled data.
+function marshall_winreg_AccessMask(accessmask)
+ local result
+ stdnse.print_debug(4, string.format("MSRPC: Entering marshall_winreg_AccessMask()"))
+
+ result = marshall_Enum32(accessmask, winreg_AccessMask)
+
+ stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_winreg_AccessMask()"))
+ return result
+end
+
+---Unmarshall a winreg_AccessMask. This datatype is tied to the table with that name.
+--
+--@param data The data packet.
+--@param pos The position within the data.
+--@return (pos, str) The new position, and the string representing the datatype.
+function unmarshall_winreg_AccessMask(data, pos)
+ local str
+ stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_winreg_AccessMask()"))
+
+ pos, str = unmarshall_Enum32(data, pos, winreg_AccessMask)
+
+ stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_winreg_AccessMask()"))
+ return pos, str
+end
+
+---Convert a winreg_AccessMask value to a string that can be shown to the user. This is
+-- based on the _str table.
+--
+--@param val The string value (returned by the unmarshall_ function) to convert.
+--@return A string suitable for displaying to the user, or nil if it wasn't found.
+function winreg_AccessMask_tostr(val)
+ local result
+ stdnse.print_debug(4, string.format("MSRPC: Entering winreg_AccessMask_tostr()"))
+
+ result = winreg_AccessMask_str[val]
+
+ stdnse.print_debug(4, string.format("MSRPC: Leaving winreg_AccessMask_tostr()"))
+ return result
+end
+
+---Registry types
+winreg_Type =
+{
+ REG_NONE = 0,
+ REG_SZ = 1,
+ REG_EXPAND_SZ = 2,
+ REG_BINARY = 3,
+ REG_DWORD = 4,
+ REG_DWORD_BIG_ENDIAN = 5,
+ REG_LINK = 6,
+ REG_MULTI_SZ = 7,
+ REG_RESOURCE_LIST = 8,
+ REG_FULL_RESOURCE_DESCRIPTOR = 9,
+ REG_RESOURCE_REQUIREMENTS_LIST = 10,
+ REG_QWORD = 11
+}
+
+---Registry type strings
+winreg_Type_str =
+{
+ REG_NONE = "None",
+ REG_SZ = "String",
+ REG_EXPAND_SZ = "String (expanded)",
+ REG_BINARY = "Binary",
+ REG_DWORD = "Dword",
+ REG_DWORD_BIG_ENDIAN = "Dword (big endian)",
+ REG_LINK = "Link",
+ REG_MULTI_SZ = "String (multi)",
+ REG_RESOURCE_LIST = "Resource list",
+ REG_FULL_RESOURCE_DESCRIPTOR = "Full resource descriptor",
+ REG_RESOURCE_REQUIREMENTS_LIST = "Resource requirements list",
+ REG_QWORD = "Qword"
+}
+
+---Marshall a winreg_Type. This datatype is tied to the table above with that
+-- name.
+--
+--@param winregtype The value to marshall, as a string
+--@return The marshalled integer representing the given value, or nil if it wasn't
+-- found.
+function marshall_winreg_Type(winregtype)
+ local result
+ stdnse.print_debug(4, string.format("MSRPC: Entering marshall_winreg_Type()"))
+
+ result = marshall_Enum32(winregtype, winreg_Type)
+
+ stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_winreg_Type()"))
+ return result
+end
+
+---Unmarshall a winreg_Type. This datatype is tied to the table with that name.
+--
+--@param data The data packet.
+--@param pos The position within the data.
+--@return (pos, str) The new position, and the string representing the datatype.
+function unmarshall_winreg_Type(data, pos)
+ local str
+ stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_winreg_Type()"))
+
+ pos, str = unmarshall_Enum32(data, pos, winreg_Type)
+
+ stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_winreg_Type()"))
+ return pos, str
+end
+
+---Marshall a pointer to a winreg_Type. This datatype is tied to the table above with that
+-- name.
+--
+--@param winreg_type The value to marshall, as a string
+--@return The marshalled integer representing the given value, or nil if it wasn't
+-- found.
+function marshall_winreg_Type_ptr(winreg_type)
+ local result
+ stdnse.print_debug(4, string.format("MSRPC: Entering marshall_winreg_Type_ptr()"))
+
+ result = marshall_ptr(ALL, marshall_winreg_Type, {winreg_type}, winreg_type)
+
+ stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_winreg_Type_ptr()"))
+ return result
+end
+
+---Unmarshall a pointer to a winreg_Type. This datatype is tied to the table with that name.
+--
+--@param data The data packet.
+--@param pos The position within the data.
+--@return (pos, str) The new position, and the string representing the datatype.
+function unmarshall_winreg_Type_ptr(data, pos)
+ local str
+ stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_winreg_Type_ptr()"))
+
+ pos, str = unmarshall_ptr(ALL, data, pos, unmarshall_winreg_Type, {})
+
+ stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_winreg_Type_ptr()"))
+ return pos, str
+end
+
+---Convert a winreg_Type value to a string that can be shown to the user. This is
+-- based on the _str table.
+--
+--@param val The string value (returned by the unmarshall_ function) to convert.
+--@return A string suitable for displaying to the user, or nil if it wasn't found.
+function winreg_Type_tostr(val)
+ local result
+ stdnse.print_debug(4, string.format("MSRPC: Entering winreg_Type_tostr()"))
+
+ result = winreg_Type_str[val]
+
+ stdnse.print_debug(4, string.format("MSRPC: Leaving winreg_Type_tostr()"))
+ return result
+end
+
+--- A winreg_stringbuf is a buffer that holds a null-terminated string. It can have a max size that's different
+-- from its actual size.
+--
+-- This is the format:
+--
+--
+-- typedef struct {
+-- [value(strlen_m_term(name)*2)] uint16 length;
+-- uint16 size;
+-- [size_is(size/2),length_is(length/2),charset(UTF16)] uint16 *name;
+-- } winreg_StringBuf;
+--
+--
+--@param table The table to marshall. Will probably contain just the 'name' entry.
+--@param max_length [optional] The maximum size of the buffer, in characters, including the null terminator.
+-- Defaults to the length of the string, including the null.
+--@return A string representing the marshalled data.
+function marshall_winreg_StringBuf(table, max_length)
+ local result
+ stdnse.print_debug(4, string.format("MSRPC: Entering marshall_winreg_StringBuf()"))
+
+ local name = table['name']
+ local length
+
+ -- Handle default max lengths
+ if(max_length == nil) then
+ if(name == nil) then
+ max_length = 0
+ else
+ max_length = string.len(name) + 1
+ end
+ end
+
+ if(name == nil) then
+ length = 0
+ else
+ length = string.len(name) + 1
+ end
+
+ result = bin.pack("marshall_winreg_StringBuf, except
+-- the string can be 'nil'.
+--
+--@param str The string to marshall. Can be nil.
+--@param max_length [optional] The maximum size of the buffer, in characters. Defaults to the length of the string, including the null.
+--@return A string representing the marshalled data.
+function marshall_winreg_StringBuf_ptr(table, max_length)
+ local result
+ stdnse.print_debug(4, string.format("MSRPC: Entering marshall_winreg_StringBuf_ptr()"))
+
+ result = marshall_ptr(ALL, marshall_winreg_StringBuf, {table, max_length}, table)
+
+ stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_winreg_StringBuf_ptr()"))
+ return result
+end
+
+---Unmarshall a winreg_StringBuffer pointer
+--
+--@param data The data buffer.
+--@param pos The position in the data buffer.
+--@return (pos, str) The new position and the string.
+function unmarshall_winreg_StringBuf_ptr(data, pos)
+ local str
+ stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_winreg_StringBuf_ptr()"))
+
+ pos, str = unmarshall_ptr(ALL, data, pos, unmarshall_winreg_StringBuf, {})
+
+ stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_winreg_StringBuf_ptr()"))
+ return pos, str
+end
+
+
+--- A winreg_String has the same makup as a winreg_StringBuf, as far as I can tell, so delegate to that function.
+--
+--@param table The table representing the String.
+--@param max_length [optional] The maximum size of the buffer, in characters. Defaults to the length of the string, including the null.
+--@return A string representing the marshalled data.
+function marshall_winreg_String(table, max_length)
+ local result
+ stdnse.print_debug(4, string.format("MSRPC: Entering marshall_winreg_String()"))
+
+ result = marshall_winreg_StringBuf(table, max_length)
+
+ stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_winreg_String()"))
+ return result
+end
+
+---Unmarshall a winreg_String. Since ti has the same makup as winreg_StringBuf, delegate to that.
+--
+--@param data The data buffer.
+--@param pos The position in the data buffer.
+--@return (pos, str) The new position and the string.
+function unmarshall_winreg_String(data, pos)
+ local str
+ stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_winreg_String()"))
+
+ pos, str = unmarshall_winreg_StringBuf(data, pos)
+
+ stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_winreg_String()"))
+ return pos, str
+end
+
+
+-------------------------------------
+-- SRVSVC
+-- (dependencies: SECURITY, SVCCTL)
+-------------------------------------
+---Share types
+local srvsvc_ShareType =
+{
+ STYPE_DISKTREE = 0x00000000,
+ STYPE_DISKTREE_TEMPORARY = 0x40000000,
+ STYPE_DISKTREE_HIDDEN = 0x80000000,
+ STYPE_PRINTQ = 0x00000001,
+ STYPE_PRINTQ_TEMPORARY = 0x40000001,
+ STYPE_PRINTQ_HIDDEN = 0x80000001,
+ STYPE_DEVICE = 0x00000002, -- Serial device
+ STYPE_DEVICE_TEMPORARY = 0x40000002,
+ STYPE_DEVICE_HIDDEN = 0x80000002,
+ STYPE_IPC = 0x00000003, -- Interprocess communication (IPC)
+ STYPE_IPC_TEMPORARY = 0x40000003,
+ STYPE_IPC_HIDDEN = 0x80000003
+}
+---Share type strings
+local srvsvc_ShareType_str =
+{
+ STYPE_DISKTREE = "Disk",
+ STYPE_DISKTREE_TEMPORARY = "Disk (temporary)",
+ STYPE_DISKTREE_HIDDEN = "Disk (hidden)",
+ STYPE_PRINTQ = "Print queue",
+ STYPE_PRINTQ_TEMPORARY = "Print queue (temporary)",
+ STYPE_PRINTQ_HIDDEN = "Print queue (hidden)",
+ STYPE_DEVICE = "Serial device",
+ STYPE_DEVICE_TEMPORARY = "Serial device (temporary)",
+ STYPE_DEVICE_HIDDEN = "Serial device (hidden)",
+ STYPE_IPC = "Interprocess Communication",
+ STYPE_IPC_TEMPORARY = "Interprocess Communication (temporary)",
+ STYPE_IPC_HIDDEN = "Interprocess Communication (hidden)"
+}
+
+---Marshall a srvsvc_ShareType. This datatype is tied to the table above with that
+-- name.
+--
+--@param sharetype The value to marshall, as a string
+--@return The marshalled integer representing the given value, or nil if it wasn't
+-- found.
+function marshall_srvsvc_ShareType(sharetype)
+ local result
+ stdnse.print_debug(4, string.format("MSRPC: Entering marshall_srvsvc_ShareType()"))
+
+ result = marshall_Enum32(sharetype, srvsvc_ShareType)
+
+ stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_srvsvc_ShareType()"))
+ return result
+end
+
+---Unmarshall a srvsvc_ShareType. This datatype is tied to the table with that name.
+--
+--@param data The data packet.
+--@param pos The position within the data.
+--@return (pos, str) The new position, and the string representing the datatype.
+function unmarshall_srvsvc_ShareType(data, pos)
+ local str
+ stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_srvsvc_ShareType()"))
+
+ pos, str = unmarshall_Enum32(data, pos, srvsvc_ShareType)
+
+ stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_srvsvc_ShareType()"))
+ return pos, str
+end
+
+---Convert a srvsvc_ShareType value to a string that can be shown to the user. This is
+-- based on the _str table.
+--
+--@param val The string value (returned by the unmarshall_ function) to convert.
+--@return A string suitable for displaying to the user, or nil if it wasn't found.
+function srvsvc_ShareType_tostr(val)
+ local result
+ stdnse.print_debug(4, string.format("MSRPC: Entering srvsvc_ShareType_tostr()"))
+
+ result = srvsvc_ShareType_str[val]
+
+ stdnse.print_debug(4, string.format("MSRPC: Leaving srvsvc_ShareType_tostr()"))
+ return result
+end
+
+---Marshall a NetShareInfo type 0, which is just a name.
+--
+--
+-- typedef struct {
+-- [string,charset(UTF16)] uint16 *name;
+-- } srvsvc_NetShareInfo0;
+--
+--
+--@param location The part of the pointer wanted, either HEAD (for the data itself), BODY
+-- (for nothing, since this isn't a pointer), or ALL (for the data). Generally, unless the
+-- referent_id is split from the data (for example, in an array), you will want
+-- ALL.
+--@param name The name to marshall.
+--@return A string representing the marshalled data.
+local function marshall_srvsvc_NetShareInfo0(location, name)
+ local result
+ stdnse.print_debug(4, string.format("MSRPC: Entering marshall_srvsvc_NetShareInfo0()"))
+
+ result = marshall_ptr(location, marshall_unicode, {name, true}, name)
+
+ stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_srvsvc_NetShareInfo0()"))
+ return result
+end
+
+---Unmarshall a NetShareInfo type 0, which is just a name. See the marshall function for more information.
+--
+--@param location The part of the pointer wanted, either HEAD (for the data itself), BODY
+-- (for nothing, since this isn't a pointer), or ALL (for the data). Generally, unless the
+-- referent_id is split from the data (for example, in an array), you will want
+-- ALL.
+--@param data The data packet.
+--@param pos The position within the data.
+--@param result This is required when unmarshalling the BODY section, which always comes after
+-- unmarshalling the HEAD. It is the result returned for this parameter during the
+-- HEAD unmarshall. If the referent_id was '0', then this function doesn't unmarshall
+-- anything.
+--@return (pos, result) The new position in data, and a table representing the datatype.
+local function unmarshall_srvsvc_NetShareInfo0(location, data, pos, result)
+ stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_srvsvc_NetShareInfo0()"))
+ if(result == nil) then
+ result = {}
+ end
+
+ if(location == HEAD or location == ALL) then
+ pos, result['name'] = unmarshall_ptr(HEAD, data, pos, unmarshall_unicode, {true})
+ end
+
+ if(location == BODY or location == ALL) then
+ pos, result['name'] = unmarshall_ptr(BODY, data, pos, unmarshall_unicode, {true}, result['name'])
+ end
+
+ stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_srvsvc_NetShareInfo0()"))
+ return pos, result
+end
+
+---Marshall a NetShareInfo type 1, which is the name and a few other things.
+--
+--
+-- typedef struct {
+-- [string,charset(UTF16)] uint16 *name;
+-- srvsvc_ShareType type;
+-- [string,charset(UTF16)] uint16 *comment;
+-- } srvsvc_NetShareInfo1;
+--
+--
+--@param location The part of the pointer wanted, either HEAD (for the data itself), BODY
+-- (for nothing, since this isn't a pointer), or ALL (for the data). Generally, unless the
+-- referent_id is split from the data (for example, in an array), you will want
+-- ALL.
+--@param name The name to marshall.
+--@param sharetype The sharetype to marshall (as a string).
+--@param comment The comment to marshall.
+--@return A string representing the marshalled data.
+local function marshall_srvsvc_NetShareInfo1(location, name, sharetype, comment)
+ local result
+ stdnse.print_debug(4, string.format("MSRPC: Entering marshall_srvsvc_NetShareInfo1()"))
+ local name = marshall_ptr(location, marshall_unicode, {name, true}, name)
+ local sharetype = marshall_basetype(location, marshall_srvsvc_ShareType, {sharetype})
+ local comment = marshall_ptr(location, marshall_unicode, {comment, true}, comment)
+
+ result = bin.pack("data, and a table representing the datatype.
+local function unmarshall_srvsvc_NetShareInfo1(location, data, pos, result)
+ stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_srvsvc_NetShareInfo1()"))
+ if(result == nil) then
+ result = {}
+ end
+
+ if(location == HEAD or location == ALL) then
+ pos, result['name'] = unmarshall_ptr(HEAD, data, pos, unmarshall_unicode, {true})
+ pos, result['sharetype'] = unmarshall_srvsvc_ShareType(data, pos)
+ pos, result['comment'] = unmarshall_ptr(HEAD, data, pos, unmarshall_unicode, {true})
+ end
+
+ if(location == BODY or location == ALL) then
+ pos, result['name'] = unmarshall_ptr(BODY, data, pos, unmarshall_unicode, {true}, result['name'])
+ pos, result['comment'] = unmarshall_ptr(BODY, data, pos, unmarshall_unicode, {true}, result['comment'])
+ end
+
+ stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_srvsvc_NetShareInfo1()"))
+ return pos, result
+end
+
+
+---Marshall a NetShareInfo type 2, which is the name and a few other things.
+--
+--
+-- typedef struct {
+-- [string,charset(UTF16)] uint16 *name;
+-- srvsvc_ShareType type;
+-- [string,charset(UTF16)] uint16 *comment;
+-- uint32 permissions;
+-- uint32 max_users;
+-- uint32 current_users;
+-- [string,charset(UTF16)] uint16 *path;
+-- [string,charset(UTF16)] uint16 *password;
+-- } srvsvc_NetShareInfo2;
+--
+--
+--@param location The part of the pointer wanted, either HEAD (for the data itself), BODY
+-- (for nothing, since this isn't a pointer), or ALL (for the data). Generally, unless the
+-- referent_id is split from the data (for example, in an array), you will want
+-- ALL.
+--@param name The name to marshall.
+--@param sharetype The sharetype to marshall (as a string).
+--@param comment The comment to marshall.
+--@param permissions The permissions, an integer.
+--@param max_users The max users, an integer.
+--@param current_users The current users, an integer.
+--@param path The path, a string.
+--@param password The share-level password, a string (never used on Windows).
+--@return A string representing the marshalled data.
+local function marshall_srvsvc_NetShareInfo2(location, name, sharetype, comment, permissions, max_users, current_users, path, password)
+ local result
+ stdnse.print_debug(4, string.format("MSRPC: Entering marshall_srvsvc_NetShareInfo2()"))
+ local name = marshall_ptr(location, marshall_unicode, {name, true}, name)
+ local sharetype = marshall_basetype(location, marshall_srvsvc_ShareType, {sharetype})
+ local comment = marshall_ptr(location, marshall_unicode, {comment, true}, comment)
+ local permissions = marshall_basetype(location, marshall_int32, {permissions})
+ local max_users = marshall_basetype(location, marshall_int32, {max_users})
+ local current_users = marshall_basetype(location, marshall_int32, {current_users})
+ local path = marshall_ptr(location, marshall_unicode, {path, true}, path)
+ local password = marshall_ptr(location, marshall_password, {password, true}, password)
+
+ result = name .. sharetype .. comment .. permissions .. max_users .. current_users .. path .. password
+
+ stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_srvsvc_NetShareInfo2()"))
+ return result
+end
+
+---Unmarshall a NetShareInfo type 2, which is a name and a few other things. See the marshall
+-- function for more information.
+--
+--@param location The part of the pointer wanted, either HEAD (for the data itself), BODY
+-- (for nothing, since this isn't a pointer), or ALL (for the data). Generally, unless the
+-- referent_id is split from the data (for example, in an array), you will want
+-- ALL.
+--@param data The data packet.
+--@param pos The position within the data.
+--@param result This is required when unmarshalling the BODY section, which always comes after
+-- unmarshalling the HEAD. It is the result returned for this parameter during the
+-- HEAD unmarshall. If the referent_id was '0', then this function doesn't unmarshall
+-- anything.
+--@return (pos, result) The new position in data, and a table representing the datatype.
+local function unmarshall_srvsvc_NetShareInfo2(location, data, pos, result)
+ stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_srvsvc_NetShareInfo2()"))
+ if(result == nil) then
+ result = {}
+ end
+
+ if(location == HEAD or location == ALL) then
+ pos, result['name'] = unmarshall_ptr(HEAD, data, pos, unmarshall_unicode, {true})
+ pos, result['sharetype'] = unmarshall_srvsvc_ShareType(data, pos)
+ pos, result['comment'] = unmarshall_ptr(HEAD, data, pos, unmarshall_unicode, {true})
+ pos, result['permissions'] = unmarshall_int32(data, pos)
+ pos, result['max_users'] = unmarshall_int32(data, pos)
+ pos, result['current_users'] = unmarshall_int32(data, pos)
+ pos, result['path'] = unmarshall_ptr(HEAD, data, pos, unmarshall_unicode, {true})
+ pos, result['password'] = unmarshall_ptr(HEAD, data, pos, unmarshall_unicode, {true})
+ end
+
+ if(location == BODY or location == ALL) then
+ pos, result['name'] = unmarshall_ptr(BODY, data, pos, unmarshall_unicode, {true}, result['name'])
+ pos, result['comment'] = unmarshall_ptr(BODY, data, pos, unmarshall_unicode, {true}, result['comment'])
+ pos, result['path'] = unmarshall_ptr(BODY, data, pos, unmarshall_unicode, {true}, result['path'])
+ pos, result['password'] = unmarshall_ptr(BODY, data, pos, unmarshall_unicode, {true}, result['password'])
+ end
+
+ stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_srvsvc_NetShareInfo2()"))
+ return pos, result
+end
+
+---Marshall a NetShareCtr (container) type 0. It is a simple array with the following definition:
+--
+--
+-- typedef struct {
+-- uint32 count;
+-- [size_is(count)] srvsvc_NetShareInfo0 *array;
+-- } srvsvc_NetShareCtr0;
+--
+--
+--@param NetShareCtr0 A table representing the structure.
+--@return A string representing the marshalled data.
+function marshall_srvsvc_NetShareCtr0(NetShareCtr0)
+ local i
+ local result = ""
+ stdnse.print_debug(4, string.format("MSRPC: Entering marshall_srvsvc_NetShareCtr0()"))
+
+ if(NetShareCtr0 == nil) then
+ result = result .. bin.pack("data, and a table representing the datatype.
+function unmarshall_srvsvc_NetShareCtr0(data, pos)
+ local count
+ local result = {}
+ stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_srvsvc_NetShareCtr0()"))
+
+ pos, count = bin.unpack("
+-- typedef struct {
+-- uint32 count;
+-- [size_is(count)] srvsvc_NetShareInfo1 *array;
+-- } srvsvc_NetShareCtr1;
+--
+--
+--@param NetShareCtr1 A table representing the structure.
+--@return A string representing the marshalled data.
+function marshall_srvsvc_NetShareCtr1(NetShareCtr1)
+ local i
+ local result = ""
+ stdnse.print_debug(4, string.format("MSRPC: Entering marshall_srvsvc_NetShareCtr1()"))
+
+ if(NetShareCtr1 == nil) then
+ result = result .. bin.pack("
+-- typedef struct {
+-- uint32 count;
+-- [size_is(count)] srvsvc_NetShareInfo2 *array;
+-- } srvsvc_NetShareCtr2;
+--
+--
+--@param NetShareCtr2 A pointer to the structure.
+--@return A string representing the marshalled data.
+function marshall_srvsvc_NetShareCtr2(NetShareCtr2)
+ local i
+ local result = ""
+ stdnse.print_debug(4, string.format("MSRPC: Entering marshall_srvsvc_NetShareCtr2()"))
+
+ if(NetShareCtr2 == nil) then
+ result = result .. bin.pack("
+-- typedef union {
+-- [case(0)] srvsvc_NetShareCtr0 *ctr0;
+-- [case(1)] srvsvc_NetShareCtr1 *ctr1;
+-- [case(2)] srvsvc_NetShareCtr2 *ctr2;
+-- [case(501)] srvsvc_NetShareCtr501 *ctr501;
+-- [case(502)] srvsvc_NetShareCtr502 *ctr502;
+-- [case(1004)] srvsvc_NetShareCtr1004 *ctr1004;
+-- [case(1005)] srvsvc_NetShareCtr1005 *ctr1005;
+-- [case(1006)] srvsvc_NetShareCtr1006 *ctr1006;
+-- [case(1007)] srvsvc_NetShareCtr1007 *ctr1007;
+-- [case(1501)] srvsvc_NetShareCtr1501 *ctr1501;
+-- [default] ;
+-- } srvsvc_NetShareCtr;
+--
+--
+-- Not all of them are implemented, however; look at the code to see which are implemented (at the
+-- time of this writing, it's 0, 1, and 2).
+--
+--@param level The level to request. Different levels will return different results, but also require
+-- different access levels to call.
+--@param data The data to populate the array with. Depending on the level, this data will be different.
+-- For level 0, you'll probably want a table containing array=nil.
+--@return A string representing the marshalled data, or 'nil' if it couldn't be marshalled.
+function marshall_srvsvc_NetShareCtr(level, data)
+ local result
+ stdnse.print_debug(4, string.format("MSRPC: Entering marshall_srvsvc_NetShareCtr()"))
+
+ if(level == 0) then
+ result = bin.pack("data, and a table representing the datatype.
+-- The result may be nil if there's an error.
+function unmarshall_srvsvc_NetShareCtr(data, pos)
+ local level
+ local result
+ stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_srv_NetShareCtr()"))
+
+ pos, level = bin.unpack("
+-- typedef union {
+-- [case(0)] srvsvc_NetShareInfo0 *info0;
+-- [case(1)] srvsvc_NetShareInfo1 *info1;
+-- [case(2)] srvsvc_NetShareInfo2 *info2;
+-- [case(501)] srvsvc_NetShareInfo501 *info501;
+-- [case(502)] srvsvc_NetShareInfo502 *info502;
+-- [case(1004)] srvsvc_NetShareInfo1004 *info1004;
+-- [case(1005)] srvsvc_NetShareInfo1005 *info1005;
+-- [case(1006)] srvsvc_NetShareInfo1006 *info1006;
+-- [case(1007)] srvsvc_NetShareInfo1007 *info1007;
+-- [case(1501)] sec_desc_buf *info1501;
+-- [default] ;
+-- } srvsvc_NetShareInfo;
+--
+--
+-- Not all of them are implemented, however; look at the code to see which are implemented (at the
+-- time of this writing, it's 0, 1, and 2).
+--
+--@param level The level to request. Different levels will return different results, but also require
+-- different access levels to call.
+--@param data The data packet being processed.
+--@param pos The position within data.
+--@return (pos, result) The new position in data, and a table representing the datatype. This may be
+-- nil if there was an error.
+function unmarshall_srvsvc_NetShareInfo(data, pos)
+ local level
+ local result
+ stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_srvsvc_NetShareInfo()"))
+ pos, level = unmarshall_int32(data, pos)
+
+ if(level == 0) then
+ pos, result = unmarshall_ptr(ALL, data, pos, unmarshall_struct, {unmarshall_srvsvc_NetShareInfo0, {}})
+ elseif(level == 1) then
+ pos, result = unmarshall_ptr(ALL, data, pos, unmarshall_struct, {unmarshall_srvsvc_NetShareInfo1, {}})
+ elseif(level == 2) then
+ pos, result = unmarshall_ptr(ALL, data, pos, unmarshall_struct, {unmarshall_srvsvc_NetShareInfo2, {}})
+ else
+ stdnse.print_debug(1, "MSRPC: ERROR: Invalid level returned by NetShareInfo: %d\n", level)
+ pos, result = nil, nil
+ end
+
+ stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_srvsvc_NetShareInfo()"))
+ return pos, result
+end
+
+---Marshall a NetSessInfo type 10.
+--
+--
+-- typedef struct {
+-- [string,charset(UTF16)] uint16 *client;
+-- [string,charset(UTF16)] uint16 *user;
+-- uint32 time;
+-- uint32 idle_time;
+-- } srvsvc_NetSessInfo10;
+--
+--
+--@param location The part of the pointer wanted, either HEAD (for the data itself), BODY
+-- (for nothing, since this isn't a pointer), or ALL (for the data). Generally, unless the
+-- referent_id is split from the data (for example, in an array), you will want
+-- ALL.
+--@param client The client string.
+--@param user The user string.
+--@param time The number of seconds that the user has been logged on.
+--@param idle_time The number of seconds that the user's been idle.
+--@return A string representing the marshalled data.
+local function marshall_srvsvc_NetSessInfo10(location, client, user, time, idle_time)
+ local result
+ stdnse.print_debug(4, string.format("MSRPC: Entering marshall_srvsvc_NetShareInfo10()"))
+ local client = marshall_ptr(location, marshall_unicode, {client, true}, client)
+ local user = marshall_ptr(location, marshall_unicode, {user, true}, user)
+ local time = marshall_basetype(location, marshall_int32, {time})
+ local idle_time = marshall_basetype(location, marshall_int32, {idle_time})
+
+ result = bin.pack("data, and a table representing the datatype.
+local function unmarshall_srvsvc_NetSessInfo10(location, data, pos, result)
+ stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_srvsvc_NetSessInfo10()"))
+ if(result == nil) then
+ result = {}
+ end
+
+ if(location == HEAD or location == ALL) then
+ pos, result['client'] = unmarshall_ptr(HEAD, data, pos, unmarshall_unicode, {true})
+ pos, result['user'] = unmarshall_ptr(HEAD, data, pos, unmarshall_unicode, {true})
+ pos, result['time'] = unmarshall_int32(data, pos)
+ pos, result['idle_time'] = unmarshall_int32(data, pos)
+ end
+
+ if(location == BODY or location == ALL) then
+ pos, result['client'] = unmarshall_ptr(BODY, data, pos, unmarshall_unicode, {true}, result['client'])
+ pos, result['user'] = unmarshall_ptr(BODY, data, pos, unmarshall_unicode, {true}, result['user'])
+ end
+
+ stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_srvsvc_NetSessInfo10()"))
+ return pos, result
+end
+
+---Marshall a NetSessCtr (session container) type 10. It is a simple array with the following definition:
+--
+--
+-- typedef struct {
+-- uint32 count;
+-- [size_is(count)] srvsvc_NetSessInfo10 *array;
+-- } srvsvc_NetSessCtr10;
+--
+--
+--@param NetSessCtr10 A table representing the structure.
+--@return A string representing the marshalled data.
+function marshall_srvsvc_NetSessCtr10(NetSessCtr10)
+ local i
+ local result = ""
+ stdnse.print_debug(4, string.format("MSRPC: Entering marshall_srvsvc_NetSessCtr10()"))
+
+ if(NetSessCtr10 == nil) then
+ result = result .. bin.pack("data, and a table representing the datatype.
+function unmarshall_srvsvc_NetSessCtr10(data, pos)
+ local count
+ local result = {}
+ stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_srvsvc_NetSessCtr10()"))
+
+ pos, count = bin.unpack("
+-- typedef union {
+-- [case(0)] srvsvc_NetSessCtr0 *ctr0;
+-- [case(1)] srvsvc_NetSessCtr1 *ctr1;
+-- [case(2)] srvsvc_NetSessCtr2 *ctr2;
+-- [case(10)] srvsvc_NetSessCtr10 *ctr10;
+-- [case(502)] srvsvc_NetSessCtr502 *ctr502;
+-- [default] ;
+-- } srvsvc_NetSessCtr;
+--
+--
+-- Not all of them are implemented, however; look at the code to see which are implemented (at the
+-- time of this writing, it's just 10).
+--
+--@param level The level to request. Different levels will return different results, but also require
+-- different access levels to call.
+--@param data The data to populate the array with. Depending on the level, this data will be different.
+--@return A string representing the marshalled data.
+function marshall_srvsvc_NetSessCtr(level, data)
+ local result
+ stdnse.print_debug(4, string.format("MSRPC: Entering marshall_srvsvc_NetShareCtr()"))
+
+ if(level == 10) then
+ result = bin.pack("data
+--@return (pos, result) The new position in data, and a table representing the datatype. Can be
+-- nil if there's an error.
+function unmarshall_srvsvc_NetSessCtr(data, pos)
+ local level
+ local result
+ stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_srvsvc_NetSessCtr()"))
+
+ pos, level = bin.unpack("srvsvc_Statistics packet. This is basically a great big struct:
+--
+--
+-- typedef struct {
+-- uint32 start;
+-- uint32 fopens;
+-- uint32 devopens;
+-- uint32 jobsqueued;
+-- uint32 sopens;
+-- uint32 stimeouts;
+-- uint32 serrorout;
+-- uint32 pwerrors;
+-- uint32 permerrors;
+-- uint32 syserrors;
+-- uint32 bytessent_low;
+-- uint32 bytessent_high;
+-- uint32 bytesrcvd_low;
+-- uint32 bytesrcvd_high;
+-- uint32 avresponse;
+-- uint32 reqbufneed;
+-- uint32 bigbufneed;
+-- } srvsvc_Statistics;
+--
+--
+-- Note that Wireshark (at least, the version I'm using, 1.0.3) gets this wrong, so be careful.
+--
+--@param data The data packet being processed.
+--@param pos The position within data
+--@return (pos, result) The new position in data, and a table representing the datatype.
+function unmarshall_srvsvc_Statistics(data, pos)
+ local response = {}
+ stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_srvsvc_Statistics()"))
+
+ pos, response['start'] = unmarshall_int32(data, pos)
+ pos, response['fopens'] = unmarshall_int32(data, pos)
+ pos, response['devopens'] = unmarshall_int32(data, pos)
+ pos, response['jobsqueued'] = unmarshall_int32(data, pos)
+ pos, response['sopens'] = unmarshall_int32(data, pos)
+ pos, response['stimeouts'] = unmarshall_int32(data, pos)
+ pos, response['serrorout'] = unmarshall_int32(data, pos)
+ pos, response['pwerrors'] = unmarshall_int32(data, pos)
+ pos, response['permerrors'] = unmarshall_int32(data, pos)
+ pos, response['syserrors'] = unmarshall_int32(data, pos)
+ pos, response['bytessent_low'] = unmarshall_int32(data, pos)
+ pos, response['bytessent_high'] = unmarshall_int32(data, pos)
+ pos, response['bytesrcvd_low'] = unmarshall_int32(data, pos)
+ pos, response['bytesrcvd_high'] = unmarshall_int32(data, pos)
+ pos, response['avresponse'] = unmarshall_int32(data, pos)
+ pos, response['reqbufneed'] = unmarshall_int32(data, pos)
+ pos, response['bigbufneed'] = unmarshall_int32(data, pos)
+
+ stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_srvsvc_Statistics()"))
+ return pos, response
+end
+
+---Unmarshalls a srvsvc_Statistics as a pointer. Wireshark fails to do this, and ends
+-- up parsing the packet wrong, so take care when packetlogging.
+--
+-- See unmarshall_srvsvc_Statistics for more information.
+--
+--@param data The data packet being processed.
+--@param pos The position within data
+--@return (pos, result) The new position in data, and a table representing the datatype.
+function unmarshall_srvsvc_Statistics_ptr(data, pos)
+ local result
+ stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_srvsvc_Statistics_ptr()"))
+
+ pos, result = unmarshall_ptr(ALL, data, pos, unmarshall_srvsvc_Statistics, {})
+
+ stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_srvsvc_Statistics_ptr()"))
+ return pos, result
+end
+
+
+
+----------------------------------
+-- SAMR
+-- (dependencies: MISC, LSA, SECURITY)
+----------------------------------
+
+local samr_ConnectAccessMask =
+{
+ SAMR_ACCESS_CONNECT_TO_SERVER = 0x00000001,
+ SAMR_ACCESS_SHUTDOWN_SERVER = 0x00000002,
+ SAMR_ACCESS_INITIALIZE_SERVER = 0x00000004,
+ SAMR_ACCESS_CREATE_DOMAIN = 0x00000008,
+ SAMR_ACCESS_ENUM_DOMAINS = 0x00000010,
+ SAMR_ACCESS_OPEN_DOMAIN = 0x00000020
+}
+local samr_ConnectAccessMask_str =
+{
+ SAMR_ACCESS_CONNECT_TO_SERVER = "Connect to server",
+ SAMR_ACCESS_SHUTDOWN_SERVER = "Shutdown server",
+ SAMR_ACCESS_INITIALIZE_SERVER = "Initialize server",
+ SAMR_ACCESS_CREATE_DOMAIN = "Create domain",
+ SAMR_ACCESS_ENUM_DOMAINS = "Enum domains",
+ SAMR_ACCESS_OPEN_DOMAIN = "Open domain"
+}
+
+---Marshall a samr_ConnectAccessMask. This datatype is tied to the table above with that
+-- name.
+--
+--@param accessmask The value to marshall, as a string
+--@return The marshalled integer representing the given value, or nil if it wasn't
+-- found.
+function marshall_samr_ConnectAccessMask(accessmask)
+ local result
+ stdnse.print_debug(4, string.format("MSRPC: Entering marshall_samr_ConnectAccessMask()"))
+
+ result = marshall_Enum32(accessmask, samr_ConnectAccessMask)
+
+ stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_samr_ConnectAccessMask()"))
+ return result
+end
+
+---Unmarshall a samr_ConnectAccessMask. This datatype is tied to the table with that name.
+--
+--@param data The data packet.
+--@param pos The position within the data.
+--@return (pos, result) The new position in data, and a table representing the datatype.
+function unmarshall_samr_ConnectAccessMask(data, pos)
+ local result
+ stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_samr_ConnectAccessMask()"))
+
+ pos, result = unmarshall_Enum32(data, pos, samr_ConnectAccessMask)
+
+ stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_samr_ConnectAccessMask()"))
+ return pos, result
+end
+
+---Convert a samr_ConnectAccessMask value to a string that can be shown to the user. This is
+-- based on the _str table.
+--
+--@param val The string value (returned by the unmarshall_ function) to convert.
+--@return A string suitable for displaying to the user, or nil if it wasn't found.
+function samr_ConnectAccessMask_tostr(val)
+ local result
+ stdnse.print_debug(4, string.format("MSRPC: Entering samr_ConnectAccessMask_tostr()"))
+
+ result = samr_ConnectAccessMask_str[val]
+
+ stdnse.print_debug(4, string.format("MSRPC: Leaving samr_ConnectAccessMask_tostr()"))
+ return result
+end
+
+local samr_DomainAccessMask =
+{
+ DOMAIN_ACCESS_LOOKUP_INFO_1 = 0x00000001,
+ DOMAIN_ACCESS_SET_INFO_1 = 0x00000002,
+ DOMAIN_ACCESS_LOOKUP_INFO_2 = 0x00000004,
+ DOMAIN_ACCESS_SET_INFO_2 = 0x00000008,
+ DOMAIN_ACCESS_CREATE_USER = 0x00000010,
+ DOMAIN_ACCESS_CREATE_GROUP = 0x00000020,
+ DOMAIN_ACCESS_CREATE_ALIAS = 0x00000040,
+ DOMAIN_ACCESS_LOOKUP_ALIAS = 0x00000080,
+ DOMAIN_ACCESS_ENUM_ACCOUNTS = 0x00000100,
+ DOMAIN_ACCESS_OPEN_ACCOUNT = 0x00000200,
+ DOMAIN_ACCESS_SET_INFO_3 = 0x00000400
+}
+local samr_DomainAccessMask_str =
+{
+ DOMAIN_ACCESS_LOOKUP_INFO_1 = "Lookup info (1)",
+ DOMAIN_ACCESS_SET_INFO_1 = "Set info (1)",
+ DOMAIN_ACCESS_LOOKUP_INFO_2 = "Lookup info (2)",
+ DOMAIN_ACCESS_SET_INFO_2 = "Set info (2)",
+ DOMAIN_ACCESS_CREATE_USER = "Create user",
+ DOMAIN_ACCESS_CREATE_GROUP = "Create group",
+ DOMAIN_ACCESS_CREATE_ALIAS = "Create alias",
+ DOMAIN_ACCESS_LOOKUP_ALIAS = "Lookup alias",
+ DOMAIN_ACCESS_ENUM_ACCOUNTS = "Enum accounts",
+ DOMAIN_ACCESS_OPEN_ACCOUNT = "Open account",
+ DOMAIN_ACCESS_SET_INFO_3 = "Set info (3)"
+}
+
+---Marshall a samr_DomainAccessMask. This datatype is tied to the table above with that
+-- name.
+--
+--@param accessmask The value to marshall, as a string
+--@return The marshalled integer representing the given value, or nil if it wasn't
+-- found.
+function marshall_samr_DomainAccessMask(accessmask)
+ local result
+ stdnse.print_debug(4, string.format("MSRPC: Entering marshall_samr_DomainAccessMask()"))
+
+ result = marshall_Enum32(accessmask, samr_DomainAccessMask)
+
+ stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_samr_DomainAccessMask()"))
+ return result
+end
+
+---Unmarshall a samr_DomainAccessMask. This datatype is tied to the table with that name.
+--
+--@param data The data packet.
+--@param pos The position within the data.
+--@return (pos, result) The new position in data, and a table representing the datatype.
+function unmarshall_samr_DomainAccessMask(data, pos)
+ local result
+ stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_samr_DomainAccessMask()"))
+
+ pos, result = unmarshall_Enum32(data, pos, samr_DomainAccessMask)
+
+ stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_samr_DomainAccessMask()"))
+ return pos, result
+end
+
+---Convert a samr_DomainAccessMask value to a string that can be shown to the user. This is
+-- based on the _str table.
+--
+--@param val The string value (returned by the unmarshall_ function) to convert.
+--@return A string suitable for displaying to the user, or nil if it wasn't found.
+function samr_DomainAccessMask_tostr(val)
+ local result
+ stdnse.print_debug(4, string.format("MSRPC: Entering samr_DomainAccessMask_tostr()"))
+
+ result = samr_DomainAccessMask_str[val]
+
+ stdnse.print_debug(4, string.format("MSRPC: Leaving samr_DomainAccessMask_tostr()"))
+ return result
+end
+
+local samr_AcctFlags =
+{
+ ACB_NONE = 0x0000000,
+ ACB_DISABLED = 0x00000001, -- User account disabled
+ ACB_HOMDIRREQ = 0x00000002, -- Home directory required
+ ACB_PWNOTREQ = 0x00000004, -- User password not required
+ ACB_TEMPDUP = 0x00000008, -- Temporary duplicate account
+ ACB_NORMAL = 0x00000010, -- Normal user account
+ ACB_MNS = 0x00000020, -- MNS logon user account
+ ACB_DOMTRUST = 0x00000040, -- Interdomain trust account
+ ACB_WSTRUST = 0x00000080, -- Workstation trust account
+ ACB_SVRTRUST = 0x00000100, -- Server trust account
+ ACB_PWNOEXP = 0x00000200, -- User password does not expire
+ ACB_AUTOLOCK = 0x00000400, -- Account auto locked
+ ACB_ENC_TXT_PWD_ALLOWED = 0x00000800, -- Encryped text password is allowed
+ ACB_SMARTCARD_REQUIRED = 0x00001000, -- Smart Card required
+ ACB_TRUSTED_FOR_DELEGATION = 0x00002000, -- Trusted for Delegation
+ ACB_NOT_DELEGATED = 0x00004000, -- Not delegated
+ ACB_USE_DES_KEY_ONLY = 0x00008000, -- Use DES key only
+ ACB_DONT_REQUIRE_PREAUTH = 0x00010000, -- Preauth not required
+ ACB_PW_EXPIRED = 0x00020000, -- Password Expired
+ ACB_NO_AUTH_DATA_REQD = 0x00080000 -- No authorization data required
+}
+local samr_AcctFlags_str =
+{
+ ACB_NONE = "n/a",
+ ACB_DISABLED = "Account disabled",
+ ACB_HOMDIRREQ = "Home directory required",
+ ACB_PWNOTREQ = "Password not required",
+ ACB_TEMPDUP = "Temporary duplicate account",
+ ACB_NORMAL = "Normal user account",
+ ACB_MNS = "MNS logon user account",
+ ACB_DOMTRUST = "Interdomain trust account",
+ ACB_WSTRUST = "Workstation trust account",
+ ACB_SVRTRUST = "Server trust account",
+ ACB_PWNOEXP = "Password does not expire",
+ ACB_AUTOLOCK = "Auto locked",
+ ACB_ENC_TXT_PWD_ALLOWED = "Encryped text password is allowed",
+ ACB_SMARTCARD_REQUIRED = "Smart Card required",
+ ACB_TRUSTED_FOR_DELEGATION = "Trusted for Delegation",
+ ACB_NOT_DELEGATED = "Not delegated",
+ ACB_USE_DES_KEY_ONLY = "Use DES key only",
+ ACB_DONT_REQUIRE_PREAUTH = "Preauth not required",
+ ACB_PW_EXPIRED = "Password Expired",
+ ACB_NO_AUTH_DATA_REQD = "No authorization data required"
+}
+
+---Marshall a samr_AcctFlags. This datatype is tied to the table above with that
+-- name.
+--
+--@param flags The value to marshall, as a string
+--@return The marshalled integer representing the given value, or nil if it wasn't
+-- found.
+function marshall_samr_AcctFlags(flags)
+ local result
+ stdnse.print_debug(4, string.format("MSRPC: Entering marshall_samr_AcctFlags()"))
+
+ result = marshall_Enum32(flags, samr_AcctFlags)
+
+ stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_samr_AcctFlags()"))
+ return result
+end
+
+---Unmarshall a samr_AcctFlags. This datatype is tied to the table with that name.
+--
+--@param data The data packet.
+--@param pos The position within the data.
+--@return (pos, str) The new position, and the string representing the datatype.
+function unmarshall_samr_AcctFlags(data, pos)
+ local str
+ stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_samr_AcctFlags()"))
+
+ pos, str = unmarshall_Enum32_array(data, pos, samr_AcctFlags)
+
+ stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_samr_AcctFlags()"))
+ return pos, str
+end
+
+---Convert a samr_AcctFlags value to a string that can be shown to the user. This is
+-- based on the _str table.
+--
+--@param val The string value (returned by the unmarshall_ function) to convert.
+--@return A string suitable for displaying to the user, or nil if it wasn't found.
+function samr_AcctFlags_tostr(val)
+ local result
+ stdnse.print_debug(4, string.format("MSRPC: Entering samr_AcctFlags_tostr()"))
+
+ result = samr_AcctFlags_str[val]
+
+ stdnse.print_debug(4, string.format("MSRPC: Leaving samr_AcctFlags_tostr()"))
+ return result
+end
+
+local samr_PasswordProperties =
+{
+ DOMAIN_PASSWORD_COMPLEX = 0x00000001,
+ DOMAIN_PASSWORD_NO_ANON_CHANGE = 0x00000002,
+ DOMAIN_PASSWORD_NO_CLEAR_CHANGE = 0x00000004,
+ DOMAIN_PASSWORD_LOCKOUT_ADMINS = 0x00000008,
+ DOMAIN_PASSWORD_STORE_CLEARTEXT = 0x00000010,
+ DOMAIN_REFUSE_PASSWORD_CHANGE = 0x00000020
+}
+local samr_PasswordProperties_str =
+{
+ DOMAIN_PASSWORD_COMPLEX = "Compexity requirements exit",
+ DOMAIN_PASSWORD_NO_ANON_CHANGE = "Must be logged in to change password",
+ DOMAIN_PASSWORD_NO_CLEAR_CHANGE = "Cannot change passwords in cleartext",
+ DOMAIN_PASSWORD_LOCKOUT_ADMINS = "Admin account can be locked out",
+ DOMAIN_PASSWORD_STORE_CLEARTEXT = "Cleartext passwords can be stored",
+ DOMAIN_REFUSE_PASSWORD_CHANGE = "Passwords cannot be changed"
+}
+
+---Marshall a samr_PasswordProperties. This datatype is tied to the table above with that
+-- name.
+--
+--@param properties The value to marshall, as a string
+--@return The marshalled integer representing the given value, or nil if it wasn't
+-- found.
+function marshall_samr_PasswordProperties(properties)
+ local result
+ stdnse.print_debug(4, string.format("MSRPC: Entering marshall_samr_PasswordProperties()"))
+
+ result = marshall_Enum32(properties, samr_PasswordProperties)
+
+ stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_samr_PasswordProperties()"))
+ return result
+end
+
+---Unmarshall a samr_PasswordProperties. This datatype is tied to the table with that name.
+--
+--@param data The data packet.
+--@param pos The position within the data.
+--@return (pos, str) The new position, and the string representing the datatype.
+function unmarshall_samr_PasswordProperties(data, pos)
+ local str
+ stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_samr_PasswordProperties()"))
+
+ pos, str = unmarshall_Enum32_array(data, pos, samr_PasswordProperties)
+
+ stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_samr_PasswordProperties()"))
+ return pos, str
+end
+
+---Convert a samr_PasswordProperties value to a string that can be shown to the user. This is
+-- based on the _str table.
+--
+--@param val The string value (returned by the unmarshall_ function) to convert.
+--@return A string suitable for displaying to the user, or nil if it wasn't found.
+function samr_PasswordProperties_tostr(val)
+ local result
+ stdnse.print_debug(4, string.format("MSRPC: Entering samr_PasswordProperties_tostr()"))
+
+ result = samr_PasswordProperties_str[val]
+
+ stdnse.print_debug(4, string.format("MSRPC: Leaving samr_PasswordProperties_tostr()"))
+ return result
+end
+
+
+---Unmarshall a struct with the following definition:
+--
+--
+-- typedef struct {
+-- uint32 idx;
+-- lsa_String name;
+-- } samr_SamEntry;
+--
+--
+--@param location The part of the pointer wanted, either HEAD (for the data itself), BODY
+-- (for nothing, since this isn't a pointer), or ALL (for the data). Generally, unless the
+-- referent_id is split from the data (for example, in an array), you will want
+-- ALL.
+--@param data The data packet being processed.
+--@param pos The position within data.
+--@param result This is required when unmarshalling the BODY section, which always comes after
+-- unmarshalling the HEAD. It is the result returned for this parameter during the
+-- HEAD unmarshall. If the referent_id was '0', then this function doesn't unmarshall
+-- anything.
+--@return (pos, result) The new position in data, and a table representing the datatype.
+local function unmarshall_samr_SamEntry(location, data, pos, result)
+ stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_samr_SamEntry()"))
+ if(result == nil) then
+ result = {}
+ end
+
+ if(location == HEAD or location == ALL) then
+ pos, result['idx'] = unmarshall_int32(data, pos)
+ pos, result['name'] = unmarshall_lsa_String_internal(HEAD, data, pos)
+ end
+
+
+ if(location == BODY or location == ALL) then
+ pos, result['name'] = unmarshall_lsa_String_internal(BODY, data, pos, result['name'])
+ end
+
+ stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_samr_SamEntry()"))
+ return pos, result
+end
+
+---Unmarshall a struct with the following definition:
+--
+--
+-- typedef struct {
+-- uint32 count;
+-- [size_is(count)] samr_SamEntry *entries;
+-- } samr_SamArray;
+--
+--
+--@param data The data packet being processed.
+--@param pos The position within data.
+--@return (pos, result) The new position in data, and a table representing the datatype.
+function unmarshall_samr_SamArray(data, pos)
+ local result = {}
+ stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_samr_SamArray()"))
+
+ pos, result['count'] = unmarshall_int32(data, pos)
+ pos, result['entries'] = unmarshall_ptr(ALL, data, pos, unmarshall_array, {result['count'], unmarshall_samr_SamEntry, {}})
+
+ stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_samr_SamArray()"))
+ return pos, result
+end
+
+---Unmarshall a pointer to a samr_SamArray type. See unmarshall_samr_SamArray for
+-- more information.
+--
+--@param data The data packet being processed.
+--@param pos The position within data.
+--@return (pos, result) The new position in data, and a table representing the datatype.
+function unmarshall_samr_SamArray_ptr(data, pos)
+ local result
+ stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_samr_SamArray_ptr()"))
+
+ pos, result = unmarshall_ptr(ALL, data, pos, unmarshall_samr_SamArray, {})
+
+ stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_samr_SamArray_ptr()"))
+ return pos, result
+end
+
+---Unmarshall a struct with the following definition:
+--
+--
+-- typedef struct {
+-- uint32 idx;
+-- uint32 rid;
+-- samr_AcctFlags acct_flags;
+-- lsa_String account_name;
+-- lsa_String description;
+-- lsa_String full_name;
+-- } samr_DispEntryGeneral;
+--
+--
+--@param location The part of the pointer wanted, either HEAD (for the data itself), BODY
+-- (for nothing, since this isn't a pointer), or ALL (for the data). Generally, unless the
+-- referent_id is split from the data (for example, in an array), you will want
+-- ALL.
+--@param data The data packet being processed.
+--@param pos The position within data.
+--@param result This is required when unmarshalling the BODY section, which always comes after
+-- unmarshalling the HEAD. It is the result returned for this parameter during the
+-- HEAD unmarshall. If the referent_id was '0', then this function doesn't unmarshall
+-- anything.
+--@return (pos, result) The new position in data, and a table representing the datatype.
+local function unmarshall_samr_DispEntryGeneral(location, data, pos, result)
+ stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_samr_DispEntryGeneral()"))
+ if(result == nil) then
+ result = {}
+ end
+
+ if(location == HEAD or location == ALL) then
+ pos, result['idx'] = unmarshall_int32(data, pos)
+ pos, result['rid'] = unmarshall_int32(data, pos)
+ pos, result['acct_flags'] = unmarshall_samr_AcctFlags(data, pos)
+ pos, result['account_name'] = unmarshall_lsa_String_internal(HEAD, data, pos)
+ pos, result['description'] = unmarshall_lsa_String_internal(HEAD, data, pos)
+ pos, result['full_name'] = unmarshall_lsa_String_internal(HEAD, data, pos)
+ end
+
+
+ if(location == BODY or location == ALL) then
+ pos, result['account_name'] = unmarshall_lsa_String_internal(BODY, data, pos, result['account_name'])
+ pos, result['description'] = unmarshall_lsa_String_internal(BODY, data, pos, result['description'])
+ pos, result['full_name'] = unmarshall_lsa_String_internal(BODY, data, pos, result['full_name'])
+ end
+
+ stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_samr_DispEntryGeneral()"))
+ return pos, result
+end
+
+---Unmarshall a struct with the following definition:
+--
+--
+-- typedef struct {
+-- uint32 count;
+-- [size_is(count)] samr_DispEntryGeneral *entries;
+-- } samr_DispInfoGeneral;
+--
+--
+--@param data The data packet being processed.
+--@param pos The position within data.
+--@return (pos, result) The new position in data, and a table representing the datatype.
+function unmarshall_samr_DispInfoGeneral(data, pos)
+ local result = {}
+ stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_samr_DispInfoGeneral()"))
+
+ pos, result['count'] = unmarshall_int32(data, pos)
+ pos, result['entries'] = unmarshall_ptr(ALL, data, pos, unmarshall_array, {result['count'], unmarshall_samr_DispEntryGeneral, {}})
+
+ stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_samr_DispInfoGeneral()"))
+ return pos, result
+end
+
+
+---Unmarshall a struct with the following definition:
+--
+--
+-- typedef [switch_type(uint16)] union {
+-- [case(1)] samr_DispInfoGeneral info1;/* users */
+-- [case(2)] samr_DispInfoFull info2; /* trust accounts? */
+-- [case(3)] samr_DispInfoFullGroups info3; /* groups */
+-- [case(4)] samr_DispInfoAscii info4; /* users */
+-- [case(5)] samr_DispInfoAscii info5; /* groups */
+-- } samr_DispInfo;
+--
+--
+--@param data The data packet being processed.
+--@param pos The position within data.
+--@return (pos, result) The new position in data, and a table representing the datatype. It may also return
+-- nil, if there was an error.
+function unmarshall_samr_DispInfo(data, pos)
+ local level
+ local result
+ stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_samr_DispInfo()"))
+
+ pos, level = msrpctypes.unmarshall_int16(data, pos)
+
+ if(level == 1) then
+ pos, result = unmarshall_samr_DispInfoGeneral(data, pos)
+ else
+ stdnse.print_debug(1, "MSRPC: ERROR: Server returned an unknown level for samr_DispInfo: %d", level)
+ pos, result = nil, nil
+ end
+
+ stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_samr_DispInfo()"))
+ return pos, result
+end
+
+---Unmarshall a struct with the following definition:
+--
+--
+-- typedef struct {
+-- uint16 min_password_length;
+-- uint16 password_history_length;
+-- samr_PasswordProperties password_properties;
+-- /* yes, these are signed. They are in negative 100ns */
+-- dlong max_password_age;
+-- dlong min_password_age;
+-- } samr_DomInfo1;
+--
+--
+--@param data The data packet being processed.
+--@param pos The position within data.
+--@return (pos, result) The new position in data, and a table representing the datatype.
+function unmarshall_samr_DomInfo1(data, pos)
+ local result = {}
+ stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_samr_DomInfo1()"))
+
+ pos, result['min_password_length'] = unmarshall_int16(data, pos, false)
+ pos, result['password_history_length'] = unmarshall_int16(data, pos, false)
+ pos, result['password_properties'] = unmarshall_samr_PasswordProperties(data, pos)
+ pos, result['max_password_age'] = unmarshall_hyper(data, pos)
+ pos, result['min_password_age'] = unmarshall_hyper(data, pos)
+
+ stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_samr_DomInfo1()"))
+ return pos, result
+end
+
+---Unmarshall a struct with the following definition:
+--
+--
+-- typedef struct {
+-- hyper sequence_num;
+-- NTTIME domain_create_time;
+-- } samr_DomInfo8;
+--
+--
+--@param data The data packet being processed.
+--@param pos The position within data.
+--@return (pos, result) The new position in data, and a table representing the datatype.
+function unmarshall_samr_DomInfo8(data, pos)
+ local result = {}
+ stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_samr_DomInfo8()"))
+
+ pos, result['sequence_num'] = unmarshall_hyper(data, pos)
+ pos, result['domain_create_time'] = unmarshall_NTTIME(data, pos)
+
+ stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_samr_DomInfo8()"))
+ return pos, result
+end
+
+---Unmarshall a struct with the following definition:
+--
+--
+-- typedef struct {
+-- hyper lockout_duration;
+-- hyper lockout_window;
+-- uint16 lockout_threshold;
+-- } samr_DomInfo12;
+--
+--
+--@param data The data packet being processed.
+--@param pos The position within data.
+--@return (pos, result) The new position in data, and a table representing the datatype.
+function unmarshall_samr_DomInfo12(data, pos)
+ local result = {}
+ stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_samr_DomInfo12()"))
+
+ pos, result['lockout_duration'] = unmarshall_hyper(data, pos)
+ pos, result['lockout_window'] = unmarshall_hyper(data, pos)
+ pos, result['lockout_threshold'] = unmarshall_int16(data, pos)
+
+ stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_samr_DomInfo12()"))
+ return pos, result
+end
+
+---Unmarshall a union with the following definition:
+--
+--
+-- typedef [switch_type(uint16)] union {
+-- [case(1)] samr_DomInfo1 info1;
+-- [case(2)] samr_DomInfo2 info2;
+-- [case(3)] samr_DomInfo3 info3;
+-- [case(4)] samr_DomInfo4 info4;
+-- [case(5)] samr_DomInfo5 info5;
+-- [case(6)] samr_DomInfo6 info6;
+-- [case(7)] samr_DomInfo7 info7;
+-- [case(8)] samr_DomInfo8 info8;
+-- [case(9)] samr_DomInfo9 info9;
+-- [case(11)] samr_DomInfo11 info11;
+-- [case(12)] samr_DomInfo12 info12;
+-- [case(13)] samr_DomInfo13 info13;
+-- } samr_DomainInfo;
+--
+--
+--@param data The data packet being processed.
+--@param pos The position within data.
+--@return (pos, result) The new position in data, and a table representing the datatype. May return
+-- nil if there was an error.
+function unmarshall_samr_DomainInfo(data, pos)
+ local level
+ local result
+ stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_samr_DomainInfo()"))
+
+ pos, level = unmarshall_int16(data, pos)
+
+ if(level == 1) then
+ pos, result = unmarshall_samr_DomInfo1(data, pos)
+ elseif(level == 8) then
+ pos, result = unmarshall_samr_DomInfo8(data, pos)
+ elseif(level == 12) then
+ pos, result = unmarshall_samr_DomInfo12(data, pos)
+ else
+ stdnse.print_debug(1, "MSRPC: ERROR: Server returned an unknown level for samr_DomainInfo: %d", level)
+ pos, result = nil, nil
+ end
+
+ stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_samr_DomainInfo()"))
+ return pos, result
+end
+
+---Unmarshall a pointer to a samr_DomainInfo. See unmarshall_samr_DomainInfo for
+-- more information.
+--
+--@param data The data packet being processed.
+--@param pos The position within data.
+--@return (pos, result) The new position in data, and a table representing the datatype. May return
+-- nil if there was an error.
+function unmarshall_samr_DomainInfo_ptr(data, pos)
+ local result
+ stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_samr_DomainInfo_ptr()"))
+
+ pos, result = unmarshall_ptr(ALL, data, pos, unmarshall_samr_DomainInfo, {})
+
+ stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_samr_DomainInfo_ptr()"))
+ return pos, result
+end
+
+
+
diff --git a/nselib/nmapdebug.lua b/nselib/nmapdebug.lua
new file mode 100644
index 000000000..bb294078f
--- /dev/null
+++ b/nselib/nmapdebug.lua
@@ -0,0 +1,62 @@
+--- Debugging functions for Nmap scripts.
+--
+-- This module contains various handy functions for debugging. These should
+-- never be used for actual results, only during testing.
+--
+-- @copyright Same as Nmap--See http://nmap.org/book/man-legal.html
+
+local require = require
+local type = type
+local pairs = pairs
+local nmap = require "nmap";
+local stdnse = require "stdnse";
+
+local EMPTY = {}; -- Empty constant table
+
+module(... or "nmapdebug");
+
+---Converts an arbitrary data type into a string. Will recursively convert
+-- tables. This can be very useful for debugging.
+--
+--@param data The data to convert.
+--@param indent (optional) The number of times to indent the line. Default
+-- is 0.
+--@return A string representation of a data, will be one or more full lines.
+function tostr(data, indent)
+ local str = ""
+
+ if(indent == nil) then
+ indent = 0
+ end
+
+ -- Check the type
+ if(type(data) == "nil") then
+ str = str .. (" "):rep(indent) .. data .. "\n"
+ elseif(type(data) == "string") then
+ str = str .. (" "):rep(indent) .. data .. "\n"
+ elseif(type(data) == "number") then
+ str = str .. (" "):rep(indent) .. data .. "\n"
+ elseif(type(data) == "boolean") then
+ if(data == true) then
+ str = str .. "true"
+ else
+ str = str .. "false"
+ end
+ elseif(type(data) == "table") then
+ local i, v
+ for i, v in pairs(data) do
+ -- Check for a table in a table
+ if(type(v) == "table") then
+ str = str .. (" "):rep(indent) .. i .. ":\n"
+ str = str .. tostr(v, indent + 2)
+ else
+ str = str .. (" "):rep(indent) .. i .. ": " .. tostr(v, 0)
+ end
+ end
+ else
+ stdnse.print_debug(1, "Error: unknown data type: %s", type(data))
+ end
+
+ return str
+end
+
diff --git a/nselib/smb.lua b/nselib/smb.lua
index 1969f5b0d..b82090b51 100644
--- a/nselib/smb.lua
+++ b/nselib/smb.lua
@@ -1,10 +1,18 @@
--- Server Message Block (SMB, also known as CIFS) traffic.
--
--- SMB traffic is normally
--- sent to/from ports 139 or 445 of Windows systems, although it's also implemented by
--- other systems (the most notable one being Samba).
+-- SMB traffic is normally sent to/from ports 139 or 445 of Windows systems, some of them
+-- properly and many of them not. Samba implements it, as do many printers and other embedded
+-- devices. Although the protocol has been documented decently well by Samba and others,
+-- many 3rd party implementations are broken or make assumptions.
+--
+-- Naturally, I do the same; however, that being said, this has been tested against every
+-- broken implementation we could find, and will accept (or fail gracefully) against any
+-- bad implementations we could find.
+--
+-- The intention of this library is to eventually handle all aspects of the SMB protocol.
+-- That being said, I'm only implementing the pieces that I find myself needing. If you
+-- require something more, let me know and I'll put it on my todo list.
--
--- The intention of this library is to eventually handle all aspects of the SMB protocol,
-- A programmer using this library must already have some knowledge of the SMB protocol,
-- although a lot isn't necessary. You can pick up a lot by looking at the code that uses
-- this. The basic login/logoff is this:
@@ -31,13 +39,14 @@
-- status, err = smb.negotiate_protocol(smbstate)
-- status, err = smb.start_session(smbstate)
-- status, err = smb.tree_connect(smbstate, path)
+-- ...
-- status, err = smb.tree_disconnect(smbstate)
-- status, err = smb.logoff(smbstate)
-- status, err = smb.stop(smbstate)
--
--
-- The stop function will automatically call tree_disconnect and logoff,
--- cleaning up the session.
+-- cleaning up the session, if it hasn't been done already.
--
-- To initially begin the connection, there are two options:
--
@@ -48,11 +57,11 @@
-- That packet requires the computer's name, which is requested
-- using a NBSTAT probe over UDP port 137.
--
--- Once it's connected, a SMB_COM_NEGOTIATE packet is sent,
--- requesting the protocol "NT LM 0.12", which is the most commonly
--- supported one. Among other things, the server's response contains
--- the host's security level, the system time, and the computer/domain
--- name.
+-- Once it's connected, a SMB_COM_NEGOTIATE packet is sent, requesting the protocol
+-- "NT LM 0.12", which is the most commonly supported one. Among other things, the server's
+-- response contains the host's security level, the system time, and the computer/domain name.
+-- Some systems will refuse to use that protocol and return "-1" or "1" instead of 0. If that's
+-- detected, we kill the connection (because the protocol following will be unexpected).
--
-- If that's successful, SMB_COM_SESSION_SETUP_ANDX is sent. It is essentially the logon
-- packet, where the username, domain, and password are sent to the server for verification.
@@ -67,13 +76,13 @@
-- any further (tree_connect might fail).
--
-- In terms of the login protocol, by default, we sent only NTLMv1 authentication, Lanman
--- isn't set. The reason for this is, NTLMv2 isn't supported by every system (and I don't know
--- how to do message signing on the v2 protocols), and doesn't have a significant security
--- advantage over NTLMv1 (the major change in NTLMv2 is incorporating a client challenge).
--- Lanman is horribly insecure, though, so I don't send it at all. These options can, however,
--- be overridden either through script parameters or registry settings [TODO].
+-- isn't sent at all. The reason for this is, NTLMv2 isn't supported by every system (and I
+-- don't know how to do message signing on the v2 protocols), and doesn't have a significant security
+-- advantage over NTLMv1 for performing single scans (the major change in NTLMv2 is incorporating
+-- a client challenge). Lanman is somewhat insecure, though, so I don't send it at all. These options
+-- can, however, be overridden either through script parameters or registry settings.
--
--- Lanman v1 is a fairly weak protocol, although it's still fairly difficult to reverse. NTLMv1 is a slightly more secure
+-- Lanman v1 is a fairly weak protocol, although it's still fairly difficult to break. NTLMv1 is a slightly more secure
-- protocol (although not much) -- it's also fairly difficult to reverse, though. Windows clients, by default send LMv1 and
-- NTLMv1 together, but every modern Windows server will accept NTLM alone, so I opted to use that. LMv2 and NTLMv2 are
-- slightly more secure, and they let the client specify random data (to help fight malicious servers with pre-
@@ -82,24 +91,8 @@
--
-- Another interesting aspect of the password hashing is that the original password isn't even necessary, the
-- password's hash can be used instead. This hash can be dumped from memory of a live system by tools such as
--- pwdump and fgdump, or read straight from the SAM file. This means that if a password file is recovered,
--- it doesn't even need to be cracked before it can be used here.
---
--- The response to SMB_COM_SESSION_SETUP_ANDX is fairly simple, containing a boolean for
--- success, along with the operating system and the lan manager name.
---
--- After a successful SMB_COM_SESSION_SETUP_ANDX has been made, a
--- SMB_COM_TREE_CONNECT_ANDX packet can be sent. This is what connects to a share.
--- The server responds to this with a boolean answer, and little more information.
---
--- Each share will either return STATUS_BAD_NETWORK_NAME if the share doesn't
--- exist, STATUS_ACCESS_DENIED if it exists but we don't have access, or
--- STATUS_SUCCESS if exists and we do have access. STATUS_ACCESS_DENIED is also returned
--- if the server requires message signing and we don't return a valid signature.
---
--- Once we're connected to a share, we can start doing other operations like reading/writing files
--- or calling RPC functions. Calling RPC functions is the interesting part, and it's done through
--- the SMB_TRANS packet. The actual RPC protocol is built on top of the SMB protocol.
+-- pwdump and fgdump, or read straight from the SAM file (maybe some day, I'll do an Nmap script to dump it).
+-- This means that if a password file is recovered, it doesn't even need to be cracked before it can be used here.
--
-- Thanks go to Christopher R. Hertel and his book Implementing CIFS, which
-- taught me everything I know about Microsoft's protocols. Additionally, I used Samba's
@@ -109,7 +102,6 @@
--
-- Scripts that use this module can use the script arguments
-- smbusername, smbpassword, smbhash,
--- smbguest, and smbtype, described below. Here's an
-- example of using these script arguments:
--
-- nmap --script=smb-