From ea42f39faaad5646a5ad5bc9685d0ce78cc4eec6 Mon Sep 17 00:00:00 2001 From: ron Date: Sun, 7 Dec 2008 16:16:11 +0000 Subject: [PATCH] Merged in significant changes to Microsoft RPC calls --- CHANGELOG | 7 + nselib/msrpc.lua | 1909 +++++----------- nselib/msrpctypes.lua | 3921 +++++++++++++++++++++++++++++++++ nselib/nmapdebug.lua | 62 + nselib/smb.lua | 118 +- nselib/stdnse.lua | 1 + scripts/smb-check-vulns.nse | 180 +- scripts/smb-enum-domains.nse | 83 +- scripts/smb-enum-sessions.nse | 125 +- scripts/smb-enum-shares.nse | 58 +- scripts/smb-enum-users.nse | 238 +- scripts/smb-os-discovery.nse | 52 +- scripts/smb-security-mode.nse | 32 +- scripts/smb-server-stats.nse | 22 +- scripts/smb-system-info.nse | 14 +- 15 files changed, 5107 insertions(+), 1715 deletions(-) create mode 100644 nselib/msrpctypes.lua create mode 100644 nselib/nmapdebug.lua 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("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-