mirror of
https://github.com/nmap/nmap.git
synced 2025-12-19 05:59:01 +00:00
Merging changes from my experimental branch; the new versions of this scripts, which have significant changes to their core functionality, managed to hold their own against Brandon's network. More testing would be very helpful, though, especially with credentials (most of Brandon's scans were anonymous).
This commit is contained in:
@@ -1,5 +1,9 @@
|
|||||||
# Nmap Changelog ($Id$); -*-text-*-
|
# Nmap Changelog ($Id$); -*-text-*-
|
||||||
|
|
||||||
|
o Added smb-enum-processes.nse, a script that allows a user with administrator
|
||||||
|
credentials to view a tree of the processes running on the remote system
|
||||||
|
(uses HKEY_PERFORMANCE_DATA hive). [Ron Bowes]
|
||||||
|
|
||||||
o A problem that caused OS detection to fail for most hosts in a
|
o A problem that caused OS detection to fail for most hosts in a
|
||||||
certain was fixed. It happened when sending raw Ethernet frames
|
certain was fixed. It happened when sending raw Ethernet frames
|
||||||
(by default on Windows or on other platforms with --send-eth) to
|
(by default on Windows or on other platforms with --send-eth) to
|
||||||
|
|||||||
@@ -436,8 +436,11 @@ int process_mainloop(lua_State *L) {
|
|||||||
while (!running_scripts.empty()) {
|
while (!running_scripts.empty()) {
|
||||||
current = *(running_scripts.begin());
|
current = *(running_scripts.begin());
|
||||||
|
|
||||||
if (current.rr.host->timedOut(&now))
|
if (current.rr.host->timedOut(&now)) {
|
||||||
state = LUA_ERRRUN;
|
printf("thread (%p) timed out\n", (void *) current.thread);
|
||||||
|
SCRIPT_ENGINE_TRY(process_finalize(L, current.registry_idx));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
state = lua_resume(current.thread, current.resume_arguments);
|
state = lua_resume(current.thread, current.resume_arguments);
|
||||||
|
|
||||||
|
|||||||
@@ -198,3 +198,4 @@ aaliyah1
|
|||||||
|
|
||||||
zxcvbnm1
|
zxcvbnm1
|
||||||
young1
|
young1
|
||||||
|
test
|
||||||
|
|||||||
464
nselib/msrpc.lua
464
nselib/msrpc.lua
@@ -81,6 +81,26 @@ 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. :)
|
-- 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
|
REFERENT_ID = 0x50414d4e
|
||||||
|
|
||||||
|
-- The maximum length of a packet fragment
|
||||||
|
MAX_FRAGMENT = 0x800
|
||||||
|
|
||||||
|
---The number of SAMR records to pull at once. This was originally 1, but since I've written
|
||||||
|
-- proper fragmentation code, I've successfully done it with 110 users, although I'd be surprised
|
||||||
|
-- if you couldn't go a lot higher. I had some issues that I suspect was UNIX truncating packets,
|
||||||
|
-- so I scaled it back.
|
||||||
|
local SAMR_GROUPSIZE = 20
|
||||||
|
|
||||||
|
---The number of LSA RIDs to check at once. I've successfully tested with up to about 110. Note that
|
||||||
|
-- due to very long message sizes, Wireshark might truncate packets if you have more than 30 together,
|
||||||
|
-- so for debugging, setting this to 30 might be a plan. Like SAMR, I scaled this back due to UNIX
|
||||||
|
-- truncation.
|
||||||
|
local LSA_GROUPSIZE = 20
|
||||||
|
|
||||||
|
---The number of consecutive empty groups to stop after. Basically, this means that after
|
||||||
|
-- <code>LSA_MINEMPTY</code> groups of <code>LSA_GROUPSIZE</code> users come back empty, we give
|
||||||
|
-- up. Raising this could find more users, but at the expense of more packets.
|
||||||
|
local LSA_MINEMPTY = 10
|
||||||
|
|
||||||
--- This is a wrapper around the SMB class, designed to get SMB going quickly for MSRPC calls. This will
|
--- 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
|
-- 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
|
-- open the named pipe given by 'path'. When this successfully returns, the 'smbstate' table can be immediately
|
||||||
@@ -180,8 +200,8 @@ function bind(smbstate, interface_uuid, interface_version, transfer_syntax)
|
|||||||
0x0048, -- Frag length
|
0x0048, -- Frag length
|
||||||
0x0000, -- Auth length
|
0x0000, -- Auth length
|
||||||
0x41414141, -- Call ID (I use 'AAAA' because it's easy to recognize)
|
0x41414141, -- Call ID (I use 'AAAA' because it's easy to recognize)
|
||||||
0x10b8, -- Max transmit frag
|
MAX_FRAGMENT, -- Max transmit frag
|
||||||
0x10b8, -- Max receive frag
|
MAX_FRAGMENT, -- Max receive frag
|
||||||
0x00000000, -- Assoc group
|
0x00000000, -- Assoc group
|
||||||
0x01, -- Number of items
|
0x01, -- Number of items
|
||||||
0x00, -- Padding/alignment
|
0x00, -- Padding/alignment
|
||||||
@@ -200,7 +220,12 @@ function bind(smbstate, interface_uuid, interface_version, transfer_syntax)
|
|||||||
2 -- Syntax version
|
2 -- Syntax version
|
||||||
)
|
)
|
||||||
|
|
||||||
status, result = smb.send_transaction(smbstate, 0x0026, "", data)
|
status, result = smb.write_file(smbstate, data, 0)
|
||||||
|
if(status ~= true) then
|
||||||
|
return false, result
|
||||||
|
end
|
||||||
|
|
||||||
|
status, result = smb.read_file(smbstate, 0, MAX_FRAGMENT)
|
||||||
if(status ~= true) then
|
if(status ~= true) then
|
||||||
return false, result
|
return false, result
|
||||||
end
|
end
|
||||||
@@ -213,6 +238,9 @@ function bind(smbstate, interface_uuid, interface_version, transfer_syntax)
|
|||||||
|
|
||||||
-- Extract the first part from the resposne
|
-- Extract the first part from the resposne
|
||||||
pos, result['version_major'], result['version_minor'], result['packet_type'], result['packet_flags'], result['data_representation'], result['frag_length'], result['auth_length'], result['call_id'] = bin.unpack("<CCCC>I<SSI", data)
|
pos, result['version_major'], result['version_minor'], result['packet_type'], result['packet_flags'], result['data_representation'], result['frag_length'], result['auth_length'], result['call_id'] = bin.unpack("<CCCC>I<SSI", data)
|
||||||
|
if(result['call_id'] == nil) then
|
||||||
|
return false, "MSRPC: ERROR: Ran off the end of SMB packet; likely due to server truncation"
|
||||||
|
end
|
||||||
|
|
||||||
-- Check if the packet tyep was a fault
|
-- Check if the packet tyep was a fault
|
||||||
if(result['packet_type'] == 0x03) then -- MSRPC_FAULT
|
if(result['packet_type'] == 0x03) then -- MSRPC_FAULT
|
||||||
@@ -241,13 +269,22 @@ function bind(smbstate, interface_uuid, interface_version, transfer_syntax)
|
|||||||
|
|
||||||
-- If we made it this far, then we have a valid Bind() result. Pull out some more parameters.
|
-- 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)
|
pos, result['max_transmit_frag'], result['max_receive_frag'], result['assoc_group'], result['secondary_address_length'] = bin.unpack("SSIS", data, pos)
|
||||||
|
if(result['secondary_address_length'] == nil) then
|
||||||
|
return false, "MSRPC: ERROR: Ran off the end of SMB packet; likely due to server truncation"
|
||||||
|
end
|
||||||
|
|
||||||
-- Read the secondary address
|
-- Read the secondary address
|
||||||
pos, result['secondary_address'] = bin.unpack(string.format("<A%d", result['secondary_address_length']), data, pos)
|
pos, result['secondary_address'] = bin.unpack(string.format("<A%d", result['secondary_address_length']), data, pos)
|
||||||
|
if(result['secondary_address'] == nil) then
|
||||||
|
return false, "MSRPC: ERROR: Ran off the end of SMB packet; likely due to server truncation"
|
||||||
|
end
|
||||||
pos = pos + ((4 - ((pos - 1) % 4)) % 4); -- Alignment -- don't ask how I came up with this, it was a lot of drawing, and there's probably a far better way
|
pos = pos + ((4 - ((pos - 1) % 4)) % 4); -- Alignment -- don't ask how I came up with this, it was a lot of drawing, and there's probably a far better way
|
||||||
|
|
||||||
-- Read the number of results
|
-- Read the number of results
|
||||||
pos, result['num_results'] = bin.unpack("<C", data, pos)
|
pos, result['num_results'] = bin.unpack("<C", data, pos)
|
||||||
|
if(result['num_results'] == nil) then
|
||||||
|
return false, "MSRPC: ERROR: Ran off the end of SMB packet; likely due to server truncation"
|
||||||
|
end
|
||||||
pos = pos + ((4 - ((pos - 1) % 4)) % 4); -- Alignment
|
pos = pos + ((4 - ((pos - 1) % 4)) % 4); -- Alignment
|
||||||
|
|
||||||
-- Verify we got back what we expected
|
-- Verify we got back what we expected
|
||||||
@@ -257,6 +294,9 @@ function bind(smbstate, interface_uuid, interface_version, transfer_syntax)
|
|||||||
|
|
||||||
-- Read in the last bits
|
-- Read in the last bits
|
||||||
pos, result['ack_result'], result['align'], result['transfer_syntax'], result['syntax_version'] = bin.unpack("<SSA16I", data, pos)
|
pos, result['ack_result'], result['align'], result['transfer_syntax'], result['syntax_version'] = bin.unpack("<SSA16I", data, pos)
|
||||||
|
if(result['syntax_version'] == nil) then
|
||||||
|
return false, "MSRPC: ERROR: Ran off the end of SMB packet; likely due to server truncation"
|
||||||
|
end
|
||||||
|
|
||||||
return true, result
|
return true, result
|
||||||
end
|
end
|
||||||
@@ -275,13 +315,17 @@ end
|
|||||||
--@param opnum The operating number (ie, the function). Find this in the MSRPC documentation or with a packet logger.
|
--@param opnum The operating number (ie, the function). Find this in the MSRPC documentation or with a packet logger.
|
||||||
--@param arguments The marshalled arguments to pass to the function. Currently, marshalling is all done manually.
|
--@param arguments The marshalled arguments to pass to the function. Currently, marshalling is all done manually.
|
||||||
--@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values, the most
|
--@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values, the most
|
||||||
-- useful one being 'arguments', which are the values returned by the server.
|
-- useful one being 'arguments', which are the values returned by the server. If the packet is fragmented, the fragments
|
||||||
|
-- will be reassembled and 'arguments' will represent all the arguments; however, the rest of the result table will represent
|
||||||
|
-- the most recent fragment.
|
||||||
local function call_function(smbstate, opnum, arguments)
|
local function call_function(smbstate, opnum, arguments)
|
||||||
local i
|
local i
|
||||||
local status, result
|
local status, result
|
||||||
local parameters, data
|
local parameters, data
|
||||||
local pos, align
|
local pos, align
|
||||||
local result
|
local result
|
||||||
|
local first = true
|
||||||
|
local is_first, is_last
|
||||||
|
|
||||||
data = bin.pack("<CCCC>I<SSIISSA",
|
data = bin.pack("<CCCC>I<SSIISSA",
|
||||||
0x05, -- Version (major)
|
0x05, -- Version (major)
|
||||||
@@ -301,7 +345,16 @@ local function call_function(smbstate, opnum, arguments)
|
|||||||
stdnse.print_debug(3, "MSRPC: Calling function 0x%02x with %d bytes of arguments", string.len(arguments), opnum)
|
stdnse.print_debug(3, "MSRPC: Calling function 0x%02x with %d bytes of arguments", string.len(arguments), opnum)
|
||||||
|
|
||||||
-- Pass the information up to the smb layer
|
-- Pass the information up to the smb layer
|
||||||
status, result = smb.send_transaction(smbstate, 0x0026, "", data)
|
status, result = smb.write_file(smbstate, data, 0)
|
||||||
|
if(status ~= true) then
|
||||||
|
return false, result
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Loop over the fragments
|
||||||
|
local arguments = ""
|
||||||
|
repeat
|
||||||
|
-- Read the information from the smb layer
|
||||||
|
status, result = smb.read_file(smbstate, 0, 0x1001)
|
||||||
if(status ~= true) then
|
if(status ~= true) then
|
||||||
return false, result
|
return false, result
|
||||||
end
|
end
|
||||||
@@ -312,6 +365,23 @@ local function call_function(smbstate, opnum, arguments)
|
|||||||
|
|
||||||
-- Extract the first part from the resposne
|
-- Extract the first part from the resposne
|
||||||
pos, result['version_major'], result['version_minor'], result['packet_type'], result['packet_flags'], result['data_representation'], result['frag_length'], result['auth_length'], result['call_id'] = bin.unpack("<CCCC>I<SSI", data)
|
pos, result['version_major'], result['version_minor'], result['packet_type'], result['packet_flags'], result['data_representation'], result['frag_length'], result['auth_length'], result['call_id'] = bin.unpack("<CCCC>I<SSI", data)
|
||||||
|
if(result['call_id'] == nil) then
|
||||||
|
return false, "MSRPC: ERROR: Ran off the end of SMB packet; likely due to server truncation"
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Check if we're fragmented
|
||||||
|
is_first = (bit.band(result['packet_flags'], 0x01) == 0x01)
|
||||||
|
is_last = (bit.band(result['packet_flags'], 0x02) == 0x02)
|
||||||
|
|
||||||
|
-- We have a fragmented packet, make sure it's the first (if we're on the first)
|
||||||
|
if(first == true and is_first == false) then
|
||||||
|
return false, "MSRPC: First fragment doesn't have proper 'first' (0x01) flag set"
|
||||||
|
end
|
||||||
|
|
||||||
|
-- We have a fragmented packet, make sure it isn't the first (if we aren't on the first)
|
||||||
|
if(first == false and is_first) then
|
||||||
|
return false, "MSRPC: Middle (or last) fragment doesn't have proper 'first' (0x01) flag set"
|
||||||
|
end
|
||||||
|
|
||||||
-- Check if there was an error
|
-- Check if there was an error
|
||||||
if(result['packet_type'] == 0x03) then -- MSRPC_FAULT
|
if(result['packet_type'] == 0x03) then -- MSRPC_FAULT
|
||||||
@@ -323,9 +393,6 @@ local function call_function(smbstate, opnum, arguments)
|
|||||||
if(result['auth_length'] ~= 0) then
|
if(result['auth_length'] ~= 0) then
|
||||||
return false, "MSRPC call returned an 'auth length', which we don't know how to deal with"
|
return false, "MSRPC call returned an 'auth length', which we don't know how to deal with"
|
||||||
end
|
end
|
||||||
if(bit.band(result['packet_flags'], 0x03) ~= 0x03) then
|
|
||||||
return false, "MSRPC call returned a fragmented packet, which we don't know how to handle"
|
|
||||||
end
|
|
||||||
if(result['packet_type'] ~= 0x02) then
|
if(result['packet_type'] ~= 0x02) then
|
||||||
return false, "MSRPC call returned an unexpected packet type (not RESPONSE)"
|
return false, "MSRPC call returned an unexpected packet type (not RESPONSE)"
|
||||||
end
|
end
|
||||||
@@ -335,13 +402,22 @@ local function call_function(smbstate, opnum, arguments)
|
|||||||
|
|
||||||
-- Extract some more
|
-- Extract some more
|
||||||
pos, result['alloc_hint'], result['context_id'], result['cancel_count'], align = bin.unpack("<ISCC", data, pos)
|
pos, result['alloc_hint'], result['context_id'], result['cancel_count'], align = bin.unpack("<ISCC", data, pos)
|
||||||
|
if(align == nil) then
|
||||||
|
return false, "MSRPC: ERROR: Ran off the end of SMB packet; likely due to server truncation"
|
||||||
|
end
|
||||||
|
|
||||||
-- Rest is the arguments
|
-- Rest is the arguments
|
||||||
result['arguments'] = string.sub(data, pos)
|
arguments = arguments .. string.sub(data, pos)
|
||||||
|
|
||||||
|
-- No longer the 'first'
|
||||||
|
first = false
|
||||||
|
until is_last == true
|
||||||
|
|
||||||
|
result['arguments'] = arguments
|
||||||
|
|
||||||
stdnse.print_debug(3, "MSRPC: Function call successful, %d bytes of returned argumenst", string.len(result['arguments']))
|
stdnse.print_debug(3, "MSRPC: Function call successful, %d bytes of returned argumenst", string.len(result['arguments']))
|
||||||
|
|
||||||
return true, result
|
return true, result
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
---A proxy to a <code>msrpctypes</code> function that converts a ShareType to an english string.
|
---A proxy to a <code>msrpctypes</code> function that converts a ShareType to an english string.
|
||||||
@@ -1054,16 +1130,21 @@ end
|
|||||||
--@param smbstate The SMB state table
|
--@param smbstate The SMB state table
|
||||||
--@param domain_handle The domain handle, returned by <code>samr_opendomain</code>
|
--@param domain_handle The domain handle, returned by <code>samr_opendomain</code>
|
||||||
--@param index The index of the user to check; the first user is 0, next is 1, etc.
|
--@param index The index of the user to check; the first user is 0, next is 1, etc.
|
||||||
|
--@param count [optional] The number of users to return; you may want to be careful about going too high. Default: 1.
|
||||||
--@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values, the most
|
--@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
|
-- 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,
|
-- '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.
|
-- '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, index)
|
function samr_querydisplayinfo(smbstate, domain_handle, index, count)
|
||||||
local i, j
|
local i, j
|
||||||
local status, result
|
local status, result
|
||||||
local arguments
|
local arguments
|
||||||
local pos, align
|
local pos, align
|
||||||
|
|
||||||
|
if(count == nil) then
|
||||||
|
count = 1
|
||||||
|
end
|
||||||
|
|
||||||
-- 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,
|
-- 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 put a little loop here and grab the names individually.
|
||||||
stdnse.print_debug(2, "MSRPC: Calling QueryDisplayInfo(%d) [%s]", index, smbstate['ip'])
|
stdnse.print_debug(2, "MSRPC: Calling QueryDisplayInfo(%d) [%s]", index, smbstate['ip'])
|
||||||
@@ -1078,10 +1159,10 @@ function samr_querydisplayinfo(smbstate, domain_handle, index)
|
|||||||
arguments = arguments .. msrpctypes.marshall_int32(index)
|
arguments = arguments .. msrpctypes.marshall_int32(index)
|
||||||
|
|
||||||
-- [in] uint32 max_entries,
|
-- [in] uint32 max_entries,
|
||||||
arguments = arguments .. msrpctypes.marshall_int32(1)
|
arguments = arguments .. msrpctypes.marshall_int32(count)
|
||||||
|
|
||||||
-- [in] uint32 buf_size,
|
-- [in] uint32 buf_size,
|
||||||
arguments = arguments .. msrpctypes.marshall_int32(0)
|
arguments = arguments .. msrpctypes.marshall_int32(0x7FFFFFFF)
|
||||||
|
|
||||||
-- [out] uint32 total_size,
|
-- [out] uint32 total_size,
|
||||||
-- [out] uint32 returned_size,
|
-- [out] uint32 returned_size,
|
||||||
@@ -1110,7 +1191,6 @@ function samr_querydisplayinfo(smbstate, domain_handle, index)
|
|||||||
|
|
||||||
-- [out] uint32 returned_size,
|
-- [out] uint32 returned_size,
|
||||||
pos, result['returned_size'] = msrpctypes.unmarshall_int32(arguments, pos)
|
pos, result['returned_size'] = msrpctypes.unmarshall_int32(arguments, pos)
|
||||||
|
|
||||||
-- [out,switch_is(level)] samr_DispInfo info
|
-- [out,switch_is(level)] samr_DispInfo info
|
||||||
pos, result['info'] = msrpctypes.unmarshall_samr_DispInfo(arguments, pos)
|
pos, result['info'] = msrpctypes.unmarshall_samr_DispInfo(arguments, pos)
|
||||||
if(pos == nil) then
|
if(pos == nil) then
|
||||||
@@ -1966,10 +2046,10 @@ function winreg_queryvalue(smbstate, handle, value)
|
|||||||
arguments = arguments .. msrpctypes.marshall_winreg_Type_ptr("REG_NONE")
|
arguments = arguments .. msrpctypes.marshall_winreg_Type_ptr("REG_NONE")
|
||||||
|
|
||||||
-- [in,out,size_is(*size),length_is(*length)] uint8 *data,
|
-- [in,out,size_is(*size),length_is(*length)] uint8 *data,
|
||||||
arguments = arguments .. msrpctypes.marshall_int8_array_ptr("", 520)
|
arguments = arguments .. msrpctypes.marshall_int8_array_ptr("", 1000000)
|
||||||
|
|
||||||
-- [in,out] uint32 *size,
|
-- [in,out] uint32 *size,
|
||||||
arguments = arguments .. msrpctypes.marshall_int32_ptr(520)
|
arguments = arguments .. msrpctypes.marshall_int32_ptr(1000000)
|
||||||
|
|
||||||
-- [in,out] uint32 *length
|
-- [in,out] uint32 *length
|
||||||
arguments = arguments .. msrpctypes.marshall_int32_ptr(0)
|
arguments = arguments .. msrpctypes.marshall_int32_ptr(0)
|
||||||
@@ -1991,9 +2071,7 @@ function winreg_queryvalue(smbstate, handle, value)
|
|||||||
-- [in,ref] policy_handle *handle,
|
-- [in,ref] policy_handle *handle,
|
||||||
-- [in] winreg_String value_name,
|
-- [in] winreg_String value_name,
|
||||||
-- [in,out] winreg_Type *type,
|
-- [in,out] winreg_Type *type,
|
||||||
pos,
|
pos, result['type'] = msrpctypes.unmarshall_winreg_Type_ptr(arguments, pos)
|
||||||
pos = pos + 4
|
|
||||||
pos, result['type'] = msrpctypes.unmarshall_winreg_Type(arguments, pos)
|
|
||||||
|
|
||||||
-- [in,out,size_is(*size),length_is(*length)] uint8 *data,
|
-- [in,out,size_is(*size),length_is(*length)] uint8 *data,
|
||||||
pos, result['data'] = msrpctypes.unmarshall_int8_array_ptr(arguments, pos)
|
pos, result['data'] = msrpctypes.unmarshall_int8_array_ptr(arguments, pos)
|
||||||
@@ -2004,6 +2082,8 @@ function winreg_queryvalue(smbstate, handle, value)
|
|||||||
_, result['value'] = bin.unpack("<I", result['data'])
|
_, result['value'] = bin.unpack("<I", result['data'])
|
||||||
elseif(result['type'] == "REG_SZ" or result['type'] == "REG_MULTI_SZ" or result['type'] == "REG_EXPAND_SZ") then
|
elseif(result['type'] == "REG_SZ" or result['type'] == "REG_MULTI_SZ" or result['type'] == "REG_EXPAND_SZ") then
|
||||||
_, result['value'] = msrpctypes.unicode_to_string(result['data'], 1, #result['data'] / 2)
|
_, result['value'] = msrpctypes.unicode_to_string(result['data'], 1, #result['data'] / 2)
|
||||||
|
elseif(result['type'] == "REG_BINARY") then
|
||||||
|
result['value'] = result['data']
|
||||||
else
|
else
|
||||||
stdnse.print_debug("MSRPC ERROR: Unknown type: %s\n\n", result['type'])
|
stdnse.print_debug("MSRPC ERROR: Unknown type: %s\n\n", result['type'])
|
||||||
result['value'] = result['type']
|
result['value'] = result['type']
|
||||||
@@ -2019,6 +2099,7 @@ function winreg_queryvalue(smbstate, handle, value)
|
|||||||
pos, result['length'] = msrpctypes.unmarshall_int32_ptr(arguments, pos)
|
pos, result['length'] = msrpctypes.unmarshall_int32_ptr(arguments, pos)
|
||||||
|
|
||||||
pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos)
|
pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos)
|
||||||
|
|
||||||
if(result['return'] == nil) then
|
if(result['return'] == nil) then
|
||||||
return false, "Read off the end of the packet (winreg.queryvalue)"
|
return false, "Read off the end of the packet (winreg.queryvalue)"
|
||||||
end
|
end
|
||||||
@@ -2075,3 +2156,348 @@ function winreg_closekey(smbstate, handle)
|
|||||||
return true, result
|
return true, result
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---Attempt to enumerate users using SAMR functions.
|
||||||
|
--
|
||||||
|
--@param host The host object.
|
||||||
|
--@return (status, result) If status is false, result is an error message. Otherwise, result
|
||||||
|
-- is an array of tables, each of which contain the following fields:
|
||||||
|
-- * name
|
||||||
|
-- * fullname
|
||||||
|
-- * description
|
||||||
|
-- * rid
|
||||||
|
-- * domain
|
||||||
|
-- * typestr
|
||||||
|
-- * source
|
||||||
|
-- * flags[]
|
||||||
|
function samr_enum_users(host)
|
||||||
|
local i, j
|
||||||
|
|
||||||
|
stdnse.print_debug(3, "Entering enum_samr()")
|
||||||
|
|
||||||
|
local smbstate
|
||||||
|
local bind_result, connect4_result, enumdomains_result
|
||||||
|
local connect_handle
|
||||||
|
local status, smbstate
|
||||||
|
local response = {}
|
||||||
|
|
||||||
|
-- Create the SMB session
|
||||||
|
status, smbstate = msrpc.start_smb(host, msrpc.SAMR_PATH)
|
||||||
|
|
||||||
|
if(status == false) then
|
||||||
|
return false, smbstate
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Bind to SAMR service
|
||||||
|
status, bind_result = msrpc.bind(smbstate, msrpc.SAMR_UUID, msrpc.SAMR_VERSION, nil)
|
||||||
|
if(status == false) then
|
||||||
|
msrpc.stop_smb(smbstate)
|
||||||
|
return false, bind_result
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Call connect4()
|
||||||
|
status, connect4_result = msrpc.samr_connect4(smbstate, host.ip)
|
||||||
|
if(status == false) then
|
||||||
|
msrpc.stop_smb(smbstate)
|
||||||
|
return false, connect4_result
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Save the connect_handle
|
||||||
|
connect_handle = connect4_result['connect_handle']
|
||||||
|
|
||||||
|
-- Call EnumDomains()
|
||||||
|
status, enumdomains_result = msrpc.samr_enumdomains(smbstate, connect_handle)
|
||||||
|
if(status == false) then
|
||||||
|
msrpc.stop_smb(smbstate)
|
||||||
|
return false, enumdomains_result
|
||||||
|
end
|
||||||
|
|
||||||
|
-- If no domains were returned, go back with an error
|
||||||
|
if(#enumdomains_result['sam']['entries'] == 0) then
|
||||||
|
msrpc.stop_smb(smbstate)
|
||||||
|
return false, "Couldn't find any domains"
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Now, loop through the domains and find the users
|
||||||
|
for i = 1, #enumdomains_result['sam']['entries'], 1 do
|
||||||
|
|
||||||
|
local domain = enumdomains_result['sam']['entries'][i]['name']
|
||||||
|
-- We don't care about the 'builtin' domain, in all my tests it's empty
|
||||||
|
if(domain ~= 'Builtin') then
|
||||||
|
local sid
|
||||||
|
local domain_handle
|
||||||
|
local opendomain_result, querydisplayinfo_result
|
||||||
|
|
||||||
|
-- Call LookupDomain()
|
||||||
|
status, lookupdomain_result = msrpc.samr_lookupdomain(smbstate, connect_handle, domain)
|
||||||
|
if(status == false) then
|
||||||
|
msrpc.stop_smb(smbstate)
|
||||||
|
return false, lookupdomain_result
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Save the sid
|
||||||
|
sid = lookupdomain_result['sid']
|
||||||
|
|
||||||
|
-- Call OpenDomain()
|
||||||
|
status, opendomain_result = msrpc.samr_opendomain(smbstate, connect_handle, sid)
|
||||||
|
if(status == false) then
|
||||||
|
msrpc.stop_smb(smbstate)
|
||||||
|
return false, opendomain_result
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Save the domain handle
|
||||||
|
domain_handle = opendomain_result['domain_handle']
|
||||||
|
|
||||||
|
-- Loop as long as we're getting valid results
|
||||||
|
j = 0
|
||||||
|
repeat
|
||||||
|
-- Call QueryDisplayInfo()
|
||||||
|
status, querydisplayinfo_result = msrpc.samr_querydisplayinfo(smbstate, domain_handle, j, SAMR_GROUPSIZE)
|
||||||
|
if(status == false) then
|
||||||
|
msrpc.stop_smb(smbstate)
|
||||||
|
return false, querydisplayinfo_result
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Save the response
|
||||||
|
if(querydisplayinfo_result['info'] ~= nil and querydisplayinfo_result['info']['entries'] ~= nil) then
|
||||||
|
local k
|
||||||
|
for k = 1, #querydisplayinfo_result['info']['entries'], 1 do
|
||||||
|
local array = {}
|
||||||
|
local l
|
||||||
|
|
||||||
|
-- The reason these are all indexed from '1' is because we request names one at a time.
|
||||||
|
array['name'] = querydisplayinfo_result['info']['entries'][k]['account_name']
|
||||||
|
array['fullname'] = querydisplayinfo_result['info']['entries'][k]['full_name']
|
||||||
|
array['description'] = querydisplayinfo_result['info']['entries'][k]['description']
|
||||||
|
array['rid'] = querydisplayinfo_result['info']['entries'][k]['rid']
|
||||||
|
array['domain'] = domain
|
||||||
|
array['type'] = 'SID_NAME_USER'
|
||||||
|
array['typestr'] = 'User'
|
||||||
|
array['source'] = 'SAMR Enumeration'
|
||||||
|
array['flags'] = querydisplayinfo_result['info']['entries'][k]['acct_flags']
|
||||||
|
|
||||||
|
-- Convert each element in the 'flags' array into the equivalent string
|
||||||
|
for l = 1, #array['flags'], 1 do
|
||||||
|
array['flags'][l] = msrpc.samr_AcctFlags_tostr(array['flags'][l])
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Add it to the array
|
||||||
|
response[#response + 1] = array
|
||||||
|
end
|
||||||
|
end
|
||||||
|
j = j + SAMR_GROUPSIZE
|
||||||
|
until querydisplayinfo_result['return'] == 0
|
||||||
|
|
||||||
|
-- Close the domain handle
|
||||||
|
msrpc.samr_close(smbstate, domain_handle)
|
||||||
|
end -- Checking for 'builtin'
|
||||||
|
end -- Domain loop
|
||||||
|
|
||||||
|
-- Close the connect handle
|
||||||
|
msrpc.samr_close(smbstate, connect_handle)
|
||||||
|
|
||||||
|
-- Stop the SAMR SMB
|
||||||
|
msrpc.stop_smb(smbstate)
|
||||||
|
|
||||||
|
stdnse.print_debug(3, "Leaving enum_samr()")
|
||||||
|
|
||||||
|
return true, response
|
||||||
|
end
|
||||||
|
|
||||||
|
---Attempt to enumerate users using LSA functions.
|
||||||
|
--
|
||||||
|
--@param host The host object.
|
||||||
|
--@return status, result -- if status is false, result is an error message; otherwise, result is
|
||||||
|
-- an array of tables, each containing the following elements:
|
||||||
|
-- * name
|
||||||
|
-- * rid
|
||||||
|
-- * domain
|
||||||
|
-- * typestr
|
||||||
|
-- * source
|
||||||
|
function lsa_enum_users(host)
|
||||||
|
|
||||||
|
local smbstate
|
||||||
|
local response = {}
|
||||||
|
local status, smbstate, bind_result, openpolicy2_result, lookupnames2_result, lookupsids2_result
|
||||||
|
|
||||||
|
stdnse.print_debug(3, "Entering enum_lsa()")
|
||||||
|
|
||||||
|
-- Create the SMB session
|
||||||
|
status, smbstate = msrpc.start_smb(host, msrpc.LSA_PATH)
|
||||||
|
if(status == false) then
|
||||||
|
return false, smbstate
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Bind to LSA service
|
||||||
|
status, bind_result = msrpc.bind(smbstate, msrpc.LSA_UUID, msrpc.LSA_VERSION, nil)
|
||||||
|
if(status == false) then
|
||||||
|
msrpc.stop_smb(smbstate)
|
||||||
|
return false, bind_result
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Open the LSA policy
|
||||||
|
status, openpolicy2_result = msrpc.lsa_openpolicy2(smbstate, host.ip)
|
||||||
|
if(status == false) then
|
||||||
|
msrpc.stop_smb(smbstate)
|
||||||
|
return false, openpolicy2_result
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Start with some common names, as well as the name returned by the negotiate call
|
||||||
|
-- Vista doesn't like a 'null' after the server name, so fix that (TODO: the way I strip the null here feels hackish, is there a better way?)
|
||||||
|
names = {"administrator", "guest", "test", smbstate['domain'], string.sub(smbstate['server'], 1, #smbstate['server'] - 1) }
|
||||||
|
|
||||||
|
-- Get the server's name from nbstat
|
||||||
|
local result, server_name = netbios.get_server_name(host.ip)
|
||||||
|
if(result == true) then
|
||||||
|
names[#names + 1] = server_name
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Get the logged in user from nbstat
|
||||||
|
local result, user_name = netbios.get_user_name(host.ip)
|
||||||
|
if(result == true) then
|
||||||
|
names[#names + 1] = user_name
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Look up the names, if any are valid than the server's SID will be returned
|
||||||
|
status, lookupnames2_result = msrpc.lsa_lookupnames2(smbstate, openpolicy2_result['policy_handle'], names)
|
||||||
|
if(status == false) then
|
||||||
|
msrpc.stop_smb(smbstate)
|
||||||
|
return false, lookupnames2_result
|
||||||
|
end
|
||||||
|
-- Loop through the domains returned and find the users in each
|
||||||
|
for i = 1, #lookupnames2_result['domains']['domains'], 1 do
|
||||||
|
local domain = lookupnames2_result['domains']['domains'][i]['name']
|
||||||
|
local sid = lookupnames2_result['domains']['domains'][i]['sid']
|
||||||
|
local sids = { }
|
||||||
|
|
||||||
|
-- Start by looking up 500 and up
|
||||||
|
for j = 500, 500 + LSA_GROUPSIZE, 1 do
|
||||||
|
sids[#sids + 1] = sid .. "-" .. j
|
||||||
|
end
|
||||||
|
|
||||||
|
status, lookupsids2_result = msrpc.lsa_lookupsids2(smbstate, openpolicy2_result['policy_handle'], sids)
|
||||||
|
if(status == false) then
|
||||||
|
stdnse.print_debug(1, string.format("Error looking up RIDs: %s", lookupsids2_result))
|
||||||
|
else
|
||||||
|
-- Put the details for each name into an array
|
||||||
|
-- NOTE: Be sure to mirror any changes here in the next bit!
|
||||||
|
for j = 1, #lookupsids2_result['names']['names'], 1 do
|
||||||
|
if(lookupsids2_result['names']['names'][j]['sid_type'] ~= "SID_NAME_UNKNOWN") then
|
||||||
|
local result = {}
|
||||||
|
result['name'] = lookupsids2_result['names']['names'][j]['name']
|
||||||
|
result['rid'] = 500 + j - 1
|
||||||
|
result['domain'] = domain
|
||||||
|
result['type'] = lookupsids2_result['names']['names'][j]['sid_type']
|
||||||
|
result['typestr'] = msrpc.lsa_SidType_tostr(result['type'])
|
||||||
|
result['source'] = "LSA Bruteforce"
|
||||||
|
table.insert(response, result)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Start at RID 1000
|
||||||
|
local start = 1000
|
||||||
|
-- Keep track of the number of consecutive empty groups
|
||||||
|
local empty = 0
|
||||||
|
repeat
|
||||||
|
-- Keep track of the number of names we found in this group
|
||||||
|
local used_names = 0
|
||||||
|
|
||||||
|
local sids = {}
|
||||||
|
for j = start, start + LSA_GROUPSIZE, 1 do
|
||||||
|
sids[#sids + 1] = sid .. "-" .. j
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Try converting this group of RIDs into names
|
||||||
|
status, lookupsids2_result = msrpc.lsa_lookupsids2(smbstate, openpolicy2_result['policy_handle'], sids)
|
||||||
|
if(status == false) then
|
||||||
|
stdnse.print_debug(1, string.format("Error looking up RIDs: %s", lookupsids2_result))
|
||||||
|
else
|
||||||
|
-- Put the details for each name into an array
|
||||||
|
for j = 1, #lookupsids2_result['names']['names'], 1 do
|
||||||
|
if(lookupsids2_result['names']['names'][j]['sid_type'] ~= "SID_NAME_UNKNOWN") then
|
||||||
|
local result = {}
|
||||||
|
result['name'] = lookupsids2_result['names']['names'][j]['name']
|
||||||
|
result['rid'] = start + j - 1
|
||||||
|
result['domain'] = domain
|
||||||
|
result['type'] = lookupsids2_result['names']['names'][j]['sid_type']
|
||||||
|
result['typestr'] = msrpc.lsa_SidType_tostr(result['type'])
|
||||||
|
result['source'] = "LSA Bruteforce"
|
||||||
|
table.insert(response, result)
|
||||||
|
|
||||||
|
-- Increment the number of names we've found
|
||||||
|
used_names = used_names + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- Either increment or reset the number of empty groups
|
||||||
|
if(used_names == 0) then
|
||||||
|
empty = empty + 1
|
||||||
|
else
|
||||||
|
empty = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Go to the next set of RIDs
|
||||||
|
start = start + LSA_GROUPSIZE
|
||||||
|
until (status == false or (empty == LSA_MINEMPTY))
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Close the handle
|
||||||
|
msrpc.lsa_close(smbstate, openpolicy2_result['policy_handle'])
|
||||||
|
|
||||||
|
msrpc.stop_smb(smbstate)
|
||||||
|
|
||||||
|
stdnse.print_debug(3, "Leaving enum_lsa()")
|
||||||
|
|
||||||
|
return true, response
|
||||||
|
end
|
||||||
|
|
||||||
|
---Gets the best possible list of user accounts on the remote system using every available method.
|
||||||
|
--
|
||||||
|
-- TODO: Caching, store this in the registry
|
||||||
|
--
|
||||||
|
--@param host The host object.
|
||||||
|
--@return (status, result, names) If status is false, result is an error message; otherwise, result
|
||||||
|
-- is an array of users indexed by username and names is a sorted array of names.
|
||||||
|
function get_user_list(host)
|
||||||
|
local status_samr, result_samr
|
||||||
|
local status_lsa, result_lsa
|
||||||
|
local response = {}
|
||||||
|
local names = {}
|
||||||
|
local i, v
|
||||||
|
|
||||||
|
status_lsa, result_lsa = lsa_enum_users(host)
|
||||||
|
if(status_lsa == false) then
|
||||||
|
stdnse.print_debug("MSRPC: Failed to enumerate users through LSA: %s", result_lsa)
|
||||||
|
else
|
||||||
|
for i = 1, #result_lsa, 1 do
|
||||||
|
if(result_lsa[i]['name'] ~= nil and result_lsa[i]['type'] == "SID_NAME_USER") then
|
||||||
|
response[result_lsa[i]['domain'] .. '\\' .. result_lsa[i]['name']] = result_lsa[i]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
status_samr, result_samr = samr_enum_users(host)
|
||||||
|
if(status_samr == false) then
|
||||||
|
stdnse.print_debug("MSRPC: Failed to enumerate users through SAMR: %s", result_samr)
|
||||||
|
else
|
||||||
|
for i = 1, #result_samr, 1 do
|
||||||
|
if(result_samr[i]['name'] ~= nil and result_samr[i]['type'] == "SID_NAME_USER") then
|
||||||
|
response[result_samr[i]['domain'] .. '\\' .. result_samr[i]['name']] = result_samr[i]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if(status_samr == false and status_lsa == false) then
|
||||||
|
return false, "MSRPC: Couldn't enumerate users; see debug output for more information"
|
||||||
|
end
|
||||||
|
|
||||||
|
for i, v in pairs(response) do
|
||||||
|
table.insert(names, i)
|
||||||
|
end
|
||||||
|
table.sort(names, function(a,b) return a:lower() < b:lower() end )
|
||||||
|
|
||||||
|
return true, response, names
|
||||||
|
end
|
||||||
|
|
||||||
|
|||||||
601
nselib/msrpcperformance.lua
Normal file
601
nselib/msrpcperformance.lua
Normal file
@@ -0,0 +1,601 @@
|
|||||||
|
---This module is designed to parse the <code>PERF_DATA_BLOCK</code> structure, which is
|
||||||
|
-- stored in the registry under HKEY_PERFORMANCE_DATA. By querying this structure, you can
|
||||||
|
-- get a whole lot of information about what's going on.
|
||||||
|
--
|
||||||
|
-- To use this from a script, see <code>get_performance_data</code>, it is the only
|
||||||
|
-- 'public' function in this module.
|
||||||
|
--
|
||||||
|
-- My primary sources of information were:
|
||||||
|
-- * This 1996 journal by Matt Pietrek: <http://www.microsoft.com/msj/archive/S271.aspx>
|
||||||
|
-- * The followup article: <http://www.microsoft.com/msj/archive/S2A9.aspx>
|
||||||
|
-- * The WinPerf.h header file
|
||||||
|
--
|
||||||
|
-- And my primary inspiration was PsTools, specifically, pstasklist.exe.
|
||||||
|
--
|
||||||
|
--@author Ron Bowes <ron@skullsecurity.net>
|
||||||
|
--@copyright See nmap's COPYING for licence
|
||||||
|
-----------------------------------------------------------------------
|
||||||
|
module(... or "msrpcperformance", package.seeall)
|
||||||
|
|
||||||
|
require 'msrpctypes'
|
||||||
|
|
||||||
|
---Parses the title database, which is a series of null-terminated string pairs.
|
||||||
|
--
|
||||||
|
--@param data The data being processed.
|
||||||
|
--@param pos The position within <code>data</code>.
|
||||||
|
--@return (status, pos, result) The status (true if successful), the new position in <code>data</code> (or an error
|
||||||
|
-- message), and a table representing the datatype, if any.
|
||||||
|
local function parse_perf_title_database(data, pos)
|
||||||
|
local result = {}
|
||||||
|
|
||||||
|
repeat
|
||||||
|
local number, name
|
||||||
|
pos, number, name = bin.unpack("<zz", data, pos)
|
||||||
|
result[tonumber(number)] = name
|
||||||
|
until pos >= #data
|
||||||
|
|
||||||
|
return true, pos, result
|
||||||
|
end
|
||||||
|
|
||||||
|
---Parses a PERF_DATA_BLOCK, which has the following definition (from "WinPerf.h" on Visual Studio 8):
|
||||||
|
--
|
||||||
|
--<code>
|
||||||
|
-- typedef struct _PERF_DATA_BLOCK {
|
||||||
|
-- WCHAR Signature[4]; // Signature: Unicode "PERF"
|
||||||
|
-- DWORD LittleEndian; // 0 = Big Endian, 1 = Little Endian
|
||||||
|
-- DWORD Version; // Version of these data structures
|
||||||
|
-- // starting at 1
|
||||||
|
-- DWORD Revision; // Revision of these data structures
|
||||||
|
-- // starting at 0 for each Version
|
||||||
|
-- DWORD TotalByteLength; // Total length of data block
|
||||||
|
-- DWORD HeaderLength; // Length of this structure
|
||||||
|
-- DWORD NumObjectTypes; // Number of types of objects
|
||||||
|
-- // being reported
|
||||||
|
-- LONG DefaultObject; // Object Title Index of default
|
||||||
|
-- // object to display when data from
|
||||||
|
-- // this system is retrieved (-1 =
|
||||||
|
-- // none, but this is not expected to
|
||||||
|
-- // be used)
|
||||||
|
-- SYSTEMTIME SystemTime; // Time at the system under
|
||||||
|
-- // measurement
|
||||||
|
-- LARGE_INTEGER PerfTime; // Performance counter value
|
||||||
|
-- // at the system under measurement
|
||||||
|
-- LARGE_INTEGER PerfFreq; // Performance counter frequency
|
||||||
|
-- // at the system under measurement
|
||||||
|
-- LARGE_INTEGER PerfTime100nSec; // Performance counter time in 100 nsec
|
||||||
|
-- // units at the system under measurement
|
||||||
|
-- DWORD SystemNameLength; // Length of the system name
|
||||||
|
-- DWORD SystemNameOffset; // Offset, from beginning of this
|
||||||
|
-- // structure, to name of system
|
||||||
|
-- // being measured
|
||||||
|
-- } PERF_DATA_BLOCK, *PPERF_DATA_BLOCK;
|
||||||
|
--</code>
|
||||||
|
--
|
||||||
|
--@param data The data being processed.
|
||||||
|
--@param pos The position within <code>data</code>.
|
||||||
|
--@return (status, pos, result) The status (true if successful), the new position in <code>data</code> (or an error
|
||||||
|
-- message), and a table representing the datatype, if any.
|
||||||
|
local function parse_perf_data_block(data, pos)
|
||||||
|
local result = {}
|
||||||
|
|
||||||
|
pos, result['Signature'] = msrpctypes.unicode_to_string(data, pos, 4, false)
|
||||||
|
if(result['Signature'] ~= "PERF") then
|
||||||
|
return false, "MSRPC: PERF_DATA_BLOCK signature is missing or incorrect"
|
||||||
|
end
|
||||||
|
|
||||||
|
pos, result['LittleEndian'] = msrpctypes.unmarshall_int32(data, pos)
|
||||||
|
if(result['LittleEndian'] ~= 1) then
|
||||||
|
return false, "MSRPC: PERF_DATA_BLOCK returned a non-understood endianness"
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Parse the header
|
||||||
|
pos, result['Version'] = msrpctypes.unmarshall_int32(data, pos)
|
||||||
|
pos, result['Revision'] = msrpctypes.unmarshall_int32(data, pos)
|
||||||
|
pos, result['TotalByteLength'] = msrpctypes.unmarshall_int32(data, pos)
|
||||||
|
pos, result['HeaderLength'] = msrpctypes.unmarshall_int32(data, pos)
|
||||||
|
pos, result['NumObjectTypes'] = msrpctypes.unmarshall_int32(data, pos)
|
||||||
|
pos, result['DefaultObject'] = msrpctypes.unmarshall_int32(data, pos)
|
||||||
|
pos, result['SystemTime'] = msrpctypes.unmarshall_SYSTEMTIME(data, pos)
|
||||||
|
pos, result['PerfTime'] = msrpctypes.unmarshall_int64(data, pos)
|
||||||
|
pos, result['PerfFreq'] = msrpctypes.unmarshall_int64(data, pos)
|
||||||
|
pos, result['PerfTime100nSec'] = msrpctypes.unmarshall_int64(data, pos)
|
||||||
|
pos = pos + 4 -- This value doesn't seem to line up, so add 4
|
||||||
|
|
||||||
|
pos, result['SystemNameLength'] = msrpctypes.unmarshall_int32(data, pos)
|
||||||
|
pos, result['SystemNameOffset'] = msrpctypes.unmarshall_int32(data, pos)
|
||||||
|
|
||||||
|
-- Ensure that the system name is directly after the header. This technically shouldn't matter, but Microsoft's documentation
|
||||||
|
-- (in WinPref.h) says that the actual object comes "after the PERF_DATA_BLOCK", so it doesn't make sense that the SystemName
|
||||||
|
-- could be anywhere else.
|
||||||
|
if(pos ~= result['SystemNameOffset'] + 1) then
|
||||||
|
return false, "MSRPC: PERF_DATA_BLOCK has SystemName in the wrong location"
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Read the system name from the next location (which happens to be identical to SystemNameOffset, on a proper system)
|
||||||
|
pos, result['SystemName'] = msrpctypes.unicode_to_string(data, pos, result['SystemNameLength'] / 2, true)
|
||||||
|
|
||||||
|
pos = pos + 4 -- Again, we end up not lined up so here we fix it
|
||||||
|
|
||||||
|
return true, pos, result
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
---Parse a PERF_OBJECT_TYPE structure. From Microsoft's documentation:
|
||||||
|
--
|
||||||
|
--<code>
|
||||||
|
-- //
|
||||||
|
-- // The _PERF_DATA_BLOCK structure is followed by NumObjectTypes of
|
||||||
|
-- // data sections, one for each type of object measured. Each object
|
||||||
|
-- // type section begins with a _PERF_OBJECT_TYPE structure.
|
||||||
|
-- //
|
||||||
|
-- typedef struct _PERF_OBJECT_TYPE {
|
||||||
|
-- DWORD TotalByteLength; // Length of this object definition
|
||||||
|
-- // including this structure, the
|
||||||
|
-- // counter definitions, and the
|
||||||
|
-- // instance definitions and the
|
||||||
|
-- // counter blocks for each instance:
|
||||||
|
-- // This is the offset from this
|
||||||
|
-- // structure to the next object, if
|
||||||
|
-- // any
|
||||||
|
-- DWORD DefinitionLength; // Length of object definition,
|
||||||
|
-- // which includes this structure
|
||||||
|
-- // and the counter definition
|
||||||
|
-- // structures for this object: this
|
||||||
|
-- // is the offset of the first
|
||||||
|
-- // instance or of the counters
|
||||||
|
-- // for this object if there is
|
||||||
|
-- // no instance
|
||||||
|
-- DWORD HeaderLength; // Length of this structure: this
|
||||||
|
-- // is the offset to the first
|
||||||
|
-- // counter definition for this
|
||||||
|
-- // object
|
||||||
|
-- DWORD ObjectNameTitleIndex;
|
||||||
|
-- // Index to name in Title Database
|
||||||
|
-- #ifdef _WIN64
|
||||||
|
-- DWORD ObjectNameTitle; // Should use this as an offset
|
||||||
|
-- #else
|
||||||
|
-- LPWSTR ObjectNameTitle; // Initially NULL, for use by
|
||||||
|
-- // analysis program to point to
|
||||||
|
-- // retrieved title string
|
||||||
|
-- #endif
|
||||||
|
-- DWORD ObjectHelpTitleIndex;
|
||||||
|
-- // Index to Help in Title Database
|
||||||
|
-- #ifdef _WIN64
|
||||||
|
-- DWORD ObjectHelpTitle; // Should use this as an offset
|
||||||
|
-- #else
|
||||||
|
-- LPWSTR ObjectHelpTitle; // Initially NULL, for use by
|
||||||
|
-- // analysis program to point to
|
||||||
|
-- // retrieved title string
|
||||||
|
-- #endif
|
||||||
|
-- DWORD DetailLevel; // Object level of detail (for
|
||||||
|
-- // controlling display complexity);
|
||||||
|
-- // will be min of detail levels
|
||||||
|
-- // for all this object's counters
|
||||||
|
-- DWORD NumCounters; // Number of counters in each
|
||||||
|
-- // counter block (one counter
|
||||||
|
-- // block per instance)
|
||||||
|
-- LONG DefaultCounter; // Default counter to display when
|
||||||
|
-- // this object is selected, index
|
||||||
|
-- // starting at 0 (-1 = none, but
|
||||||
|
-- // this is not expected to be used)
|
||||||
|
-- LONG NumInstances; // Number of object instances
|
||||||
|
-- // for which counters are being
|
||||||
|
-- // returned from the system under
|
||||||
|
-- // measurement. If the object defined
|
||||||
|
-- // will never have any instance data
|
||||||
|
-- // structures (PERF_INSTANCE_DEFINITION)
|
||||||
|
-- // then this value should be -1, if the
|
||||||
|
-- // object can have 0 or more instances,
|
||||||
|
-- // but has none present, then this
|
||||||
|
-- // should be 0, otherwise this field
|
||||||
|
-- // contains the number of instances of
|
||||||
|
-- // this counter.
|
||||||
|
-- DWORD CodePage; // 0 if instance strings are in
|
||||||
|
-- // UNICODE, else the Code Page of
|
||||||
|
-- // the instance names
|
||||||
|
-- LARGE_INTEGER PerfTime; // Sample Time in "Object" units
|
||||||
|
-- //
|
||||||
|
-- LARGE_INTEGER PerfFreq; // Frequency of "Object" units in
|
||||||
|
-- // counts per second.
|
||||||
|
-- } PERF_OBJECT_TYPE, *PPERF_OBJECT_TYPE;
|
||||||
|
--</code>
|
||||||
|
--
|
||||||
|
--@param data The data being processed.
|
||||||
|
--@param pos The position within <code>data</code>.
|
||||||
|
--@return (status, pos, result) The status (true if successful), the new position in <code>data</code> (or an error
|
||||||
|
-- message), and a table representing the datatype, if any.
|
||||||
|
local function parse_perf_object_type(data, pos)
|
||||||
|
local result = {}
|
||||||
|
|
||||||
|
pos, result['TotalByteLength'] = msrpctypes.unmarshall_int32(data, pos) -- Offset to the next object
|
||||||
|
pos, result['DefinitionLength'] = msrpctypes.unmarshall_int32(data, pos) -- Offset to the first instance (or counter, if no instances)
|
||||||
|
pos, result['HeaderLength'] = msrpctypes.unmarshall_int32(data, pos) -- Offset to the first counter definition
|
||||||
|
pos, result['ObjectNameTitleIndex'] = msrpctypes.unmarshall_int32(data, pos) -- Index in the Title Database
|
||||||
|
pos, result['ObjectNameTitle'] = msrpctypes.unmarshall_int32(data, pos) -- TODO: will this work with 64-bit?
|
||||||
|
pos, result['ObjectHelpTitleIndex'] = msrpctypes.unmarshall_int32(data, pos) -- Index in the Help Database
|
||||||
|
pos, result['ObjectHelpTitle'] = msrpctypes.unmarshall_int32(data, pos) -- TODO: will this workw ith 64-bit?
|
||||||
|
pos, result['DetailLevel'] = msrpctypes.unmarshall_int32(data, pos)
|
||||||
|
pos, result['NumCounters'] = msrpctypes.unmarshall_int32(data, pos) -- The number of counters in each counter block
|
||||||
|
pos, result['DefaultCounter'] = msrpctypes.unmarshall_int32(data, pos)
|
||||||
|
pos, result['NumInstances'] = msrpctypes.unmarshall_int32(data, pos) -- Numer of object instances for which counters are being returned
|
||||||
|
pos, result['CodePage'] = msrpctypes.unmarshall_int32(data, pos) -- 0 if strings are in UNICODE, otherwise the Code Page
|
||||||
|
-- if(result['CodePage'] ~= 0) then
|
||||||
|
-- return false, string.format("Unknown Code Page for data: %d\n", result['CodePage'])
|
||||||
|
-- end
|
||||||
|
pos, result['PerfTime'] = msrpctypes.unmarshall_int64(data, pos) -- Sample time in "Object" units
|
||||||
|
pos, result['PerfFreq'] = msrpctypes.unmarshall_int64(data, pos) -- Frequency of "Object" units in counts/second
|
||||||
|
|
||||||
|
return true, pos, result
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
---Parse a PERF_COUNTER_DEFINITION structure. From Microsoft's documentation:
|
||||||
|
--
|
||||||
|
--<code>
|
||||||
|
-- // There is one of the following for each of the
|
||||||
|
-- // PERF_OBJECT_TYPE.NumCounters. The Unicode names in this structure MUST
|
||||||
|
-- // come from a message file.
|
||||||
|
-- typedef struct _PERF_COUNTER_DEFINITION {
|
||||||
|
-- DWORD ByteLength; // Length in bytes of this structure
|
||||||
|
-- DWORD CounterNameTitleIndex;
|
||||||
|
-- // Index of Counter name into
|
||||||
|
-- // Title Database
|
||||||
|
-- #ifdef _WIN64
|
||||||
|
-- DWORD CounterNameTitle;
|
||||||
|
-- #else
|
||||||
|
-- LPWSTR CounterNameTitle; // Initially NULL, for use by
|
||||||
|
-- // analysis program to point to
|
||||||
|
-- // retrieved title string
|
||||||
|
-- #endif
|
||||||
|
-- DWORD CounterHelpTitleIndex;
|
||||||
|
-- // Index of Counter Help into
|
||||||
|
-- // Title Database
|
||||||
|
-- #ifdef _WIN64
|
||||||
|
-- DWORD CounterHelpTitle;
|
||||||
|
-- #else
|
||||||
|
-- LPWSTR CounterHelpTitle; // Initially NULL, for use by
|
||||||
|
-- // analysis program to point to
|
||||||
|
-- // retrieved title string
|
||||||
|
-- #endif
|
||||||
|
-- LONG DefaultScale; // Power of 10 by which to scale
|
||||||
|
-- // chart line if vertical axis is 100
|
||||||
|
-- // 0 ==> 1, 1 ==> 10, -1 ==>1/10, etc.
|
||||||
|
-- DWORD DetailLevel; // Counter level of detail (for
|
||||||
|
-- // controlling display complexity)
|
||||||
|
-- DWORD CounterType; // Type of counter
|
||||||
|
-- DWORD CounterSize; // Size of counter in bytes
|
||||||
|
-- DWORD CounterOffset; // Offset from the start of the
|
||||||
|
-- // PERF_COUNTER_BLOCK to the first
|
||||||
|
-- // byte of this counter
|
||||||
|
-- } PERF_COUNTER_DEFINITION, *PPERF_COUNTER_DEFINITION;
|
||||||
|
--</code>
|
||||||
|
--
|
||||||
|
--@param data The data being processed.
|
||||||
|
--@param pos The position within <code>data</code>.
|
||||||
|
--@return (status, pos, result) The status (true if successful), the new position in <code>data</code> (or an error
|
||||||
|
-- message), and a table representing the datatype, if any.
|
||||||
|
local function parse_perf_counter_definition(data, pos)
|
||||||
|
local result = {}
|
||||||
|
local initial_pos = pos
|
||||||
|
|
||||||
|
pos, result['ByteLength'] = msrpctypes.unmarshall_int32(data, pos)
|
||||||
|
pos, result['CounterNameTitleIndex'] = msrpctypes.unmarshall_int32(data, pos)
|
||||||
|
pos, result['CounterNameTitle'] = msrpctypes.unmarshall_int32(data, pos)
|
||||||
|
pos, result['CounterHelpTitleIndex'] = msrpctypes.unmarshall_int32(data, pos)
|
||||||
|
pos, result['CounterHelpTitle'] = msrpctypes.unmarshall_int32(data, pos)
|
||||||
|
pos, result['DefaultScale'] = msrpctypes.unmarshall_int32(data, pos)
|
||||||
|
pos, result['DetailLevel'] = msrpctypes.unmarshall_int32(data, pos)
|
||||||
|
pos, result['CounterType'] = msrpctypes.unmarshall_int32(data, pos)
|
||||||
|
pos, result['CounterSize'] = msrpctypes.unmarshall_int32(data, pos)
|
||||||
|
pos, result['CounterOffset'] = msrpctypes.unmarshall_int32(data, pos)
|
||||||
|
|
||||||
|
pos = initial_pos + result['ByteLength']
|
||||||
|
|
||||||
|
return true, pos, result
|
||||||
|
end
|
||||||
|
|
||||||
|
---Parse the actual counter value. This is a fairly simple function, it takes a counter
|
||||||
|
-- definition and pulls out data based on it.
|
||||||
|
--
|
||||||
|
-- Note: I don't think this is doing the 8-byte values right, I suspect that they're supposed
|
||||||
|
-- to be doubles.
|
||||||
|
--
|
||||||
|
--@param data The data being processed.
|
||||||
|
--@param pos The position within <code>data</code>.
|
||||||
|
--@param counter_definition The matching counter_definition.
|
||||||
|
--@return (status, pos, result) The status (true if successful), the new position in <code>data</code> (or an error
|
||||||
|
-- message), and a table representing the datatype, if any.
|
||||||
|
local function parse_perf_counter(data, pos, counter_definition)
|
||||||
|
local result
|
||||||
|
|
||||||
|
if(counter_definition['CounterSize'] == 4) then
|
||||||
|
pos, result = msrpctypes.unmarshall_int32(data, pos)
|
||||||
|
elseif(counter_definition['CounterSize'] == 8) then
|
||||||
|
pos, result = msrpctypes.unmarshall_int64(data, pos)
|
||||||
|
-- pos, result = bin.unpack("<d", data, pos)
|
||||||
|
else
|
||||||
|
pos, result = msrpctypes.unmarshall_raw(data, pos, counter_definition['CounterSize'])
|
||||||
|
end
|
||||||
|
|
||||||
|
return true, pos, result
|
||||||
|
end
|
||||||
|
|
||||||
|
---Parse a PERF_INSTANCE_DEFINITION structure. From Microsoft's documentation:
|
||||||
|
--
|
||||||
|
--<code>
|
||||||
|
-- // If (PERF_DATA_BLOCK.NumInstances >= 0) then there will be
|
||||||
|
-- // PERF_DATA_BLOCK.NumInstances of a (PERF_INSTANCE_DEFINITION
|
||||||
|
-- // followed by a PERF_COUNTER_BLOCK followed by the counter data fields)
|
||||||
|
-- // for each instance.
|
||||||
|
-- //
|
||||||
|
-- // If (PERF_DATA_BLOCK.NumInstances < 0) then the counter definition
|
||||||
|
-- // strucutre above will be followed by only a PERF_COUNTER_BLOCK and the
|
||||||
|
-- // counter data for that COUNTER.
|
||||||
|
-- typedef struct _PERF_INSTANCE_DEFINITION {
|
||||||
|
-- DWORD ByteLength; // Length in bytes of this structure,
|
||||||
|
-- // including the subsequent name
|
||||||
|
-- DWORD ParentObjectTitleIndex;
|
||||||
|
-- // Title Index to name of "parent"
|
||||||
|
-- // object (e.g., if thread, then
|
||||||
|
-- // process is parent object type);
|
||||||
|
-- // if logical drive, the physical
|
||||||
|
-- // drive is parent object type
|
||||||
|
-- DWORD ParentObjectInstance;
|
||||||
|
-- // Index to instance of parent object
|
||||||
|
-- // type which is the parent of this
|
||||||
|
-- // instance.
|
||||||
|
-- LONG UniqueID; // A unique ID used instead of
|
||||||
|
-- // matching the name to identify
|
||||||
|
-- // this instance, -1 = none
|
||||||
|
-- DWORD NameOffset; // Offset from beginning of
|
||||||
|
-- // this struct to the Unicode name
|
||||||
|
-- // of this instance
|
||||||
|
-- DWORD NameLength; // Length in bytes of name; 0 = none
|
||||||
|
-- // this length includes the characters
|
||||||
|
-- // in the string plus the size of the
|
||||||
|
-- // terminating NULL char. It does not
|
||||||
|
-- // include any additional pad bytes to
|
||||||
|
-- // correct structure alignment
|
||||||
|
-- } PERF_INSTANCE_DEFINITION, *PPERF_INSTANCE_DEFINITION;
|
||||||
|
--</code>
|
||||||
|
--
|
||||||
|
--@param data The data being processed.
|
||||||
|
--@param pos The position within <code>data</code>.
|
||||||
|
--@return (status, pos, result) The status (true if successful), the new position in <code>data</code> (or an error
|
||||||
|
-- message), and a table representing the datatype, if any.
|
||||||
|
local function parse_perf_instance_definition(data, pos)
|
||||||
|
local result = {}
|
||||||
|
|
||||||
|
-- Remember where we started. I noticed that where the counter part starts can move around, so we have to
|
||||||
|
-- determine it by adding ByteLength to the initial position
|
||||||
|
local initial_pos = pos
|
||||||
|
|
||||||
|
pos, result['ByteLength'] = msrpctypes.unmarshall_int32(data, pos)
|
||||||
|
pos, result['ParentObjectTitleIndex'] = msrpctypes.unmarshall_int32(data, pos)
|
||||||
|
pos, result['ParentObjectInstance'] = msrpctypes.unmarshall_int32(data, pos)
|
||||||
|
pos, result['UniqueID'] = msrpctypes.unmarshall_int32(data, pos)
|
||||||
|
pos, result['NameOffset'] = msrpctypes.unmarshall_int32(data, pos)
|
||||||
|
pos, result['NameLength'] = msrpctypes.unmarshall_int32(data, pos)
|
||||||
|
|
||||||
|
pos, result['InstanceName'] = msrpctypes.unicode_to_string(data, pos, result['NameLength'] / 2, true)
|
||||||
|
|
||||||
|
pos = initial_pos + result['ByteLength']
|
||||||
|
|
||||||
|
return true, pos, result
|
||||||
|
end
|
||||||
|
|
||||||
|
---Parse a PERF_COUNTER_BLOCK structure. From Microsoft's documentation:
|
||||||
|
--
|
||||||
|
--<code>
|
||||||
|
-- typedef struct _PERF_COUNTER_BLOCK {
|
||||||
|
-- DWORD ByteLength; // Length in bytes of this structure,
|
||||||
|
-- // including the following counters
|
||||||
|
-- } PERF_COUNTER_BLOCK, *PPERF_COUNTER_BLOCK;
|
||||||
|
--
|
||||||
|
--</code>
|
||||||
|
--
|
||||||
|
--@param data The data being processed.
|
||||||
|
--@param pos The position within <code>data</code>.
|
||||||
|
--@return (status, pos, result) The status (true if successful), the new position in <code>data</code> (or an error
|
||||||
|
-- message), and a table representing the datatype, if any.
|
||||||
|
local function parse_perf_counter_block(data, pos)
|
||||||
|
local result = {}
|
||||||
|
|
||||||
|
pos, result['ByteLength'] = msrpctypes.unmarshall_int32(data, pos)
|
||||||
|
|
||||||
|
return true, pos, result
|
||||||
|
end
|
||||||
|
|
||||||
|
---Retrieve the parsed performance data from the given host for the requested object values. To get a list of possible
|
||||||
|
-- object values, leave 'objects' blank and look at <code>result['title_database']</code> -- it'll contain a list of
|
||||||
|
-- indexes that can be looked up. These indexes are passed as a string or as a series of space-separated strings (eg,
|
||||||
|
-- "230" for "Process" and "238" for "Process" and "Processor").
|
||||||
|
--
|
||||||
|
--@param host The host object
|
||||||
|
--@param objects [optional] The space-separated list of object numbers to retrieve. Default: only retrieve the database.
|
||||||
|
function get_performance_data(host, objects)
|
||||||
|
|
||||||
|
local status, smbstate
|
||||||
|
local bind_result, openhkpd_result, queryvalue_result, data_block
|
||||||
|
local pos
|
||||||
|
local result = {}
|
||||||
|
local i, j, k
|
||||||
|
local pos
|
||||||
|
|
||||||
|
-- Create the SMB session
|
||||||
|
status, smbstate = msrpc.start_smb(host, msrpc.WINREG_PATH)
|
||||||
|
if(status == false) then
|
||||||
|
return false, smbstate
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Bind to WINREG service
|
||||||
|
status, bind_result = msrpc.bind(smbstate, msrpc.WINREG_UUID, msrpc.WINREG_VERSION, nil)
|
||||||
|
if(status == false) then
|
||||||
|
msrpc.stop_smb(smbstate)
|
||||||
|
return false, bind_result
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Open HKEY_PERFORMANCE_DATA
|
||||||
|
status, openhkpd_result = msrpc.winreg_openhkpd(smbstate)
|
||||||
|
if(status == false) then
|
||||||
|
msrpc.stop_smb(smbstate)
|
||||||
|
return false, openhkpd_result
|
||||||
|
end
|
||||||
|
|
||||||
|
status, queryvalue_result = msrpc.winreg_queryvalue(smbstate, openhkpd_result['handle'], "Counter 009")
|
||||||
|
if(status == false) then
|
||||||
|
msrpc.stop_smb(smbstate)
|
||||||
|
return false, queryvalue_result
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Parse the title database
|
||||||
|
pos = 1
|
||||||
|
status, pos, result['title_database'] = parse_perf_title_database(queryvalue_result['value'], pos)
|
||||||
|
result['title_database'][0] = "<null>"
|
||||||
|
|
||||||
|
if(status == false) then
|
||||||
|
msrpc.stop_smb(smbstate)
|
||||||
|
return false, pos
|
||||||
|
end
|
||||||
|
|
||||||
|
if(objects ~= nil and #objects > 0) then
|
||||||
|
-- Query for the objects
|
||||||
|
status, queryvalue_result = msrpc.winreg_queryvalue(smbstate, openhkpd_result['handle'], objects)
|
||||||
|
if(status == false) then
|
||||||
|
msrpc.stop_smb(smbstate)
|
||||||
|
return false, queryvalue_result
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Parse the header
|
||||||
|
pos = 1
|
||||||
|
status, pos, data_block = parse_perf_data_block(queryvalue_result['value'], pos)
|
||||||
|
if(status == false) then
|
||||||
|
msrpc.stop_smb(smbstate)
|
||||||
|
return false, pos
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Move past the header
|
||||||
|
pos = 1 + data_block['HeaderLength']
|
||||||
|
|
||||||
|
-- Parse the data sections
|
||||||
|
for i = 1, data_block['NumObjectTypes'], 1 do
|
||||||
|
local object_start = pos
|
||||||
|
local object_name
|
||||||
|
|
||||||
|
local counter_definitions = {}
|
||||||
|
local object_instances = {}
|
||||||
|
local counter_definitions = {}
|
||||||
|
|
||||||
|
-- Get the type of the object (this is basically the class definition -- info about the object instances)
|
||||||
|
status, pos, object_type = parse_perf_object_type(queryvalue_result['value'], pos)
|
||||||
|
if(status == false) then
|
||||||
|
msrpc.stop_smb(smbstate)
|
||||||
|
return false, pos
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Start setting up the result object
|
||||||
|
--io.write(string.format("Index = %d\n", object_type['ObjectNameTitleIndex']))
|
||||||
|
object_name = result['title_database'][object_type['ObjectNameTitleIndex']]
|
||||||
|
result[object_name] = {}
|
||||||
|
|
||||||
|
--io.write(string.format("\n\nOBJECT: %s\n", object_name))
|
||||||
|
--io.write(string.format(" Counters: %d\n", object_type['NumCounters']))
|
||||||
|
--io.write(string.format(" Instances: %d\n", object_type['NumInstances']))
|
||||||
|
--io.write("-----------------\n")
|
||||||
|
|
||||||
|
-- Bring the position to the beginning of the counter definitions
|
||||||
|
pos = object_start + object_type['HeaderLength']
|
||||||
|
|
||||||
|
-- Parse the counter definitions
|
||||||
|
for j = 1, object_type['NumCounters'], 1 do
|
||||||
|
status, pos, counter_definitions[j] = parse_perf_counter_definition(queryvalue_result['value'], pos)
|
||||||
|
if(status == false) then
|
||||||
|
msrpc.stop_smb(smbstate)
|
||||||
|
return false, pos
|
||||||
|
end
|
||||||
|
--io.write(string.format(" Counter definition #%2d: [%d bytes] %s\n", j, counter_definitions[j]['CounterSize'], result['title_database'][counter_definitions[j]['CounterNameTitleIndex']]))
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Bring the position to the beginning of the instances (or counters)
|
||||||
|
pos = object_start + object_type['DefinitionLength']
|
||||||
|
|
||||||
|
-- Check if we have any instances (sometimes we don't -- if we don't, the value returned is a negative)
|
||||||
|
if(bit.band(object_type['NumInstances'], 0x80000000) == 0) then
|
||||||
|
-- Parse the object instances and counters
|
||||||
|
for j = 1, object_type['NumInstances'], 1 do
|
||||||
|
local instance_start = pos
|
||||||
|
local instance_name
|
||||||
|
local counter_block
|
||||||
|
-- Instance definition
|
||||||
|
status, pos, object_instances[j] = parse_perf_instance_definition(queryvalue_result['value'], pos)
|
||||||
|
if(status == false) then
|
||||||
|
msrpc.stop_smb(smbstate)
|
||||||
|
return false, pos
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Set up the instance array
|
||||||
|
instance_name = object_instances[j]['InstanceName']
|
||||||
|
result[object_name][instance_name] = {}
|
||||||
|
|
||||||
|
-- Bring the pos to the start of the counter block
|
||||||
|
pos = instance_start + object_instances[j]['ByteLength']
|
||||||
|
|
||||||
|
--io.write(string.format("\n INSTANCE: %s\n", instance_name))
|
||||||
|
--io.write(string.format(" Length: %d\n", object_instances[j]['ByteLength']))
|
||||||
|
--io.write(string.format(" NameOffset: %d\n", object_instances[j]['NameOffset']))
|
||||||
|
--io.write(string.format(" NameLength: %d\n", object_instances[j]['NameLength']))
|
||||||
|
--io.write(" --------------\n")
|
||||||
|
|
||||||
|
-- The counter block
|
||||||
|
status, pos, counter_block = parse_perf_counter_block(queryvalue_result['value'], pos)
|
||||||
|
if(status == false) then
|
||||||
|
msrpc.stop_smb(smbstate)
|
||||||
|
return false, pos
|
||||||
|
end
|
||||||
|
|
||||||
|
for k = 1, object_type['NumCounters'], 1 do
|
||||||
|
local counter_name
|
||||||
|
-- Each individual counter
|
||||||
|
status, pos, counter_result = parse_perf_counter(queryvalue_result['value'], pos, counter_definitions[k])
|
||||||
|
if(status == false) then
|
||||||
|
msrpc.stop_smb(smbstate)
|
||||||
|
return false, pos
|
||||||
|
end
|
||||||
|
counter_name = result['title_database'][counter_definitions[k]['CounterNameTitleIndex']]
|
||||||
|
--io.write(string.format(" %s: %s\n", counter_name, counter_result))
|
||||||
|
|
||||||
|
-- Save it in the result
|
||||||
|
result[object_name][instance_name][counter_name] = counter_result
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Bring the pos to the end of the next section
|
||||||
|
pos = instance_start + object_instances[j]['ByteLength'] + counter_block['ByteLength']
|
||||||
|
end
|
||||||
|
else
|
||||||
|
for k = 1, object_type['NumCounters'], 1 do
|
||||||
|
local counter_name
|
||||||
|
-- Each individual counter
|
||||||
|
status, pos, counter_result = parse_perf_counter(queryvalue_result['value'], pos, counter_definitions[k])
|
||||||
|
if(status == false) then
|
||||||
|
msrpc.stop_smb(smbstate)
|
||||||
|
return false, pos
|
||||||
|
end
|
||||||
|
counter_name = result['title_database'][counter_definitions[k]['CounterNameTitleIndex']]
|
||||||
|
--io.write(string.format(" %s: %s\n", counter_name, counter_result))
|
||||||
|
|
||||||
|
-- Save it in the result
|
||||||
|
result[object_name][counter_name] = counter_result
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Blank out the database
|
||||||
|
result['title_database'] = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
msrpc.stop_smb(smbstate)
|
||||||
|
|
||||||
|
return true, result
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
@@ -289,7 +289,7 @@ end
|
|||||||
-- (for the pointer data), or ALL (for both together). Generally, unless the
|
-- (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
|
-- referent_id is split from the data (for example, in an array), you will want
|
||||||
-- ALL.
|
-- ALL.
|
||||||
--@param data The data packet being processed.
|
--@param data The data being processed.
|
||||||
--@param pos The position within <code>data</code>
|
--@param pos The position within <code>data</code>
|
||||||
--@param func The function that's used to process the body data (only called if it isn't a null
|
--@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.
|
-- pointer). This function has to conform to a specific prototype, see above.
|
||||||
@@ -311,6 +311,10 @@ local function unmarshall_ptr(location, data, pos, func, args, result)
|
|||||||
if(location == HEAD or location == ALL) then
|
if(location == HEAD or location == ALL) then
|
||||||
local referent_id
|
local referent_id
|
||||||
pos, referent_id = bin.unpack("<I", data, pos)
|
pos, referent_id = bin.unpack("<I", data, pos)
|
||||||
|
if(referent_id == nil) then
|
||||||
|
stdnse.print_debug(1, "MSRPC: ERROR: Ran off the end of a packet in unmarshall_ptr(). Please report!")
|
||||||
|
end
|
||||||
|
|
||||||
if(location == HEAD) then
|
if(location == HEAD) then
|
||||||
if(referent_id == 0) then
|
if(referent_id == 0) then
|
||||||
result = false
|
result = false
|
||||||
@@ -435,7 +439,7 @@ end
|
|||||||
-- whether or not to null-terminate a string, or whether or not to pad an int16. If different types are
|
-- 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.
|
-- required, you're probably out of luck.
|
||||||
--
|
--
|
||||||
--@param data The data packet being processed.
|
--@param data The data being processed.
|
||||||
--@param pos The position within <code>data</code>.
|
--@param pos The position within <code>data</code>.
|
||||||
--@param count The number of elements in the array.
|
--@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;
|
--@param func The function to call to unmarshall each parameter. Has to match a specific prototype;
|
||||||
@@ -454,6 +458,9 @@ local function unmarshall_array(data, pos, count, func, args)
|
|||||||
end
|
end
|
||||||
|
|
||||||
pos, max_count = bin.unpack("<I", data, pos)
|
pos, max_count = bin.unpack("<I", data, pos)
|
||||||
|
if(max_count == nil) then
|
||||||
|
stdnse.print_debug(1, "MSRPC: ERROR: Ran off the end of a packet in unmarshall_array(). Please report!")
|
||||||
|
end
|
||||||
|
|
||||||
-- Unmarshall the header, which will be referent_ids and base types.
|
-- Unmarshall the header, which will be referent_ids and base types.
|
||||||
for i = 1, count, 1 do
|
for i = 1, count, 1 do
|
||||||
@@ -483,7 +490,7 @@ end
|
|||||||
-- func(location, data, pos, result, <args>)
|
-- func(location, data, pos, result, <args>)
|
||||||
--</code>
|
--</code>
|
||||||
--
|
--
|
||||||
--@param data The data packet being processed.
|
--@param data The data being processed.
|
||||||
--@param pos The position within <code>data</code>.
|
--@param pos The position within <code>data</code>.
|
||||||
--@param func The function to call to unmarshall each parameter. Has to match a specific prototype;
|
--@param func The function to call to unmarshall each parameter. Has to match a specific prototype;
|
||||||
-- see the function comment.
|
-- see the function comment.
|
||||||
@@ -594,6 +601,9 @@ function unmarshall_unicode(data, pos, do_null)
|
|||||||
end
|
end
|
||||||
|
|
||||||
pos, max, offset, actual = bin.unpack("<III", data, pos)
|
pos, max, offset, actual = bin.unpack("<III", data, pos)
|
||||||
|
if(actual == nil) then
|
||||||
|
stdnse.print_debug(1, "MSRPC: ERROR: Ran off the end of a packet in unmarshall_unicode(). Please report!")
|
||||||
|
end
|
||||||
|
|
||||||
pos, str = unicode_to_string(data, pos, actual, do_null, true)
|
pos, str = unicode_to_string(data, pos, actual, do_null, true)
|
||||||
|
|
||||||
@@ -604,7 +614,7 @@ end
|
|||||||
|
|
||||||
---Unmarshall a pointer to a unicode string.
|
---Unmarshall a pointer to a unicode string.
|
||||||
--
|
--
|
||||||
--@param data The data packet being processed.
|
--@param data The data being processed.
|
||||||
--@param pos The position within <code>data</code>.
|
--@param pos The position within <code>data</code>.
|
||||||
--@param do_null [optional] Assumes a null is at the end of the string. Default false.
|
--@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.
|
--@return (pos, result) The new position and the string.
|
||||||
@@ -695,7 +705,7 @@ end
|
|||||||
|
|
||||||
--- Unmarshall an int64. See <code>marshall_int64</code> for more information.
|
--- Unmarshall an int64. See <code>marshall_int64</code> for more information.
|
||||||
--
|
--
|
||||||
--@param data The data packet being processed.
|
--@param data The data being processed.
|
||||||
--@param pos The position within <code>data</code>.
|
--@param pos The position within <code>data</code>.
|
||||||
--@return (pos, int64) The new position, and the value.
|
--@return (pos, int64) The new position, and the value.
|
||||||
function unmarshall_int64(data, pos)
|
function unmarshall_int64(data, pos)
|
||||||
@@ -703,6 +713,9 @@ function unmarshall_int64(data, pos)
|
|||||||
|
|
||||||
stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_int64()"))
|
stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_int64()"))
|
||||||
pos, value = bin.unpack("<l", data, pos)
|
pos, value = bin.unpack("<l", data, pos)
|
||||||
|
if(value == nil) then
|
||||||
|
stdnse.print_debug(1, "MSRPC: ERROR: Ran off the end of a packet in unmarshall_int64(). Please report!")
|
||||||
|
end
|
||||||
stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_int64()"))
|
stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_int64()"))
|
||||||
|
|
||||||
return pos, value
|
return pos, value
|
||||||
@@ -710,7 +723,7 @@ end
|
|||||||
|
|
||||||
--- Unmarshall an int32. See <code>marshall_int32</code> for more information.
|
--- Unmarshall an int32. See <code>marshall_int32</code> for more information.
|
||||||
--
|
--
|
||||||
--@param data The data packet being processed.
|
--@param data The data being processed.
|
||||||
--@param pos The position within <code>data</code>.
|
--@param pos The position within <code>data</code>.
|
||||||
--@return (pos, int32) The new position, and the value.
|
--@return (pos, int32) The new position, and the value.
|
||||||
function unmarshall_int32(data, pos)
|
function unmarshall_int32(data, pos)
|
||||||
@@ -718,6 +731,9 @@ function unmarshall_int32(data, pos)
|
|||||||
|
|
||||||
stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_int32()"))
|
stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_int32()"))
|
||||||
pos, value = bin.unpack("<I", data, pos)
|
pos, value = bin.unpack("<I", data, pos)
|
||||||
|
if(value == nil) then
|
||||||
|
stdnse.print_debug(1, "MSRPC: ERROR: Ran off the end of a packet in unmarshall_int32(). Please report!")
|
||||||
|
end
|
||||||
stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_int32()"))
|
stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_int32()"))
|
||||||
|
|
||||||
return pos, value
|
return pos, value
|
||||||
@@ -735,6 +751,9 @@ function unmarshall_int16(data, pos, pad)
|
|||||||
stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_int16()"))
|
stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_int16()"))
|
||||||
|
|
||||||
pos, value = bin.unpack("<S", data, pos)
|
pos, value = bin.unpack("<S", data, pos)
|
||||||
|
if(value == nil) then
|
||||||
|
stdnse.print_debug(1, "MSRPC: ERROR: Ran off the end of a packet in unmarshall_int16(). Please report!")
|
||||||
|
end
|
||||||
|
|
||||||
if(pad == nil or pad == true) then
|
if(pad == nil or pad == true) then
|
||||||
pos = pos + 2
|
pos = pos + 2
|
||||||
@@ -757,6 +776,9 @@ function unmarshall_int8(data, pos, pad)
|
|||||||
stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_int8()"))
|
stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_int8()"))
|
||||||
|
|
||||||
pos, value = bin.unpack("<C", data, pos)
|
pos, value = bin.unpack("<C", data, pos)
|
||||||
|
if(value == nil) then
|
||||||
|
stdnse.print_debug(1, "MSRPC: ERROR: Ran off the end of a packet in unmarshall_int8(). Please report!")
|
||||||
|
end
|
||||||
|
|
||||||
if(pad == nil or pad == true) then
|
if(pad == nil or pad == true) then
|
||||||
pos = pos + 3
|
pos = pos + 3
|
||||||
@@ -918,7 +940,14 @@ function unmarshall_int8_array(data, pos, pad)
|
|||||||
stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_int8_array()"))
|
stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_int8_array()"))
|
||||||
|
|
||||||
pos, max, offset, actual = bin.unpack("<III", data, pos)
|
pos, max, offset, actual = bin.unpack("<III", data, pos)
|
||||||
|
if(actual == nil) then
|
||||||
|
stdnse.print_debug(1, "MSRPC: ERROR: Ran off the end of a packet in unmarshall_int8_array(). Please report!")
|
||||||
|
end
|
||||||
|
|
||||||
pos, str = bin.unpack("<A"..actual, data, pos)
|
pos, str = bin.unpack("<A"..actual, data, pos)
|
||||||
|
if(str == nil) then
|
||||||
|
stdnse.print_debug(1, "MSRPC: ERROR: Ran off the end of a packet in unmarshall_int8_array() [2]. Please report!")
|
||||||
|
end
|
||||||
|
|
||||||
-- Do the alignment (note the "- 1", it's there because of 1-based arrays)
|
-- Do the alignment (note the "- 1", it's there because of 1-based arrays)
|
||||||
if(pad == nil or pad == true) then
|
if(pad == nil or pad == true) then
|
||||||
@@ -997,6 +1026,9 @@ function unmarshall_NTTIME(data, pos)
|
|||||||
stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_NTTIME()"))
|
stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_NTTIME()"))
|
||||||
|
|
||||||
pos, time = bin.unpack("<L", data, pos)
|
pos, time = bin.unpack("<L", data, pos)
|
||||||
|
if(time == nil) then
|
||||||
|
stdnse.print_debug(1, "MSRPC: ERROR: Ran off the end of a packet in unmarshall_NTTIME(). Please report!")
|
||||||
|
end
|
||||||
|
|
||||||
if(time ~= 0) then
|
if(time ~= 0) then
|
||||||
time = (time / 10000000) - 11644473600
|
time = (time / 10000000) - 11644473600
|
||||||
@@ -1035,6 +1067,36 @@ function unmarshall_NTTIME_ptr(data, pos)
|
|||||||
return pos, time
|
return pos, time
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---Unmarshall a SYSTEMTIME structure, converting it to a standard representation. The structure is a
|
||||||
|
-- follows:
|
||||||
|
--
|
||||||
|
-- <code>
|
||||||
|
-- typedef struct _SYSTEMTIME {
|
||||||
|
-- WORD wYear;
|
||||||
|
-- WORD wMonth;
|
||||||
|
-- WORD wDayOfWeek;
|
||||||
|
-- WORD wDay;
|
||||||
|
-- WORD wHour;
|
||||||
|
-- WORD wMinute;
|
||||||
|
-- WORD wSecond;
|
||||||
|
-- WORD wMilliseconds;
|
||||||
|
-- } SYSTEMTIME
|
||||||
|
-- </code>
|
||||||
|
--
|
||||||
|
--@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_SYSTEMTIME(data, pos)
|
||||||
|
local date = {}
|
||||||
|
|
||||||
|
pos, date['year'], date['month'], _, date['day'], date['hour'], date['min'], date['sec'], _ = bin.unpack("<SSSSSSSS", data, pos)
|
||||||
|
if(date['sec'] == nil) then
|
||||||
|
stdnse.print_debug(1, "MSRPC: ERROR: Ran off the end of a packet in unmarshall_SYSTEMTIME(). Please report!")
|
||||||
|
end
|
||||||
|
|
||||||
|
return pos, os.time(date)
|
||||||
|
end
|
||||||
|
|
||||||
---Unmarshalls a <code>hyper</code>. I have no idea what a <code>hyper</code> is, just that it seems
|
---Unmarshalls a <code>hyper</code>. I have no idea what a <code>hyper</code> is, just that it seems
|
||||||
-- to be a 64-bit data type used for measuring time, and that the units happen to be negative
|
-- 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.
|
-- microseconds. This function converts the value to seconds and returns it.
|
||||||
@@ -1143,6 +1205,7 @@ end
|
|||||||
--@param pos The position within the data.
|
--@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
|
--@param table The table to use for lookups. The keys should be the names, and the values should be
|
||||||
-- the numbers.
|
-- the numbers.
|
||||||
|
--@return (pos, array) The new position, and a table representing the enumeration values.
|
||||||
local function unmarshall_Enum32_array(data, pos, table)
|
local function unmarshall_Enum32_array(data, pos, table)
|
||||||
local array = {}
|
local array = {}
|
||||||
local i, v
|
local i, v
|
||||||
@@ -1161,6 +1224,23 @@ local function unmarshall_Enum32_array(data, pos, table)
|
|||||||
return pos, array
|
return pos, array
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---Unmarshall raw data.
|
||||||
|
--@param data The data packet.
|
||||||
|
--@param pos The position within the data.
|
||||||
|
--@param length The number of bytes to unmarshall.
|
||||||
|
--@return (pos, data) The new position in the packet, and a string representing the raw data.
|
||||||
|
function unmarshall_raw(data, pos, length)
|
||||||
|
local val
|
||||||
|
stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_raw()"))
|
||||||
|
|
||||||
|
pos, val = bin.unpack(string.format("A%d", length), data, pos)
|
||||||
|
if(val == nil) then
|
||||||
|
stdnse.print_debug(1, "MSRPC: ERROR: Ran off the end of a packet in unmarshall_raw(). Please report!")
|
||||||
|
end
|
||||||
|
|
||||||
|
stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_raw()"))
|
||||||
|
return pos, val
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
-------------------------------------
|
-------------------------------------
|
||||||
@@ -1202,6 +1282,9 @@ local function unmarshall_guid(data, pos)
|
|||||||
stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_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("<ISSA2A6", data, pos)
|
pos, guid['time_low'], guid['time_high'], guid['time_hi_and_version'], guid['clock_seq'], guid['node'] = bin.unpack("<ISSA2A6", data, pos)
|
||||||
|
if(guid['node'] == nil) then
|
||||||
|
stdnse.print_debug(1, "MSRPC: ERROR: Ran off the end of a packet in unmarshall_guid(). Please report!")
|
||||||
|
end
|
||||||
|
|
||||||
stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_guid()"))
|
stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_guid()"))
|
||||||
return pos, guid
|
return pos, guid
|
||||||
@@ -1237,7 +1320,7 @@ function unmarshall_policy_handle(data, pos)
|
|||||||
local policy_handle = {}
|
local policy_handle = {}
|
||||||
stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_policy_handle()"))
|
stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_policy_handle()"))
|
||||||
|
|
||||||
pos, policy_handle['handle_type'] = bin.unpack("<I", data, pos)
|
pos, policy_handle['handle_type'] = unmarshall_int32(data, pos)
|
||||||
pos, policy_handle['uuid'] = unmarshall_guid(data, pos)
|
pos, policy_handle['uuid'] = unmarshall_guid(data, pos)
|
||||||
|
|
||||||
stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_policy_handle()"))
|
stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_policy_handle()"))
|
||||||
@@ -1260,7 +1343,7 @@ end
|
|||||||
-- } dom_sid;
|
-- } dom_sid;
|
||||||
--</code>
|
--</code>
|
||||||
--
|
--
|
||||||
--@param data The data packet being processed.
|
--@param data The data being processed.
|
||||||
--@param pos The position within <code>data</code>.
|
--@param pos The position within <code>data</code>.
|
||||||
--@return (pos, result) The new position in <code>data</code>, and a table representing the datatype.
|
--@return (pos, result) The new position in <code>data</code>, and a table representing the datatype.
|
||||||
function unmarshall_dom_sid2(data, pos)
|
function unmarshall_dom_sid2(data, pos)
|
||||||
@@ -1269,16 +1352,20 @@ function unmarshall_dom_sid2(data, pos)
|
|||||||
|
|
||||||
-- Read the SID from the packet
|
-- Read the SID from the packet
|
||||||
local sid = {}
|
local sid = {}
|
||||||
pos, sid['count'] = bin.unpack("<I", data, pos)
|
pos, sid['count'] = unmarshall_int32(data, pos)
|
||||||
pos, sid['sid_rev_num'] = bin.unpack("<C", data, pos)
|
pos, sid['sid_rev_num'] = unmarshall_int8(data, pos, false)
|
||||||
pos, sid['num_auths'] = bin.unpack("<C", data, pos)
|
pos, sid['num_auths'] = unmarshall_int8(data, pos, false)
|
||||||
|
|
||||||
-- Note that authority is big endian (I guess it's an array, not really an integer like we're handling it)
|
-- Note that authority is big endian (I guess it's an array, not really an integer like we're handling it)
|
||||||
pos, sid['authority_high'], sid['authority_low'] = bin.unpack(">SI", data, pos)
|
pos, sid['authority_high'], sid['authority_low'] = bin.unpack(">SI", data, pos)
|
||||||
|
if(sid['authority_low'] == nil) then
|
||||||
|
stdnse.print_debug(1, "MSRPC: ERROR: Ran off the end of a packet in unmarshall_dom_sid2(). Please report!")
|
||||||
|
end
|
||||||
sid['authority'] = bit.bor(bit.lshift(sid['authority_high'], 32), sid['authority_low'])
|
sid['authority'] = bit.bor(bit.lshift(sid['authority_high'], 32), sid['authority_low'])
|
||||||
|
|
||||||
sid['sub_auths'] = {}
|
sid['sub_auths'] = {}
|
||||||
for i = 1, sid['num_auths'], 1 do
|
for i = 1, sid['num_auths'], 1 do
|
||||||
pos, sid['sub_auths'][i] = bin.unpack("<I", data, pos)
|
pos, sid['sub_auths'][i] = unmarshall_int32(data, pos)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Convert the SID to a string
|
-- Convert the SID to a string
|
||||||
@@ -1294,7 +1381,7 @@ end
|
|||||||
---Unmarshall a pointer to a <code>dom_sid2</code> struct. See the <code>unmarshall_dom_sid2</code> function
|
---Unmarshall a pointer to a <code>dom_sid2</code> struct. See the <code>unmarshall_dom_sid2</code> function
|
||||||
-- for more information.
|
-- for more information.
|
||||||
--
|
--
|
||||||
--@param data The data packet being processed.
|
--@param data The data being processed.
|
||||||
--@param pos The position within <code>data</code>.
|
--@param pos The position within <code>data</code>.
|
||||||
--@return (pos, result) The new position in <code>data</code>, and a table representing the datatype.
|
--@return (pos, result) The new position in <code>data</code>, and a table representing the datatype.
|
||||||
function unmarshall_dom_sid2_ptr(data, pos)
|
function unmarshall_dom_sid2_ptr(data, pos)
|
||||||
@@ -1452,7 +1539,9 @@ local function unmarshall_lsa_String_internal(location, data, pos, result)
|
|||||||
stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_lsa_String_internal()"))
|
stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_lsa_String_internal()"))
|
||||||
|
|
||||||
if(location == HEAD or location == ALL) then
|
if(location == HEAD or location == ALL) then
|
||||||
pos, length, size = bin.unpack("<SS", data, pos)
|
pos, length = unmarshall_int16(data, pos, false)
|
||||||
|
pos, size = unmarshall_int16(data, pos, false)
|
||||||
|
|
||||||
pos, str = unmarshall_ptr(HEAD, data, pos, unmarshall_unicode, {false})
|
pos, str = unmarshall_ptr(HEAD, data, pos, unmarshall_unicode, {false})
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1702,7 +1791,7 @@ end
|
|||||||
-- (for nothing, since this isn't a pointer), or ALL (for the data). Generally, unless the
|
-- (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
|
-- referent_id is split from the data (for example, in an array), you will want
|
||||||
-- ALL.
|
-- ALL.
|
||||||
--@param data The data packet being processed.
|
--@param data The data being processed.
|
||||||
--@param pos The position within <code>data</code>.
|
--@param pos The position within <code>data</code>.
|
||||||
--@param result This is required when unmarshalling the BODY section, which always comes after
|
--@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
|
-- unmarshalling the HEAD. It is the result returned for this parameter during the
|
||||||
@@ -1779,7 +1868,7 @@ end
|
|||||||
-- (for nothing, since this isn't a pointer), or ALL (for the data). Generally, unless the
|
-- (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
|
-- referent_id is split from the data (for example, in an array), you will want
|
||||||
-- ALL.
|
-- ALL.
|
||||||
--@param data The data packet being processed.
|
--@param data The data being processed.
|
||||||
--@param pos The position within <code>data</code>.
|
--@param pos The position within <code>data</code>.
|
||||||
--@param result This is required when unmarshalling the BODY section, which always comes after
|
--@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
|
-- unmarshalling the HEAD. It is the result returned for this parameter during the
|
||||||
@@ -1853,7 +1942,7 @@ end
|
|||||||
-- (for nothing, since this isn't a pointer), or ALL (for the data). Generally, unless the
|
-- (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
|
-- referent_id is split from the data (for example, in an array), you will want
|
||||||
-- ALL.
|
-- ALL.
|
||||||
--@param data The data packet being processed.
|
--@param data The data being processed.
|
||||||
--@param pos The position within <code>data</code>.
|
--@param pos The position within <code>data</code>.
|
||||||
--@param result This is required when unmarshalling the BODY section, which always comes after
|
--@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
|
-- unmarshalling the HEAD. It is the result returned for this parameter during the
|
||||||
@@ -1866,7 +1955,9 @@ local function unmarshall_lsa_StringLarge(location, data, pos, result)
|
|||||||
stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_lsa_StringLarge()"))
|
stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_lsa_StringLarge()"))
|
||||||
|
|
||||||
if(location == HEAD or location == ALL) then
|
if(location == HEAD or location == ALL) then
|
||||||
pos, length, size = bin.unpack("<SS", data, pos)
|
pos, length = unmarshall_int16(data, pos, false)
|
||||||
|
pos, size = unmarshall_int16(data, pos, false)
|
||||||
|
|
||||||
pos, str = unmarshall_ptr(HEAD, data, pos, unmarshall_unicode, {false})
|
pos, str = unmarshall_ptr(HEAD, data, pos, unmarshall_unicode, {false})
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1891,7 +1982,7 @@ end
|
|||||||
-- (for nothing, since this isn't a pointer), or ALL (for the data). Generally, unless the
|
-- (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
|
-- referent_id is split from the data (for example, in an array), you will want
|
||||||
-- ALL.
|
-- ALL.
|
||||||
--@param data The data packet being processed.
|
--@param data The data being processed.
|
||||||
--@param pos The position within <code>data</code>.
|
--@param pos The position within <code>data</code>.
|
||||||
--@param result This is required when unmarshalling the BODY section, which always comes after
|
--@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
|
-- unmarshalling the HEAD. It is the result returned for this parameter during the
|
||||||
@@ -1928,7 +2019,7 @@ end
|
|||||||
-- } lsa_RefDomainList;
|
-- } lsa_RefDomainList;
|
||||||
--</code>
|
--</code>
|
||||||
--
|
--
|
||||||
--@param data The data packet being processed.
|
--@param data The data being processed.
|
||||||
--@param pos The position within <code>data</code>.
|
--@param pos The position within <code>data</code>.
|
||||||
--@return (pos, result) The new position in <code>data</code>, and a table representing the datatype.
|
--@return (pos, result) The new position in <code>data</code>, and a table representing the datatype.
|
||||||
function unmarshall_lsa_RefDomainList(data, pos)
|
function unmarshall_lsa_RefDomainList(data, pos)
|
||||||
@@ -1950,7 +2041,7 @@ end
|
|||||||
---Unmarshall a pointer to a <code>lsa_RefDomainList</code>. See the <code>unmarshall_lsa_RefDomainList</code> function
|
---Unmarshall a pointer to a <code>lsa_RefDomainList</code>. See the <code>unmarshall_lsa_RefDomainList</code> function
|
||||||
-- for more information.
|
-- for more information.
|
||||||
--
|
--
|
||||||
--@param data The data packet being processed.
|
--@param data The data being processed.
|
||||||
--@param pos The position within <code>data</code>.
|
--@param pos The position within <code>data</code>.
|
||||||
--@return (pos, result) The new position in <code>data</code>, and a table representing the datatype.
|
--@return (pos, result) The new position in <code>data</code>, and a table representing the datatype.
|
||||||
function unmarshall_lsa_RefDomainList_ptr(data, pos)
|
function unmarshall_lsa_RefDomainList_ptr(data, pos)
|
||||||
@@ -1972,7 +2063,7 @@ end
|
|||||||
-- } lsa_TransSidArray2;
|
-- } lsa_TransSidArray2;
|
||||||
--</code>
|
--</code>
|
||||||
--
|
--
|
||||||
--@param data The data packet being processed.
|
--@param data The data being processed.
|
||||||
--@param pos The position within <code>data</code>.
|
--@param pos The position within <code>data</code>.
|
||||||
--@return (pos, result) The new position in <code>data</code>, and a table representing the datatype.
|
--@return (pos, result) The new position in <code>data</code>, and a table representing the datatype.
|
||||||
function unmarshall_lsa_TransSidArray2(data, pos)
|
function unmarshall_lsa_TransSidArray2(data, pos)
|
||||||
@@ -2139,7 +2230,7 @@ end
|
|||||||
---Unmarshall a <code>lsa_TransNameArray2</code> structure. See the <code>marshall_lsa_TransNameArray2</code> for more
|
---Unmarshall a <code>lsa_TransNameArray2</code> structure. See the <code>marshall_lsa_TransNameArray2</code> for more
|
||||||
-- information.
|
-- information.
|
||||||
--
|
--
|
||||||
--@param data The data packet being processed.
|
--@param data The data being processed.
|
||||||
--@param pos The position within <code>data</code>.
|
--@param pos The position within <code>data</code>.
|
||||||
--@return (pos, result) The new position in <code>data</code>, and a table representing the datatype.
|
--@return (pos, result) The new position in <code>data</code>, and a table representing the datatype.
|
||||||
function unmarshall_lsa_TransNameArray2(data, pos)
|
function unmarshall_lsa_TransNameArray2(data, pos)
|
||||||
@@ -2403,7 +2494,9 @@ function unmarshall_winreg_StringBuf(data, pos)
|
|||||||
local str
|
local str
|
||||||
stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_winreg_StringBuf()"))
|
stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_winreg_StringBuf()"))
|
||||||
|
|
||||||
pos, length, size = bin.unpack("<SS", data, pos)
|
pos, length = unmarshall_int16(data, pos, false)
|
||||||
|
pos, size = unmarshall_int16(data, pos, false)
|
||||||
|
|
||||||
pos, str = unmarshall_ptr(ALL, data, pos, unmarshall_unicode, {true})
|
pos, str = unmarshall_ptr(ALL, data, pos, unmarshall_unicode, {true})
|
||||||
|
|
||||||
stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_winreg_StringBuf()"))
|
stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_winreg_StringBuf()"))
|
||||||
@@ -2819,7 +2912,7 @@ function unmarshall_srvsvc_NetShareCtr0(data, pos)
|
|||||||
local result = {}
|
local result = {}
|
||||||
stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_srvsvc_NetShareCtr0()"))
|
stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_srvsvc_NetShareCtr0()"))
|
||||||
|
|
||||||
pos, count = bin.unpack("<I", data, pos)
|
pos, count = unmarshall_int32(data, pos)
|
||||||
|
|
||||||
pos, result['array'] = unmarshall_ptr(ALL, data, pos, unmarshall_array, {count, unmarshall_srvsvc_NetShareInfo0, {}})
|
pos, result['array'] = unmarshall_ptr(ALL, data, pos, unmarshall_array, {count, unmarshall_srvsvc_NetShareInfo0, {}})
|
||||||
|
|
||||||
@@ -2972,7 +3065,7 @@ function unmarshall_srvsvc_NetShareCtr(data, pos)
|
|||||||
local result
|
local result
|
||||||
stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_srv_NetShareCtr()"))
|
stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_srv_NetShareCtr()"))
|
||||||
|
|
||||||
pos, level = bin.unpack("<I", data, pos)
|
pos, level = unmarshall_int32(data, pos)
|
||||||
|
|
||||||
if(level == 0) then
|
if(level == 0) then
|
||||||
pos, result = unmarshall_ptr(ALL, data, pos, unmarshall_srvsvc_NetShareCtr0, {})
|
pos, result = unmarshall_ptr(ALL, data, pos, unmarshall_srvsvc_NetShareCtr0, {})
|
||||||
@@ -3008,7 +3101,7 @@ end
|
|||||||
--
|
--
|
||||||
--@param level The level to request. Different levels will return different results, but also require
|
--@param level The level to request. Different levels will return different results, but also require
|
||||||
-- different access levels to call.
|
-- different access levels to call.
|
||||||
--@param data The data packet being processed.
|
--@param data The data being processed.
|
||||||
--@param pos The position within <code>data</code>.
|
--@param pos The position within <code>data</code>.
|
||||||
--@return (pos, result) The new position in <code>data</code>, and a table representing the datatype. This may be
|
--@return (pos, result) The new position in <code>data</code>, and a table representing the datatype. This may be
|
||||||
-- <code>nil</code> if there was an error.
|
-- <code>nil</code> if there was an error.
|
||||||
@@ -3155,7 +3248,7 @@ function unmarshall_srvsvc_NetSessCtr10(data, pos)
|
|||||||
local result = {}
|
local result = {}
|
||||||
stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_srvsvc_NetSessCtr10()"))
|
stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_srvsvc_NetSessCtr10()"))
|
||||||
|
|
||||||
pos, count = bin.unpack("<I", data, pos)
|
pos, count = unmarshall_int32(data, pos)
|
||||||
|
|
||||||
pos, result['array'] = unmarshall_ptr(ALL, data, pos, unmarshall_array, {count, unmarshall_srvsvc_NetSessInfo10, {}})
|
pos, result['array'] = unmarshall_ptr(ALL, data, pos, unmarshall_array, {count, unmarshall_srvsvc_NetSessInfo10, {}})
|
||||||
|
|
||||||
@@ -3200,7 +3293,7 @@ end
|
|||||||
|
|
||||||
---Unmarshall the top-level NetShareCtr. This is a union; see the marshall function for more information.
|
---Unmarshall the top-level NetShareCtr. This is a union; see the marshall function for more information.
|
||||||
--
|
--
|
||||||
--@param data The data packet being processed.
|
--@param data The data being processed.
|
||||||
--@param pos The position within <code>data</code>
|
--@param pos The position within <code>data</code>
|
||||||
--@return (pos, result) The new position in <code>data</code>, and a table representing the datatype. Can be
|
--@return (pos, result) The new position in <code>data</code>, and a table representing the datatype. Can be
|
||||||
-- <code>nil</code> if there's an error.
|
-- <code>nil</code> if there's an error.
|
||||||
@@ -3249,7 +3342,7 @@ end
|
|||||||
--
|
--
|
||||||
-- Note that Wireshark (at least, the version I'm using, 1.0.3) gets this wrong, so be careful.
|
-- 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 data The data being processed.
|
||||||
--@param pos The position within <code>data</code>
|
--@param pos The position within <code>data</code>
|
||||||
--@return (pos, result) The new position in <code>data</code>, and a table representing the datatype.
|
--@return (pos, result) The new position in <code>data</code>, and a table representing the datatype.
|
||||||
function unmarshall_srvsvc_Statistics(data, pos)
|
function unmarshall_srvsvc_Statistics(data, pos)
|
||||||
@@ -3283,7 +3376,7 @@ end
|
|||||||
--
|
--
|
||||||
-- See <code>unmarshall_srvsvc_Statistics</code> for more information.
|
-- See <code>unmarshall_srvsvc_Statistics</code> for more information.
|
||||||
--
|
--
|
||||||
--@param data The data packet being processed.
|
--@param data The data being processed.
|
||||||
--@param pos The position within <code>data</code>
|
--@param pos The position within <code>data</code>
|
||||||
--@return (pos, result) The new position in <code>data</code>, and a table representing the datatype.
|
--@return (pos, result) The new position in <code>data</code>, and a table representing the datatype.
|
||||||
function unmarshall_srvsvc_Statistics_ptr(data, pos)
|
function unmarshall_srvsvc_Statistics_ptr(data, pos)
|
||||||
@@ -3615,7 +3708,7 @@ end
|
|||||||
-- (for nothing, since this isn't a pointer), or ALL (for the data). Generally, unless the
|
-- (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
|
-- referent_id is split from the data (for example, in an array), you will want
|
||||||
-- ALL.
|
-- ALL.
|
||||||
--@param data The data packet being processed.
|
--@param data The data being processed.
|
||||||
--@param pos The position within <code>data</code>.
|
--@param pos The position within <code>data</code>.
|
||||||
--@param result This is required when unmarshalling the BODY section, which always comes after
|
--@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
|
-- unmarshalling the HEAD. It is the result returned for this parameter during the
|
||||||
@@ -3651,7 +3744,7 @@ end
|
|||||||
-- } samr_SamArray;
|
-- } samr_SamArray;
|
||||||
--</code>
|
--</code>
|
||||||
--
|
--
|
||||||
--@param data The data packet being processed.
|
--@param data The data being processed.
|
||||||
--@param pos The position within <code>data</code>.
|
--@param pos The position within <code>data</code>.
|
||||||
--@return (pos, result) The new position in <code>data</code>, and a table representing the datatype.
|
--@return (pos, result) The new position in <code>data</code>, and a table representing the datatype.
|
||||||
function unmarshall_samr_SamArray(data, pos)
|
function unmarshall_samr_SamArray(data, pos)
|
||||||
@@ -3668,7 +3761,7 @@ end
|
|||||||
---Unmarshall a pointer to a <code>samr_SamArray</code> type. See <code>unmarshall_samr_SamArray</code> for
|
---Unmarshall a pointer to a <code>samr_SamArray</code> type. See <code>unmarshall_samr_SamArray</code> for
|
||||||
-- more information.
|
-- more information.
|
||||||
--
|
--
|
||||||
--@param data The data packet being processed.
|
--@param data The data being processed.
|
||||||
--@param pos The position within <code>data</code>.
|
--@param pos The position within <code>data</code>.
|
||||||
--@return (pos, result) The new position in <code>data</code>, and a table representing the datatype.
|
--@return (pos, result) The new position in <code>data</code>, and a table representing the datatype.
|
||||||
function unmarshall_samr_SamArray_ptr(data, pos)
|
function unmarshall_samr_SamArray_ptr(data, pos)
|
||||||
@@ -3698,7 +3791,7 @@ end
|
|||||||
-- (for nothing, since this isn't a pointer), or ALL (for the data). Generally, unless the
|
-- (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
|
-- referent_id is split from the data (for example, in an array), you will want
|
||||||
-- ALL.
|
-- ALL.
|
||||||
--@param data The data packet being processed.
|
--@param data The data being processed.
|
||||||
--@param pos The position within <code>data</code>.
|
--@param pos The position within <code>data</code>.
|
||||||
--@param result This is required when unmarshalling the BODY section, which always comes after
|
--@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
|
-- unmarshalling the HEAD. It is the result returned for this parameter during the
|
||||||
@@ -3740,7 +3833,7 @@ end
|
|||||||
-- } samr_DispInfoGeneral;
|
-- } samr_DispInfoGeneral;
|
||||||
--</code>
|
--</code>
|
||||||
--
|
--
|
||||||
--@param data The data packet being processed.
|
--@param data The data being processed.
|
||||||
--@param pos The position within <code>data</code>.
|
--@param pos The position within <code>data</code>.
|
||||||
--@return (pos, result) The new position in <code>data</code>, and a table representing the datatype.
|
--@return (pos, result) The new position in <code>data</code>, and a table representing the datatype.
|
||||||
function unmarshall_samr_DispInfoGeneral(data, pos)
|
function unmarshall_samr_DispInfoGeneral(data, pos)
|
||||||
@@ -3767,7 +3860,7 @@ end
|
|||||||
-- } samr_DispInfo;
|
-- } samr_DispInfo;
|
||||||
--</code>
|
--</code>
|
||||||
--
|
--
|
||||||
--@param data The data packet being processed.
|
--@param data The data being processed.
|
||||||
--@param pos The position within <code>data</code>.
|
--@param pos The position within <code>data</code>.
|
||||||
--@return (pos, result) The new position in <code>data</code>, and a table representing the datatype. It may also return
|
--@return (pos, result) The new position in <code>data</code>, and a table representing the datatype. It may also return
|
||||||
-- <code>nil</code>, if there was an error.
|
-- <code>nil</code>, if there was an error.
|
||||||
@@ -3802,7 +3895,7 @@ end
|
|||||||
-- } samr_DomInfo1;
|
-- } samr_DomInfo1;
|
||||||
--</code>
|
--</code>
|
||||||
--
|
--
|
||||||
--@param data The data packet being processed.
|
--@param data The data being processed.
|
||||||
--@param pos The position within <code>data</code>.
|
--@param pos The position within <code>data</code>.
|
||||||
--@return (pos, result) The new position in <code>data</code>, and a table representing the datatype.
|
--@return (pos, result) The new position in <code>data</code>, and a table representing the datatype.
|
||||||
function unmarshall_samr_DomInfo1(data, pos)
|
function unmarshall_samr_DomInfo1(data, pos)
|
||||||
@@ -3828,7 +3921,7 @@ end
|
|||||||
-- } samr_DomInfo8;
|
-- } samr_DomInfo8;
|
||||||
--</code>
|
--</code>
|
||||||
--
|
--
|
||||||
--@param data The data packet being processed.
|
--@param data The data being processed.
|
||||||
--@param pos The position within <code>data</code>.
|
--@param pos The position within <code>data</code>.
|
||||||
--@return (pos, result) The new position in <code>data</code>, and a table representing the datatype.
|
--@return (pos, result) The new position in <code>data</code>, and a table representing the datatype.
|
||||||
function unmarshall_samr_DomInfo8(data, pos)
|
function unmarshall_samr_DomInfo8(data, pos)
|
||||||
@@ -3852,7 +3945,7 @@ end
|
|||||||
-- } samr_DomInfo12;
|
-- } samr_DomInfo12;
|
||||||
--</code>
|
--</code>
|
||||||
--
|
--
|
||||||
--@param data The data packet being processed.
|
--@param data The data being processed.
|
||||||
--@param pos The position within <code>data</code>.
|
--@param pos The position within <code>data</code>.
|
||||||
--@return (pos, result) The new position in <code>data</code>, and a table representing the datatype.
|
--@return (pos, result) The new position in <code>data</code>, and a table representing the datatype.
|
||||||
function unmarshall_samr_DomInfo12(data, pos)
|
function unmarshall_samr_DomInfo12(data, pos)
|
||||||
@@ -3886,7 +3979,7 @@ end
|
|||||||
-- } samr_DomainInfo;
|
-- } samr_DomainInfo;
|
||||||
--</code>
|
--</code>
|
||||||
--
|
--
|
||||||
--@param data The data packet being processed.
|
--@param data The data being processed.
|
||||||
--@param pos The position within <code>data</code>.
|
--@param pos The position within <code>data</code>.
|
||||||
--@return (pos, result) The new position in <code>data</code>, and a table representing the datatype. May return
|
--@return (pos, result) The new position in <code>data</code>, and a table representing the datatype. May return
|
||||||
-- <code>nil</code> if there was an error.
|
-- <code>nil</code> if there was an error.
|
||||||
@@ -3915,7 +4008,7 @@ end
|
|||||||
---Unmarshall a pointer to a <code>samr_DomainInfo</code>. See <code>unmarshall_samr_DomainInfo</code> for
|
---Unmarshall a pointer to a <code>samr_DomainInfo</code>. See <code>unmarshall_samr_DomainInfo</code> for
|
||||||
-- more information.
|
-- more information.
|
||||||
--
|
--
|
||||||
--@param data The data packet being processed.
|
--@param data The data being processed.
|
||||||
--@param pos The position within <code>data</code>.
|
--@param pos The position within <code>data</code>.
|
||||||
--@return (pos, result) The new position in <code>data</code>, and a table representing the datatype. May return
|
--@return (pos, result) The new position in <code>data</code>, and a table representing the datatype. May return
|
||||||
-- <code>nil</code> if there was an error.
|
-- <code>nil</code> if there was an error.
|
||||||
@@ -3931,3 +4024,7 @@ end
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -353,6 +353,9 @@ function do_nbstat(host)
|
|||||||
rrlength = rrlength - 18
|
rrlength = rrlength - 18
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if(rrlength > 0) then
|
||||||
|
rrlength = rrlength - 1
|
||||||
|
end
|
||||||
pos, statistics = bin.unpack(string.format(">A%d", rrlength), result, pos)
|
pos, statistics = bin.unpack(string.format(">A%d", rrlength), result, pos)
|
||||||
|
|
||||||
-- Put it in the registry, in case anybody else needs it
|
-- Put it in the registry, in case anybody else needs it
|
||||||
|
|||||||
@@ -1,62 +0,0 @@
|
|||||||
--- 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
|
|
||||||
|
|
||||||
119
nselib/nsedebug.lua
Normal file
119
nselib/nsedebug.lua
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
-- 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
|
||||||
|
|
||||||
|
require "stdnse"
|
||||||
|
|
||||||
|
local EMPTY = {}; -- Empty constant table
|
||||||
|
|
||||||
|
module(... or "nsedebug", package.seeall);
|
||||||
|
|
||||||
|
---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
|
||||||
|
|
||||||
|
-- Print out a string in hex, for debugging.
|
||||||
|
function print_hex(str)
|
||||||
|
|
||||||
|
-- Prints out the full lines
|
||||||
|
for line=1, string.len(str)/16, 1 do
|
||||||
|
io.write(string.format("%08x ", (line - 1) * 16))
|
||||||
|
|
||||||
|
-- Loop through the string, printing the hex
|
||||||
|
for char=1, 16, 1 do
|
||||||
|
ch = string.byte(str, ((line - 1) * 16) + char)
|
||||||
|
io.write(string.format("%02x ", ch))
|
||||||
|
end
|
||||||
|
|
||||||
|
io.write(" ")
|
||||||
|
|
||||||
|
-- Loop through the string again, this time the ascii
|
||||||
|
for char=1, 16, 1 do
|
||||||
|
ch = string.byte(str, ((line - 1) * 16) + char)
|
||||||
|
if ch < 0x20 or ch > 0x7f then
|
||||||
|
ch = string.byte(".", 1)
|
||||||
|
end
|
||||||
|
io.write(string.format("%c", ch))
|
||||||
|
end
|
||||||
|
|
||||||
|
io.write("\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Prints out the final, partial line
|
||||||
|
line = math.floor((string.len(str)/16)) + 1
|
||||||
|
io.write(string.format("%08x ", (line - 1) * 16))
|
||||||
|
|
||||||
|
for char=1, string.len(str) % 16, 1 do
|
||||||
|
ch = string.byte(str, ((line - 1) * 16) + char)
|
||||||
|
io.write(string.format("%02x ", ch))
|
||||||
|
end
|
||||||
|
io.write(string.rep(" ", 16 - (string.len(str) % 16)));
|
||||||
|
io.write(" ")
|
||||||
|
|
||||||
|
for char=1, string.len(str) % 16, 1 do
|
||||||
|
ch = string.byte(str, ((line - 1) * 16) + char)
|
||||||
|
if ch < 0x20 or ch > 0x7f then
|
||||||
|
ch = string.byte(".", 1)
|
||||||
|
end
|
||||||
|
io.write(string.format("%c", ch))
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Print out the length
|
||||||
|
io.write(string.format("\n Length: %d [0x%x]\n", string.len(str), string.len(str)))
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
---Print out a stacktrace. The stacktrace will naturally include this function call.
|
||||||
|
function print_stack()
|
||||||
|
local thread = coroutine.running()
|
||||||
|
local trace = debug.traceback(thread);
|
||||||
|
if trace ~= "stack traceback:" then
|
||||||
|
print(thread, "\n", trace, "\n");
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
308
nselib/smb.lua
308
nselib/smb.lua
@@ -66,9 +66,9 @@
|
|||||||
-- If that's successful, <code>SMB_COM_SESSION_SETUP_ANDX</code> is sent. It is essentially the logon
|
-- If that's successful, <code>SMB_COM_SESSION_SETUP_ANDX</code> is sent. It is essentially the logon
|
||||||
-- packet, where the username, domain, and password are sent to the server for verification.
|
-- packet, where the username, domain, and password are sent to the server for verification.
|
||||||
-- The username and password are generally picked up from the program parameters, which are
|
-- The username and password are generally picked up from the program parameters, which are
|
||||||
-- set when running a script, or from the registry [TODO: Where?], which are set by other
|
-- set when running a script, or from the registry (<code>nmap.registry[<ip>]['smbaccounts'])
|
||||||
-- scripts. However, they can also be passed as parameters to the function, which will
|
-- where it can be set by other scripts (for example, smb-brute.nse). However, they can also
|
||||||
-- override any other username/password set.
|
-- be passed as parameters to the function, which will override any other username/password set.
|
||||||
--
|
--
|
||||||
-- If a username is set without a password, then a NULL session is started. If a login fails,
|
-- If a username is set without a password, then a NULL session is started. If a login fails,
|
||||||
-- we attempt to log in as the 'GUEST' account with a blank password. If that fails, we try
|
-- we attempt to log in as the 'GUEST' account with a blank password. If that fails, we try
|
||||||
@@ -151,6 +151,8 @@ status_names = {}
|
|||||||
local mutexes = setmetatable({}, {__mode = "k"});
|
local mutexes = setmetatable({}, {__mode = "k"});
|
||||||
--local debug_mutex = nmap.mutex("SMB-DEBUG")
|
--local debug_mutex = nmap.mutex("SMB-DEBUG")
|
||||||
|
|
||||||
|
local TIMEOUT = 5000
|
||||||
|
|
||||||
---Returns the mutex that should be used by the current connection. This mutex attempts
|
---Returns the mutex that should be used by the current connection. This mutex attempts
|
||||||
-- to use the name, first, then falls back to the IP if no name was returned.
|
-- to use the name, first, then falls back to the IP if no name was returned.
|
||||||
--
|
--
|
||||||
@@ -220,7 +222,7 @@ end
|
|||||||
function get_status_name(status)
|
function get_status_name(status)
|
||||||
|
|
||||||
if(status_names[status] == nil) then
|
if(status_names[status] == nil) then
|
||||||
-- If the name wasn't found in the array, do a linear search on it (TODO: Why is this happening??)
|
-- If the name wasn't found in the array, do a linear search on it (TODO: Why is this happening??) (XXX: I think I fixed this)
|
||||||
for i, v in pairs(status_names) do
|
for i, v in pairs(status_names) do
|
||||||
if(v == status) then
|
if(v == status) then
|
||||||
return i
|
return i
|
||||||
@@ -380,6 +382,7 @@ function start_raw(host, port)
|
|||||||
local status, err
|
local status, err
|
||||||
local socket = nmap.new_socket()
|
local socket = nmap.new_socket()
|
||||||
|
|
||||||
|
socket:set_timeout(TIMEOUT)
|
||||||
status, err = socket:connect(host.ip, port, "tcp")
|
status, err = socket:connect(host.ip, port, "tcp")
|
||||||
|
|
||||||
if(status == false) then
|
if(status == false) then
|
||||||
@@ -486,6 +489,7 @@ function start_netbios(host, port, name)
|
|||||||
);
|
);
|
||||||
|
|
||||||
stdnse.print_debug(3, "SMB: Connecting to %s", host.ip)
|
stdnse.print_debug(3, "SMB: Connecting to %s", host.ip)
|
||||||
|
socket:set_timeout(TIMEOUT)
|
||||||
status, err = socket:connect(host.ip, port, "tcp")
|
status, err = socket:connect(host.ip, port, "tcp")
|
||||||
if(status == false) then
|
if(status == false) then
|
||||||
socket:close()
|
socket:close()
|
||||||
@@ -499,7 +503,7 @@ function start_netbios(host, port, name)
|
|||||||
socket:close()
|
socket:close()
|
||||||
return false, "SMB: Failed to send: " .. err
|
return false, "SMB: Failed to send: " .. err
|
||||||
end
|
end
|
||||||
socket:set_timeout(5000)
|
socket:set_timeout(TIMEOUT)
|
||||||
|
|
||||||
-- Receive the session response
|
-- Receive the session response
|
||||||
stdnse.print_debug(3, "SMB: Receiving NetBIOS session response")
|
stdnse.print_debug(3, "SMB: Receiving NetBIOS session response")
|
||||||
@@ -509,6 +513,9 @@ function start_netbios(host, port, name)
|
|||||||
return false, "SMB: Failed to close socket: " .. result
|
return false, "SMB: Failed to close socket: " .. result
|
||||||
end
|
end
|
||||||
pos, result, flags, length = bin.unpack(">CCS", result)
|
pos, result, flags, length = bin.unpack(">CCS", result)
|
||||||
|
if(length == nil) then
|
||||||
|
return false, "SMB: ERROR: Ran off the end of SMB packet; likely due to server truncation [1]"
|
||||||
|
end
|
||||||
|
|
||||||
-- Check for a position session response (0x82)
|
-- Check for a position session response (0x82)
|
||||||
if result == 0x82 then
|
if result == 0x82 then
|
||||||
@@ -747,6 +754,11 @@ local function smb_encode_header(smb, command)
|
|||||||
-- the server that we deal in ASCII.
|
-- the server that we deal in ASCII.
|
||||||
local flags2 = bit.bor(0x4000, 0x0040, 0x0001) -- SMB_FLAGS2_32BIT_STATUS | SMB_FLAGS2_IS_LONG_NAME | SMB_FLAGS2_KNOWS_LONG_NAMES
|
local flags2 = bit.bor(0x4000, 0x0040, 0x0001) -- SMB_FLAGS2_32BIT_STATUS | SMB_FLAGS2_IS_LONG_NAME | SMB_FLAGS2_KNOWS_LONG_NAMES
|
||||||
|
|
||||||
|
-- TreeID should never ever be 'nil', but it seems to happen once in awhile so print an error
|
||||||
|
if(smb['tid'] == nil) then
|
||||||
|
return false, string.format("SMB: ERROR: TreeID value was set to nil on host %s", smb['ip'])
|
||||||
|
end
|
||||||
|
|
||||||
local header = bin.pack("<CCCCCICSSLSSSSS",
|
local header = bin.pack("<CCCCCICSSLSSSSS",
|
||||||
sig:byte(1), -- Header
|
sig:byte(1), -- Header
|
||||||
sig:byte(2), -- Header
|
sig:byte(2), -- Header
|
||||||
@@ -824,10 +836,10 @@ function smb_read(smb)
|
|||||||
local status, result
|
local status, result
|
||||||
local pos, netbios_length, length, header, parameter_length, parameters, data_length, data
|
local pos, netbios_length, length, header, parameter_length, parameters, data_length, data
|
||||||
|
|
||||||
|
stdnse.print_debug(3, "SMB: Receiving SMB packet")
|
||||||
|
|
||||||
-- Receive the response -- we make sure to receive at least 4 bytes, the length of the NetBIOS length
|
-- Receive the response -- we make sure to receive at least 4 bytes, the length of the NetBIOS length
|
||||||
-- [TODO] set the timeout length per jah's strategy:
|
smb['socket']:set_timeout(TIMEOUT)
|
||||||
-- http://seclists.org/nmap-dev/2008/q3/0702.html
|
|
||||||
smb['socket']:set_timeout(5000)
|
|
||||||
status, result = smb['socket']:receive_bytes(4);
|
status, result = smb['socket']:receive_bytes(4);
|
||||||
|
|
||||||
-- Make sure the connection is still alive
|
-- Make sure the connection is still alive
|
||||||
@@ -839,7 +851,7 @@ function smb_read(smb)
|
|||||||
-- The NetBIOS header is 24 bits, big endian
|
-- The NetBIOS header is 24 bits, big endian
|
||||||
pos, netbios_length = bin.unpack(">I", result)
|
pos, netbios_length = bin.unpack(">I", result)
|
||||||
if(netbios_length == nil) then
|
if(netbios_length == nil) then
|
||||||
return false, "SMB: SMB server didn't comply with standards (incorrect data was returned) [1]"
|
return false, "SMB: ERROR: Ran off the end of SMB packet; likely due to server truncation [2]"
|
||||||
end
|
end
|
||||||
-- Make the length 24 bits
|
-- Make the length 24 bits
|
||||||
netbios_length = bit.band(netbios_length, 0x00FFFFFF)
|
netbios_length = bit.band(netbios_length, 0x00FFFFFF)
|
||||||
@@ -850,10 +862,9 @@ function smb_read(smb)
|
|||||||
-- If we haven't received enough bytes, try and get the rest (fragmentation!)
|
-- If we haven't received enough bytes, try and get the rest (fragmentation!)
|
||||||
if(#result < length) then
|
if(#result < length) then
|
||||||
local new_result
|
local new_result
|
||||||
status, new_result = smb['socket']:receive_bytes(netbios_length)
|
|
||||||
|
|
||||||
stdnse.print_debug(1, "SMB: Received a fragmented packet, attempting to receive the rest of it (got %d bytes, need %d)", #result, length)
|
stdnse.print_debug(1, "SMB: Received a fragmented packet, attempting to receive the rest of it (got %d bytes, need %d)", #result, length)
|
||||||
|
|
||||||
|
status, new_result = smb['socket']:receive_bytes(netbios_length - #result)
|
||||||
-- Make sure the connection is still alive
|
-- Make sure the connection is still alive
|
||||||
if(status ~= true) then
|
if(status ~= true) then
|
||||||
return false, "SMB: Failed to receive bytes: " .. result
|
return false, "SMB: Failed to receive bytes: " .. result
|
||||||
@@ -872,31 +883,31 @@ function smb_read(smb)
|
|||||||
-- The header is 32 bytes.
|
-- The header is 32 bytes.
|
||||||
pos, header = bin.unpack("<A32", result, pos)
|
pos, header = bin.unpack("<A32", result, pos)
|
||||||
if(header == nil) then
|
if(header == nil) then
|
||||||
return false, "SMB: SMB server didn't comply with standards (incorrect data was returned) [2]"
|
return false, "SMB: ERROR: Ran off the end of SMB packet; likely due to server truncation [3]"
|
||||||
end
|
end
|
||||||
|
|
||||||
-- The parameters length is a 1-byte value.
|
-- The parameters length is a 1-byte value.
|
||||||
pos, parameter_length = bin.unpack("<C", result, pos)
|
pos, parameter_length = bin.unpack("<C", result, pos)
|
||||||
if(parameter_length == nil) then
|
if(parameter_length == nil) then
|
||||||
return false, "SMB: SMB server didn't comply with standards (incorrect data was returned) [3]"
|
return false, "SMB: ERROR: Ran off the end of SMB packet; likely due to server truncation [4]"
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Double the length parameter, since parameters are two-byte values.
|
-- Double the length parameter, since parameters are two-byte values.
|
||||||
pos, parameters = bin.unpack(string.format("<A%d", parameter_length*2), result, pos)
|
pos, parameters = bin.unpack(string.format("<A%d", parameter_length*2), result, pos)
|
||||||
if(parameters == nil) then
|
if(parameters == nil) then
|
||||||
return false, "SMB: SMB server didn't comply with standards (incorrect data was returned) [4]"
|
return false, "SMB: ERROR: Ran off the end of SMB packet; likely due to server truncation [5]"
|
||||||
end
|
end
|
||||||
|
|
||||||
-- The data length is a 2-byte value.
|
-- The data length is a 2-byte value.
|
||||||
pos, data_length = bin.unpack("<S", result, pos)
|
pos, data_length = bin.unpack("<S", result, pos)
|
||||||
if(data_length == nil) then
|
if(data_length == nil) then
|
||||||
return false, "SMB: SMB server didn't comply with standards (incorrect data was returned) [5]"
|
return false, "SMB: ERROR: Ran off the end of SMB packet; likely due to server truncation [6]"
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Read that many bytes of data.
|
-- Read that many bytes of data.
|
||||||
pos, data = bin.unpack(string.format("<A%d", data_length), result, pos)
|
pos, data = bin.unpack(string.format("<A%d", data_length), result, pos)
|
||||||
if(data == nil) then
|
if(data == nil) then
|
||||||
return false, "SMB: SMB server didn't comply with standards (incorrect data was returned) [6]"
|
return false, "SMB: ERROR: Ran off the end of SMB packet; likely due to server truncation [7]"
|
||||||
end
|
end
|
||||||
|
|
||||||
stdnse.print_debug(3, "SMB: Received %d bytes", string.len(result))
|
stdnse.print_debug(3, "SMB: Received %d bytes", string.len(result))
|
||||||
@@ -967,15 +978,18 @@ function negotiate_protocol(smb)
|
|||||||
|
|
||||||
-- Check if we fell off the packet (if that happened, the last parameter will be nil)
|
-- Check if we fell off the packet (if that happened, the last parameter will be nil)
|
||||||
if(mid == nil) then
|
if(mid == nil) then
|
||||||
return false, "SMB: SMB server didn't comply with standards (incorrect data was returned) [7]"
|
return false, "SMB: ERROR: Ran off the end of SMB packet; likely due to server truncation [8]"
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Parse the parameter section
|
-- Parse the parameter section
|
||||||
pos, dialect = bin.unpack("<S", parameters)
|
pos, dialect = bin.unpack("<S", parameters)
|
||||||
|
if(dialect == nil) then
|
||||||
|
return false, "SMB: ERROR: Ran off the end of SMB packet; likely due to server truncation [9]"
|
||||||
|
end
|
||||||
|
|
||||||
-- Check if we ran off the packet
|
-- Check if we ran off the packet
|
||||||
if(dialect == nil) then
|
if(dialect == nil) then
|
||||||
return false, "SMB: SMB server didn't comply with standards (incorrect data was returned) [8]"
|
return false, "SMB: ERROR: Ran off the end of SMB packet; likely due to server truncation [10]"
|
||||||
end
|
end
|
||||||
-- Check if the server didn't like our requested protocol
|
-- Check if the server didn't like our requested protocol
|
||||||
if(dialect ~= 0) then
|
if(dialect ~= 0) then
|
||||||
@@ -983,7 +997,9 @@ function negotiate_protocol(smb)
|
|||||||
end
|
end
|
||||||
|
|
||||||
pos, security_mode, max_mpx, max_vc, max_buffer, max_raw_buffer, session_key, capabilities, time, timezone, key_length = bin.unpack("<CSSIIIILsC", parameters, pos)
|
pos, security_mode, max_mpx, max_vc, max_buffer, max_raw_buffer, session_key, capabilities, time, timezone, key_length = bin.unpack("<CSSIIIILsC", parameters, pos)
|
||||||
|
if(capabilities == nil) then
|
||||||
|
return false, "SMB: ERROR: Ran off the end of SMB packet; likely due to server truncation [11]"
|
||||||
|
end
|
||||||
-- Some broken implementations of SMB don't send these variables
|
-- Some broken implementations of SMB don't send these variables
|
||||||
if(time == nil) then
|
if(time == nil) then
|
||||||
time = 0
|
time = 0
|
||||||
@@ -1010,6 +1026,9 @@ function negotiate_protocol(smb)
|
|||||||
-- Data section
|
-- Data section
|
||||||
-- This one's a little messier, because I don't appear to have unicode support
|
-- This one's a little messier, because I don't appear to have unicode support
|
||||||
pos, server_challenge = bin.unpack(string.format("<A%d", key_length), data)
|
pos, server_challenge = bin.unpack(string.format("<A%d", key_length), data)
|
||||||
|
if(server_challenge == nil) then
|
||||||
|
return false, "SMB: ERROR: Ran off the end of SMB packet; likely due to server truncation [12]"
|
||||||
|
end
|
||||||
|
|
||||||
-- Get the domain as a Unicode string
|
-- Get the domain as a Unicode string
|
||||||
local ch, dummy
|
local ch, dummy
|
||||||
@@ -1017,18 +1036,26 @@ function negotiate_protocol(smb)
|
|||||||
server = ""
|
server = ""
|
||||||
|
|
||||||
pos, ch, dummy = bin.unpack("<CC", data, pos)
|
pos, ch, dummy = bin.unpack("<CC", data, pos)
|
||||||
|
if(dummy == nil) then
|
||||||
|
return false, "SMB: ERROR: Ran off the end of SMB packet; likely due to server truncation [13]"
|
||||||
|
end
|
||||||
while ch ~= nil and ch ~= 0 do
|
while ch ~= nil and ch ~= 0 do
|
||||||
domain = domain .. string.char(ch)
|
domain = domain .. string.char(ch)
|
||||||
pos, ch, dummy = bin.unpack("<CC", data, pos)
|
pos, ch, dummy = bin.unpack("<CC", data, pos)
|
||||||
|
if(dummy == nil) then
|
||||||
|
return false, "SMB: ERROR: Ran off the end of SMB packet; likely due to server truncation [14]"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Get the server name as a Unicode string
|
-- Get the server name as a Unicode string
|
||||||
|
-- Note: This can be nil, Samba leaves this off
|
||||||
pos, ch, dummy = bin.unpack("<CC", data, pos)
|
pos, ch, dummy = bin.unpack("<CC", data, pos)
|
||||||
while ch ~= nil and ch ~= 0 do
|
while ch ~= nil and ch ~= 0 do
|
||||||
server = server .. string.char(ch)
|
server = server .. string.char(ch)
|
||||||
pos, ch, dummy = bin.unpack("<CC", data, pos)
|
pos, ch, dummy = bin.unpack("<CC", data, pos)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
-- Fill out smb variables
|
-- Fill out smb variables
|
||||||
smb['security_mode'] = security_mode
|
smb['security_mode'] = security_mode
|
||||||
smb['max_mpx'] = max_mpx
|
smb['max_mpx'] = max_mpx
|
||||||
@@ -1074,15 +1101,24 @@ end
|
|||||||
---Determines which username is going to be used, based on the function parameters, the registry, and
|
---Determines which username is going to be used, based on the function parameters, the registry, and
|
||||||
-- the nmap arguments (in that order).
|
-- the nmap arguments (in that order).
|
||||||
--
|
--
|
||||||
|
--@param ip The ip address, used when reading from the registry
|
||||||
--@param username [optional] The function parameter version, which will override all others if set.
|
--@param username [optional] The function parameter version, which will override all others if set.
|
||||||
--@return The highest priority username that's set.
|
--@return The highest priority username that's set.
|
||||||
-- TODO: Get username from the registry
|
local function get_username(ip, username)
|
||||||
local function get_username(username)
|
|
||||||
|
|
||||||
if(username ~= nil) then
|
if(username ~= nil) then
|
||||||
stdnse.print_debug(2, "SMB: Using username passed as a parameter: %s", username)
|
stdnse.print_debug(2, "SMB: Using username passed as a parameter: %s", username)
|
||||||
else
|
else
|
||||||
if(nmap.registry.args.smbusername ~= nil) then
|
if(nmap.registry[ip] ~= nil and nmap.registry[ip]['smbaccounts'] ~= nil and next(nmap.registry[ip]['smbaccounts']) ~= nil) then
|
||||||
|
if(nmap.registry[ip]['smbaccounts']['administrator'] ~= nil) then
|
||||||
|
-- If we found an administrator account, use it first
|
||||||
|
username = "administrator"
|
||||||
|
else
|
||||||
|
-- Otherwise, use whichever is first
|
||||||
|
username, _ = next(nmap.registry[ip]['smbaccounts'])
|
||||||
|
end
|
||||||
|
stdnse.print_debug(2, "SMB: Using username found in the registry: %s", username)
|
||||||
|
elseif(nmap.registry.args.smbusername ~= nil) then
|
||||||
username = nmap.registry.args.smbusername
|
username = nmap.registry.args.smbusername
|
||||||
stdnse.print_debug(2, "SMB: Using username passed as an nmap parameter (smbusername): %s", username)
|
stdnse.print_debug(2, "SMB: Using username passed as an nmap parameter (smbusername): %s", username)
|
||||||
elseif(nmap.registry.args.smbuser ~= nil) then
|
elseif(nmap.registry.args.smbuser ~= nil) then
|
||||||
@@ -1125,6 +1161,7 @@ end
|
|||||||
--
|
--
|
||||||
-- The output passwords are hashed based on the hash type.
|
-- The output passwords are hashed based on the hash type.
|
||||||
--
|
--
|
||||||
|
--@param ip The ip address of the host, used for registry lookups.
|
||||||
--@param username The username, which is used for v2 passwords.
|
--@param username The username, which is used for v2 passwords.
|
||||||
--@param domain The username, which is used for v2 passwords.
|
--@param domain The username, which is used for v2 passwords.
|
||||||
--@param password [optional] The overriding password.
|
--@param password [optional] The overriding password.
|
||||||
@@ -1132,7 +1169,7 @@ end
|
|||||||
--@param challenge The server challenge.
|
--@param challenge The server challenge.
|
||||||
--@param hash_type The way in which to hash the password.
|
--@param hash_type The way in which to hash the password.
|
||||||
--@return (lm_response, ntlm_response) The two strings that can be sent directly back to the server.
|
--@return (lm_response, ntlm_response) The two strings that can be sent directly back to the server.
|
||||||
local function get_password_response(username, domain, password, password_hash, challenge, hash_type)
|
local function get_password_response(ip, username, domain, password, password_hash, challenge, hash_type)
|
||||||
|
|
||||||
local lm_hash = nil
|
local lm_hash = nil
|
||||||
local ntlm_hash = nil
|
local ntlm_hash = nil
|
||||||
@@ -1141,7 +1178,10 @@ local function get_password_response(username, domain, password, password_hash,
|
|||||||
if(password ~= nil) then
|
if(password ~= nil) then
|
||||||
stdnse.print_debug(2, "SMB: Using password passed as a parameter")
|
stdnse.print_debug(2, "SMB: Using password passed as a parameter")
|
||||||
else
|
else
|
||||||
if(nmap.registry.args.smbpassword ~= nil) then
|
if(nmap.registry[ip] ~= nil and nmap.registry[ip]['smbaccounts'] ~= nil and nmap.registry[ip]['smbaccounts'][username] ~= nil) then
|
||||||
|
password = nmap.registry[ip]['smbaccounts'][username]
|
||||||
|
stdnse.print_debug(2, "SMB: Using password found in the registry")
|
||||||
|
elseif(nmap.registry.args.smbpassword ~= nil) then
|
||||||
password = nmap.registry.args.smbpassword
|
password = nmap.registry.args.smbpassword
|
||||||
stdnse.print_debug(2, "SMB: Using password passed as an nmap parameter (smbpassword)")
|
stdnse.print_debug(2, "SMB: Using password passed as an nmap parameter (smbpassword)")
|
||||||
elseif(nmap.registry.args.smbpass ~= nil) then
|
elseif(nmap.registry.args.smbpass ~= nil) then
|
||||||
@@ -1253,24 +1293,25 @@ end
|
|||||||
--@param password [optional] The password to override with.
|
--@param password [optional] The password to override with.
|
||||||
--@param password_hash [optional] The password hash to override this (shouldn't be used along with password).
|
--@param password_hash [optional] The password hash to override this (shouldn't be used along with password).
|
||||||
--@param hash_type [optional] The type of hash to override with.
|
--@param hash_type [optional] The type of hash to override with.
|
||||||
|
--@param use_default [optional] If set, will attempt anonymous/guest. Default: true.
|
||||||
--@return An array of tables, each of which contain a 'username', 'domain', 'lanman', 'ntlm'.
|
--@return An array of tables, each of which contain a 'username', 'domain', 'lanman', 'ntlm'.
|
||||||
local function get_logins(ip, challenge, username, domain, password, password_hash, hash_type)
|
local function get_logins(ip, challenge, username, domain, password, password_hash, hash_type, use_default)
|
||||||
|
|
||||||
local response = {}
|
local response = {}
|
||||||
|
|
||||||
-- If we don't have OpenSSL, don't bother with any of this
|
-- If we don't have OpenSSL, don't bother with any of this
|
||||||
if(have_ssl == true) then
|
if(have_ssl == true) then
|
||||||
-- We choose *one* username to try, here. First, see if the user set a username
|
-- We choose *one* username to try, here. First, see if the user set a username
|
||||||
-- in the function parameters, then in the registry [TODO], then as an nmap
|
-- in the function parameters, then in the registry, then as an nmap
|
||||||
-- parameter, then disable it.
|
-- parameter, then disable it.
|
||||||
|
|
||||||
-- If a username was found, look for a domain and password
|
-- If a username was found, look for a domain and password
|
||||||
username = get_username(username)
|
username = get_username(ip, username)
|
||||||
domain = get_domain(domain)
|
domain = get_domain(domain)
|
||||||
hash_type = get_hash_type(hash_type)
|
hash_type = get_hash_type(hash_type)
|
||||||
|
|
||||||
if(username ~= nil) then
|
if(username ~= nil) then
|
||||||
lm_response, ntlm_response = get_password_response(username, domain, password, password_hash, challenge, hash_type)
|
lm_response, ntlm_response = get_password_response(ip, username, domain, password, password_hash, challenge, hash_type)
|
||||||
|
|
||||||
if(lm_response ~= nil and ntlm_response ~= nil) then
|
if(lm_response ~= nil and ntlm_response ~= nil) then
|
||||||
local data = {}
|
local data = {}
|
||||||
@@ -1293,10 +1334,10 @@ local function get_logins(ip, challenge, username, domain, password, password_ha
|
|||||||
stdnse.print_debug(1, "SMB: ERROR: Couldn't find OpenSSL library, only checking Guest and/or Anonymous accounts")
|
stdnse.print_debug(1, "SMB: ERROR: Couldn't find OpenSSL library, only checking Guest and/or Anonymous accounts")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Check if we're using default accounts
|
||||||
|
if(use_default == nil or use_default == true) then
|
||||||
local data
|
local data
|
||||||
-- Add guest account
|
-- Add guest account
|
||||||
stdnse.print_debug(2, "SMB: Going to try guest account before attempting anonymous")
|
|
||||||
|
|
||||||
data = {}
|
data = {}
|
||||||
data['username'] = 'guest'
|
data['username'] = 'guest'
|
||||||
data['domain'] = ''
|
data['domain'] = ''
|
||||||
@@ -1311,6 +1352,7 @@ local function get_logins(ip, challenge, username, domain, password, password_ha
|
|||||||
data['lanman'] = ''
|
data['lanman'] = ''
|
||||||
data['ntlm'] = ''
|
data['ntlm'] = ''
|
||||||
response[#response + 1] = data
|
response[#response + 1] = data
|
||||||
|
end
|
||||||
|
|
||||||
return response
|
return response
|
||||||
end
|
end
|
||||||
@@ -1333,21 +1375,23 @@ end
|
|||||||
--@param domain [optional] Overrides the domain to use.
|
--@param domain [optional] Overrides the domain to use.
|
||||||
--@param password [optional] Overrides the password to use. Will use Nmap parameters or registry by default.
|
--@param password [optional] Overrides the password to use. Will use Nmap parameters or registry by default.
|
||||||
--@param hash_type [optional] Overrides the hash type to use (can be v1, LM, NTLM, LMv2, v2). Default is 'NTLM'.
|
--@param hash_type [optional] Overrides the hash type to use (can be v1, LM, NTLM, LMv2, v2). Default is 'NTLM'.
|
||||||
|
--@param use_default [optional] If set, will attempt anonymous/guest. Default: true.
|
||||||
|
--@param log_errors [optional] If set, will display login. Default: true.
|
||||||
--@return (status, result) If status is false, result is an error message. Otherwise, result is nil and the following
|
--@return (status, result) If status is false, result is an error message. Otherwise, result is nil and the following
|
||||||
-- elements are added to the smb table:
|
-- elements are added to the smb table:
|
||||||
-- * 'uid' The UserID for the session
|
-- * 'uid' The UserID for the session
|
||||||
-- * 'is_guest' If set, the username wasn't found so the user was automatically logged in as the guest account
|
-- * 'is_guest' If set, the username wasn't found so the user was automatically logged in as the guest account
|
||||||
-- * 'os' The operating system
|
-- * 'os' The operating system
|
||||||
-- * 'lanmanager' The servers's LAN Manager
|
-- * 'lanmanager' The servers's LAN Manager
|
||||||
function start_session(smb, username, domain, password, password_hash, hash_type)
|
function start_session(smb, username, domain, password, password_hash, hash_type, use_default, log_errors)
|
||||||
local i
|
local i
|
||||||
local status, result
|
local status, result
|
||||||
local header, parameters, data
|
local header, parameters, data
|
||||||
local pos
|
local pos
|
||||||
local header1, header2, header3, header4, command, status, flags, flags2, pid_high, signature, unused, tid, pid, uid, mid
|
local header1, header2, header3, header4, command, status, flags, flags2, pid_high, signature, unused, tid, pid, uid, mid
|
||||||
local andx_command, andx_reserved, andx_offset, action
|
local andx_command, andx_reserved, andx_offset, action
|
||||||
local os, lanmanager, domain
|
local os, lanmanager
|
||||||
local logins = get_logins(smb['ip'], smb['server_challenge'], username, domain, password, password_hash, hash_type)
|
local logins = get_logins(smb['ip'], smb['server_challenge'], username, domain, password, password_hash, hash_type, use_default)
|
||||||
|
|
||||||
header = smb_encode_header(smb, command_codes['SMB_COM_SESSION_SETUP_ANDX'])
|
header = smb_encode_header(smb, command_codes['SMB_COM_SESSION_SETUP_ANDX'])
|
||||||
|
|
||||||
@@ -1358,7 +1402,7 @@ function start_session(smb, username, domain, password, password_hash, hash_type
|
|||||||
0xFF, -- ANDX -- no further commands
|
0xFF, -- ANDX -- no further commands
|
||||||
0x00, -- ANDX -- Reserved (0)
|
0x00, -- ANDX -- Reserved (0)
|
||||||
0x0000, -- ANDX -- next offset
|
0x0000, -- ANDX -- next offset
|
||||||
0x1000, -- Max buffer size
|
0xFFFF, -- Max buffer size
|
||||||
0x0001, -- Max multiplexes
|
0x0001, -- Max multiplexes
|
||||||
0x0000, -- Virtual circuit num
|
0x0000, -- Virtual circuit num
|
||||||
smb['session_key'], -- The session key
|
smb['session_key'], -- The session key
|
||||||
@@ -1394,7 +1438,7 @@ function start_session(smb, username, domain, password, password_hash, hash_type
|
|||||||
pos, header1, header2, header3, header4, command, status, flags, flags2, pid_high, signature, unused, tid, pid, uid, mid = bin.unpack("<CCCCCICSSlSSSSS", header)
|
pos, header1, header2, header3, header4, command, status, flags, flags2, pid_high, signature, unused, tid, pid, uid, mid = bin.unpack("<CCCCCICSSlSSSSS", header)
|
||||||
|
|
||||||
if(mid == nil) then
|
if(mid == nil) then
|
||||||
return false, "SMB: SMB server didn't comply with standards (incorrect data was returned) [9]"
|
return false, "SMB: ERROR: Ran off the end of SMB packet; likely due to server truncation [17]"
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Check if we're successful
|
-- Check if we're successful
|
||||||
@@ -1403,11 +1447,14 @@ function start_session(smb, username, domain, password, password_hash, hash_type
|
|||||||
-- Parse the parameters
|
-- Parse the parameters
|
||||||
pos, andx_command, andx_reserved, andx_offset, action = bin.unpack("<CCSS", parameters)
|
pos, andx_command, andx_reserved, andx_offset, action = bin.unpack("<CCSS", parameters)
|
||||||
if(action == nil) then
|
if(action == nil) then
|
||||||
return false, "SMB: SMB server didn't comply with standards (incorrect data was returned) [10]"
|
return false, "SMB: ERROR: Ran off the end of SMB packet; likely due to server truncation [18]"
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Parse the data
|
-- Parse the data
|
||||||
pos, os, lanmanager, domain = bin.unpack("<zzz", data)
|
pos, os, lanmanager, domain = bin.unpack("<zzz", data)
|
||||||
|
if(domain == nil) then
|
||||||
|
return false, "SMB: ERROR: Ran off the end of SMB packet; likely due to server truncation [19]"
|
||||||
|
end
|
||||||
|
|
||||||
-- Fill in the smb object and smb string
|
-- Fill in the smb object and smb string
|
||||||
smb['uid'] = uid
|
smb['uid'] = uid
|
||||||
@@ -1423,21 +1470,27 @@ function start_session(smb, username, domain, password, password_hash, hash_type
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- Check if they were logged in as a guest
|
-- Check if they were logged in as a guest
|
||||||
|
if(log_errors == nil or log_errors == true) then
|
||||||
if(smb['is_guest'] == 1) then
|
if(smb['is_guest'] == 1) then
|
||||||
stdnse.print_debug(1, "SMB: Login as %s\\%s failed, but was given guest access (username may be wrong, or system may only allow guest)", logins[i]['domain'], string_or_blank(logins[i]['username']))
|
stdnse.print_debug(1, "SMB: Login as %s\\%s failed, but was given guest access (username may be wrong, or system may only allow guest)", logins[i]['domain'], string_or_blank(logins[i]['username']))
|
||||||
else
|
else
|
||||||
stdnse.print_debug(1, "SMB: Login as %s\\%s succeeded", logins[i]['domain'], string_or_blank(logins[i]['username']))
|
stdnse.print_debug(1, "SMB: Login as %s\\%s succeeded", logins[i]['domain'], string_or_blank(logins[i]['username']))
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
return true
|
return true
|
||||||
|
|
||||||
else
|
else
|
||||||
-- This username failed, print a warning and keep going
|
-- This username failed, print a warning and keep going
|
||||||
stdnse.print_debug(1, "SMB: Login as %s\\%s failed (%s), trying next login", logins[i]['domain'], string_or_blank(logins[i]['username']), get_status_name(status))
|
if(log_errors == nil or log_errors == true) then
|
||||||
|
stdnse.print_debug(1, "SMB: Login as %s\\%s failed (%s)", logins[i]['domain'], string_or_blank(logins[i]['username']), get_status_name(status))
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if(log_errors == nil or log_errors == true) then
|
||||||
stdnse.print_debug(1, "SMB: ERROR: All logins failed, sorry it didn't work out!")
|
stdnse.print_debug(1, "SMB: ERROR: All logins failed, sorry it didn't work out!")
|
||||||
|
end
|
||||||
return false, get_status_name(status)
|
return false, get_status_name(status)
|
||||||
|
|
||||||
end
|
end
|
||||||
@@ -1492,7 +1545,7 @@ function tree_connect(smb, path)
|
|||||||
-- Check if we were allowed in
|
-- Check if we were allowed in
|
||||||
pos, header1, header2, header3, header4, command, status, flags, flags2, pid_high, signature, unused, tid, pid, uid, mid = bin.unpack("<CCCCCICSSlSSSSS", header)
|
pos, header1, header2, header3, header4, command, status, flags, flags2, pid_high, signature, unused, tid, pid, uid, mid = bin.unpack("<CCCCCICSSlSSSSS", header)
|
||||||
if(mid == nil) then
|
if(mid == nil) then
|
||||||
return false, "SMB: SMB server didn't comply with standards (incorrect data was returned) [11]"
|
return false, "SMB: ERROR: Ran off the end of SMB packet; likely due to server truncation [20]"
|
||||||
end
|
end
|
||||||
|
|
||||||
if(status ~= 0) then
|
if(status ~= 0) then
|
||||||
@@ -1531,7 +1584,7 @@ function tree_disconnect(smb)
|
|||||||
-- Check if there was an error
|
-- Check if there was an error
|
||||||
pos, header1, header2, header3, header4, command, status, flags, flags2, pid_high, signature, unused, tid, pid, uid, mid = bin.unpack("<CCCCCICSSlSSSSS", header)
|
pos, header1, header2, header3, header4, command, status, flags, flags2, pid_high, signature, unused, tid, pid, uid, mid = bin.unpack("<CCCCCICSSlSSSSS", header)
|
||||||
if(mid == nil) then
|
if(mid == nil) then
|
||||||
return false, "SMB: SMB server didn't comply with standards (incorrect data was returned) [12]"
|
return false, "SMB: ERROR: Ran off the end of SMB packet; likely due to server truncation [21]"
|
||||||
end
|
end
|
||||||
if(status ~= 0) then
|
if(status ~= 0) then
|
||||||
return false, get_status_name(status)
|
return false, get_status_name(status)
|
||||||
@@ -1577,7 +1630,7 @@ function logoff(smb)
|
|||||||
-- Check if there was an error
|
-- Check if there was an error
|
||||||
pos, header1, header2, header3, header4, command, status, flags, flags2, pid_high, signature, unused, tid, pid, uid, mid = bin.unpack("<CCCCCICSSlSSSSS", header)
|
pos, header1, header2, header3, header4, command, status, flags, flags2, pid_high, signature, unused, tid, pid, uid, mid = bin.unpack("<CCCCCICSSlSSSSS", header)
|
||||||
if(mid == nil) then
|
if(mid == nil) then
|
||||||
return false, "SMB: SMB server didn't comply with standards (incorrect data was returned) [13]"
|
return false, "SMB: ERROR: Ran off the end of SMB packet; likely due to server truncation [22]"
|
||||||
end
|
end
|
||||||
if(status ~= 0) then
|
if(status ~= 0) then
|
||||||
return false, get_status_name(status)
|
return false, get_status_name(status)
|
||||||
@@ -1613,14 +1666,14 @@ function create_file(smb, path)
|
|||||||
string.len(path), -- Path length
|
string.len(path), -- Path length
|
||||||
0x00000016, -- Create flags
|
0x00000016, -- Create flags
|
||||||
0x00000000, -- Root FID
|
0x00000000, -- Root FID
|
||||||
0x0002019F, -- Access mask
|
0x02000000, -- Access mask
|
||||||
0x0000000000000000, -- Allocation size
|
0x0000000000000000, -- Allocation size
|
||||||
0x00000000, -- File attributes
|
0x00000000, -- File attributes
|
||||||
0x00000003, -- Share attributes
|
0x00000003, -- Share attributes
|
||||||
0x00000001, -- Disposition
|
0x00000001, -- Disposition
|
||||||
0x00400040, -- Create options
|
0x00000000, -- Create options
|
||||||
0x00000002, -- Impersonation
|
0x00000002, -- Impersonation
|
||||||
0x01 -- Security flags
|
0x00 -- Security flags
|
||||||
)
|
)
|
||||||
|
|
||||||
data = bin.pack("z", path)
|
data = bin.pack("z", path)
|
||||||
@@ -1641,7 +1694,7 @@ function create_file(smb, path)
|
|||||||
-- Check if we were allowed in
|
-- Check if we were allowed in
|
||||||
pos, header1, header2, header3, header4, command, status, flags, flags2, pid_high, signature, unused, tid, pid, uid, mid = bin.unpack("<CCCCCICSSlSSSSS", header)
|
pos, header1, header2, header3, header4, command, status, flags, flags2, pid_high, signature, unused, tid, pid, uid, mid = bin.unpack("<CCCCCICSSlSSSSS", header)
|
||||||
if(mid == nil) then
|
if(mid == nil) then
|
||||||
return false, "SMB: SMB server didn't comply with standards (incorrect data was returned) [14]"
|
return false, "SMB: ERROR: Ran off the end of SMB packet; likely due to server truncation [23]"
|
||||||
end
|
end
|
||||||
if(status ~= 0) then
|
if(status ~= 0) then
|
||||||
return false, get_status_name(status)
|
return false, get_status_name(status)
|
||||||
@@ -1650,7 +1703,7 @@ function create_file(smb, path)
|
|||||||
-- Parse the parameters
|
-- Parse the parameters
|
||||||
pos, andx_command, andx_reserved, andx_offset, oplock_level, fid, create_action, created, last_access, last_write, last_change, attributes, allocation_size, end_of_file, filetype, ipc_state, is_directory = bin.unpack("<CCSCSILLLLILLSSC", parameters)
|
pos, andx_command, andx_reserved, andx_offset, oplock_level, fid, create_action, created, last_access, last_write, last_change, attributes, allocation_size, end_of_file, filetype, ipc_state, is_directory = bin.unpack("<CCSCSILLLLILLSSC", parameters)
|
||||||
if(is_directory == nil) then
|
if(is_directory == nil) then
|
||||||
return false, "SMB: SMB server didn't comply with standards (incorrect data was returned) [15]"
|
return false, "SMB: ERROR: Ran off the end of SMB packet; likely due to server truncation [24]"
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Fill in the smb string
|
-- Fill in the smb string
|
||||||
@@ -1669,9 +1722,165 @@ function create_file(smb, path)
|
|||||||
smb['is_directory'] = is_directory
|
smb['is_directory'] = is_directory
|
||||||
|
|
||||||
return true
|
return true
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- This sends a SMB request to read from a file (or a pipe).
|
||||||
|
--
|
||||||
|
--@param smb The SMB object associated with the connection
|
||||||
|
--@param offset The offset to read from (ignored if it's a pipe)
|
||||||
|
--@param count The maximum number of bytes to read
|
||||||
|
--@return (status, result) If status is false, result is an error message. Otherwise, result is a table
|
||||||
|
-- containing a lot of different elements, the most important one being 'fid', the handle to the opened file.
|
||||||
|
function read_file(smb, offset, count)
|
||||||
|
local header, parameters, data
|
||||||
|
local pos
|
||||||
|
local header1, header2, header3, header4, command, status, flags, flags2, pid_high, signature, unused, pid, mid
|
||||||
|
local andx_command, andx_reserved, andx_offset
|
||||||
|
local remaining, data_compaction_mode, reserved_1, data_length_low, data_offset, data_length_high, reserved_2, reserved_3
|
||||||
|
local response = {}
|
||||||
|
|
||||||
|
header = smb_encode_header(smb, command_codes['SMB_COM_READ_ANDX'])
|
||||||
|
parameters = bin.pack("<CCSSISSISI",
|
||||||
|
0xFF, -- ANDX no further commands
|
||||||
|
0x00, -- ANDX reserved
|
||||||
|
0x0000, -- ANDX offset
|
||||||
|
smb['fid'], -- FID
|
||||||
|
offset, -- Offset
|
||||||
|
count, -- Max count low
|
||||||
|
count, -- Min count
|
||||||
|
0xFFFFFFFF, -- Reserved
|
||||||
|
0, -- Remaining
|
||||||
|
0x00000000 -- High offset
|
||||||
|
)
|
||||||
|
|
||||||
|
data = ""
|
||||||
|
|
||||||
|
-- Send the create file
|
||||||
|
stdnse.print_debug(2, "SMB: Sending SMB_COM_READ_ANDX")
|
||||||
|
result, err = smb_send(smb, header, parameters, data)
|
||||||
|
if(result == false) then
|
||||||
|
return false, err
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Read the result
|
||||||
|
status, header, parameters, data = smb_read(smb)
|
||||||
|
if(status ~= true) then
|
||||||
|
return false, header
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Check if we were allowed in
|
||||||
|
pos, header1, header2, header3, header4, command, status, flags, flags2, pid_high, signature, unused, tid, pid, uid, mid = bin.unpack("<CCCCCICSSlSSSSS", header)
|
||||||
|
|
||||||
|
if(mid == nil) then
|
||||||
|
return false, "SMB: ERROR: Ran off the end of SMB packet; likely due to server truncation [25]"
|
||||||
|
end
|
||||||
|
if(status ~= 0) then
|
||||||
|
return false, get_status_name(status)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Parse the parameters
|
||||||
|
pos, andx_command, andx_reserved, andx_offset, remaining, data_compaction_mode, reserved_1, data_length_low, data_offset, data_length_high, reserved_2, reserved_3 = bin.unpack("<CCSSSSSSISI", parameters)
|
||||||
|
if(reserved_3 == nil) then
|
||||||
|
return false, "SMB: ERROR: Ran off the end of SMB packet; likely due to server truncation [26]"
|
||||||
|
end
|
||||||
|
|
||||||
|
response['remaining'] = remaining
|
||||||
|
response['data_length'] = bit.bor(data_length_low, bit.lshift(data_length_high, 16))
|
||||||
|
|
||||||
|
|
||||||
|
-- data_start is the offset of the beginning of the data section -- we use this to calculate where the read data lives
|
||||||
|
local data_start = #header + 1 + #parameters + 2
|
||||||
|
if(data_offset < data_start) then
|
||||||
|
return false, "SMB: Start of data isn't in data section"
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Figure out the offset into the data section
|
||||||
|
data_offset = data_offset - data_start
|
||||||
|
|
||||||
|
-- Make sure we don't run off the edge of the packet
|
||||||
|
if(data_offset + response['data_length'] > #data) then
|
||||||
|
return false, "SMB: Data returned runs off the end of the packet"
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Pull the data string out of the data
|
||||||
|
response['data'] = string.sub(data, data_offset + 1, data_offset + response['data_length'])
|
||||||
|
|
||||||
|
return true, response
|
||||||
|
end
|
||||||
|
|
||||||
|
--- This sends a SMB request to write to a file (or a pipe).
|
||||||
|
--
|
||||||
|
--@param smb The SMB object associated with the connection
|
||||||
|
--@param write_data The data to write
|
||||||
|
--@param offset The offset to write it to (ignored for pipes)
|
||||||
|
--@param path The path of the file or pipe to open
|
||||||
|
--@return (status, result) If status is false, result is an error message. Otherwise, result is a table
|
||||||
|
-- containing a lot of different elements, the most important one being 'fid', the handle to the opened file.
|
||||||
|
function write_file(smb, write_data, offset)
|
||||||
|
local header, parameters, data
|
||||||
|
local pos
|
||||||
|
local header1, header2, header3, header4, command, status, flags, flags2, pid_high, signature, unused, pid, mid
|
||||||
|
local andx_command, andx_reserved, andx_offset
|
||||||
|
local response = {}
|
||||||
|
|
||||||
|
header = smb_encode_header(smb, command_codes['SMB_COM_WRITE_ANDX'])
|
||||||
|
parameters = bin.pack("<CCSSIISSSSSI",
|
||||||
|
0xFF, -- ANDX no further commands
|
||||||
|
0x00, -- ANDX reserved
|
||||||
|
0x0000, -- ANDX offset
|
||||||
|
smb['fid'], -- FID
|
||||||
|
offset, -- Offset
|
||||||
|
0xFFFFFFFF, -- Reserved
|
||||||
|
0x0008, -- Write mode (Message start, don't write raw, don't return remaining, don't write through
|
||||||
|
#write_data,-- Remaining
|
||||||
|
0x0000, -- Data length high
|
||||||
|
#write_data,-- Data length low -- TODO: set this properly (to the 2-byte value)
|
||||||
|
0x003F, -- Data offset
|
||||||
|
0x00000000 -- Data offset high
|
||||||
|
)
|
||||||
|
|
||||||
|
data = write_data
|
||||||
|
|
||||||
|
-- Send the create file
|
||||||
|
stdnse.print_debug(2, "SMB: Sending SMB_COM_WRITE_ANDX")
|
||||||
|
result, err = smb_send(smb, header, parameters, data)
|
||||||
|
if(result == false) then
|
||||||
|
return false, err
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- Read the result
|
||||||
|
status, header, parameters, data = smb_read(smb)
|
||||||
|
if(status ~= true) then
|
||||||
|
return false, header
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Check if we were allowed in
|
||||||
|
pos, header1, header2, header3, header4, command, status, flags, flags2, pid_high, signature, unused, tid, pid, uid, mid = bin.unpack("<CCCCCICSSlSSSSS", header)
|
||||||
|
if(mid == nil) then
|
||||||
|
return false, "SMB: ERROR: Ran off the end of SMB packet; likely due to server truncation [27]"
|
||||||
|
end
|
||||||
|
if(status ~= 0) then
|
||||||
|
return false, get_status_name(status)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Parse the parameters
|
||||||
|
pos, andx_command, andx_reserved, andx_offset, count_low, remaining, count_high, reserved = bin.unpack("<CCSSSSS", parameters)
|
||||||
|
if(reserved == nil) then
|
||||||
|
return false, "SMB: ERROR: Ran off the end of SMB packet; likely due to server truncation [28]"
|
||||||
|
end
|
||||||
|
|
||||||
|
response['count_low'] = count_low
|
||||||
|
response['remaining'] = remaining
|
||||||
|
response['count_high'] = count_high
|
||||||
|
response['reserved'] = count_reserved
|
||||||
|
|
||||||
|
return true, response
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
---This is the core of making MSRPC calls. It sends out a MSRPC packet with the given parameters and data.
|
---This is the core of making MSRPC calls. It sends out a MSRPC packet with the given parameters and data.
|
||||||
-- Don't confuse these parameters and data with SMB's concepts of parameters and data -- they are completely
|
-- Don't confuse these parameters and data with SMB's concepts of parameters and data -- they are completely
|
||||||
-- different. In fact, these parameters and data are both sent in the SMB packet's 'data' section.
|
-- different. In fact, these parameters and data are both sent in the SMB packet's 'data' section.
|
||||||
@@ -1742,7 +1951,7 @@ function send_transaction(smb, func, function_parameters, function_data)
|
|||||||
-- Check if it worked
|
-- Check if it worked
|
||||||
pos, header1, header2, header3, header4, command, status, flags, flags2, pid_high, signature, unused, tid, pid, uid, mid = bin.unpack("<CCCCCICSSlSSSSS", header)
|
pos, header1, header2, header3, header4, command, status, flags, flags2, pid_high, signature, unused, tid, pid, uid, mid = bin.unpack("<CCCCCICSSlSSSSS", header)
|
||||||
if(mid == nil) then
|
if(mid == nil) then
|
||||||
return false, "SMB: SMB server didn't comply with standards (incorrect data was returned) [16]"
|
return false, "SMB: ERROR: Ran off the end of SMB packet; likely due to server truncation [29]"
|
||||||
end
|
end
|
||||||
if(status ~= 0) then
|
if(status ~= 0) then
|
||||||
if(status_names[status] == nil) then
|
if(status_names[status] == nil) then
|
||||||
@@ -1755,7 +1964,7 @@ function send_transaction(smb, func, function_parameters, function_data)
|
|||||||
-- Parse the parameters
|
-- Parse the parameters
|
||||||
pos, total_word_count, total_data_count, reserved1, parameter_count, parameter_offset, parameter_displacement, data_count, data_offset, data_displacement, setup_count, reserved2 = bin.unpack("<SSSSSSSSSCC", parameters)
|
pos, total_word_count, total_data_count, reserved1, parameter_count, parameter_offset, parameter_displacement, data_count, data_offset, data_displacement, setup_count, reserved2 = bin.unpack("<SSSSSSSSSCC", parameters)
|
||||||
if(reserved2 == nil) then
|
if(reserved2 == nil) then
|
||||||
return false, "SMB: SMB server didn't comply with standards (incorrect data was returned) [17]"
|
return false, "SMB: ERROR: Ran off the end of SMB packet; likely due to server truncation [30]"
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Convert the parameter/data offsets into something more useful (the offset into the data section)
|
-- Convert the parameter/data offsets into something more useful (the offset into the data section)
|
||||||
@@ -1868,11 +2077,12 @@ status_codes =
|
|||||||
NT_STATUS_WERR_ACCESS_DENIED = 0x00000005,
|
NT_STATUS_WERR_ACCESS_DENIED = 0x00000005,
|
||||||
NT_STATUS_WERR_INVALID_NAME = 0x0000007b,
|
NT_STATUS_WERR_INVALID_NAME = 0x0000007b,
|
||||||
NT_STATUS_WERR_UNKNOWN_LEVEL = 0x0000007c,
|
NT_STATUS_WERR_UNKNOWN_LEVEL = 0x0000007c,
|
||||||
|
NT_STATUS_WERR_MORE_DATA = 0x000000ea,
|
||||||
NT_STATUS_NO_MORE_ITEMS = 0x00000103,
|
NT_STATUS_NO_MORE_ITEMS = 0x00000103,
|
||||||
NT_STATUS_MORE_ENTRIES = 0x00000105,
|
NT_STATUS_MORE_ENTRIES = 0x00000105,
|
||||||
NT_STATUS_SOME_NOT_MAPPED = 0x00000107,
|
NT_STATUS_SOME_NOT_MAPPED = 0x00000107,
|
||||||
DOS_STATUS_UNKNOWN_ERROR = 0x00010001,
|
DOS_STATUS_UNKNOWN_ERROR = 0x00010001,
|
||||||
DOS_STATUS_UNKNOWN_ERROR_2 = 0x00010002,
|
DOS_STATUS_NONSPECIFIC_ERROR = 0x00010002,
|
||||||
DOS_STATUS_DIRECTORY_NOT_FOUND = 0x00030001,
|
DOS_STATUS_DIRECTORY_NOT_FOUND = 0x00030001,
|
||||||
DOS_STATUS_ACCESS_DENIED = 0x00050001,
|
DOS_STATUS_ACCESS_DENIED = 0x00050001,
|
||||||
DOS_STATUS_INVALID_FID = 0x00060001,
|
DOS_STATUS_INVALID_FID = 0x00060001,
|
||||||
|
|||||||
@@ -63,6 +63,8 @@ Entry{ category = "version", filename = "skypev2-version.nse" }
|
|||||||
Entry{ category = "intrusive", filename = "smb-check-vulns.nse" }
|
Entry{ category = "intrusive", filename = "smb-check-vulns.nse" }
|
||||||
Entry{ category = "discovery", filename = "smb-enum-domains.nse" }
|
Entry{ category = "discovery", filename = "smb-enum-domains.nse" }
|
||||||
Entry{ category = "intrusive", filename = "smb-enum-domains.nse" }
|
Entry{ category = "intrusive", filename = "smb-enum-domains.nse" }
|
||||||
|
Entry{ category = "discovery", filename = "smb-enum-processes.nse" }
|
||||||
|
Entry{ category = "intrusive", filename = "smb-enum-processes.nse" }
|
||||||
Entry{ category = "discovery", filename = "smb-enum-sessions.nse" }
|
Entry{ category = "discovery", filename = "smb-enum-sessions.nse" }
|
||||||
Entry{ category = "intrusive", filename = "smb-enum-sessions.nse" }
|
Entry{ category = "intrusive", filename = "smb-enum-sessions.nse" }
|
||||||
Entry{ category = "discovery", filename = "smb-enum-shares.nse" }
|
Entry{ category = "discovery", filename = "smb-enum-shares.nse" }
|
||||||
|
|||||||
@@ -61,6 +61,9 @@ author = "Ron Bowes"
|
|||||||
copyright = "Ron Bowes"
|
copyright = "Ron Bowes"
|
||||||
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
|
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
|
||||||
categories = {"intrusive"}
|
categories = {"intrusive"}
|
||||||
|
-- Set the runlevel to >2 so this runs last (so if it DOES crash something, it doesn't
|
||||||
|
-- till other scans have had a chance to run)
|
||||||
|
runlevel = 2
|
||||||
|
|
||||||
require 'msrpc'
|
require 'msrpc'
|
||||||
require 'smb'
|
require 'smb'
|
||||||
@@ -181,6 +184,7 @@ end
|
|||||||
|
|
||||||
|
|
||||||
action = function(host)
|
action = function(host)
|
||||||
|
|
||||||
local status, result
|
local status, result
|
||||||
local response = " \n"
|
local response = " \n"
|
||||||
local found = false
|
local found = false
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ hostrule = function(host)
|
|||||||
end
|
end
|
||||||
|
|
||||||
action = function(host)
|
action = function(host)
|
||||||
|
|
||||||
local response = " \n"
|
local response = " \n"
|
||||||
local status, smbstate
|
local status, smbstate
|
||||||
local i, j
|
local i, j
|
||||||
|
|||||||
282
scripts/smb-enum-processes.nse
Normal file
282
scripts/smb-enum-processes.nse
Normal file
@@ -0,0 +1,282 @@
|
|||||||
|
|
||||||
|
description = [[
|
||||||
|
Pulls a list of processes from the remote server over SMB (using the remote registry service and
|
||||||
|
HKEY_PERFORMANCE_DATA).
|
||||||
|
|
||||||
|
Requires Administrator access.
|
||||||
|
|
||||||
|
WARNING: I have experienced crashes in regsvc.exe while making registry calls against a fully patched Windows
|
||||||
|
2000 system; I've fixed the issue that caused it, but there's no guarantee that it (or a similar vuln in the
|
||||||
|
same code) won't show up again.
|
||||||
|
]]
|
||||||
|
|
||||||
|
---
|
||||||
|
-- @usage
|
||||||
|
-- nmap --script smb-enum-processes.nse -p445 <host>
|
||||||
|
-- sudo nmap -sU -sS --script smb-enum-processes.nse -p U:137,T:139 <host>
|
||||||
|
--
|
||||||
|
---
|
||||||
|
-- @output
|
||||||
|
-- Host script results:
|
||||||
|
-- |_ smb-enum-processes: Idle, _Total, System, wmiprvse, VMwareUser, VMwareTray, smss, csrss, winlogon, services, lsass, logon.scr, spoolsv, msdtc, VMwareService, svchost, alg, explorer
|
||||||
|
-- --
|
||||||
|
-- Host script results:
|
||||||
|
-- | smb-enum-processes:
|
||||||
|
-- | Idle
|
||||||
|
-- | | PID: 0, Parent: 0 [Idle]
|
||||||
|
-- | | Priority: 0
|
||||||
|
-- | |_Thread Count: 1, Handle Count: 0
|
||||||
|
-- | System
|
||||||
|
-- | | PID: 4, Parent: 0 [Idle]
|
||||||
|
-- | | Priority: 8
|
||||||
|
-- | |_Thread Count: 48, Handle Count: 392
|
||||||
|
-- | VMwareUser
|
||||||
|
-- | | PID: 212, Parent: 1832 [explorer]
|
||||||
|
-- | | Priority: 8
|
||||||
|
-- | |_Thread Count: 1, Handle Count: 45
|
||||||
|
-- | VMwareTray
|
||||||
|
-- | | PID: 240, Parent: 1832 [explorer]
|
||||||
|
-- | | Priority: 8
|
||||||
|
-- | |_Thread Count: 1, Handle Count: 41
|
||||||
|
-- | smss
|
||||||
|
-- | | PID: 252, Parent: 4 [System]
|
||||||
|
-- | | Priority: 11
|
||||||
|
-- | |_Thread Count: 3, Handle Count: 19
|
||||||
|
-- | csrss
|
||||||
|
-- | | PID: 300, Parent: 252 [smss]
|
||||||
|
-- | | Priority: 13
|
||||||
|
-- | |_Thread Count: 10, Handle Count: 347
|
||||||
|
-- | winlogon
|
||||||
|
-- | | PID: 324, Parent: 252 [smss]
|
||||||
|
-- | | Priority: 13
|
||||||
|
-- | |_Thread Count: 18, Handle Count: 513
|
||||||
|
-- | services
|
||||||
|
-- | | PID: 372, Parent: 324 [winlogon]
|
||||||
|
-- | | Priority: 9
|
||||||
|
-- | |_Thread Count: 17, Handle Count: 275
|
||||||
|
-- | lsass
|
||||||
|
-- | | PID: 384, Parent: 324 [winlogon]
|
||||||
|
-- | | Priority: 9
|
||||||
|
-- | |_Thread Count: 29, Handle Count: 415
|
||||||
|
-- | logon.scr
|
||||||
|
-- | | PID: 868, Parent: 324 [winlogon]
|
||||||
|
-- | | Priority: 4
|
||||||
|
-- | |_Thread Count: 1, Handle Count: 22
|
||||||
|
-- ...
|
||||||
|
--
|
||||||
|
-- @args smb* This script supports the <code>smbusername</code>,
|
||||||
|
-- <code>smbpassword</code>, <code>smbhash</code>, and <code>smbtype</code>
|
||||||
|
-- script arguments of the <code>smb</code> module.
|
||||||
|
-----------------------------------------------------------------------
|
||||||
|
|
||||||
|
author = "Ron Bowes"
|
||||||
|
copyright = "Ron Bowes"
|
||||||
|
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
|
||||||
|
categories = {"discovery","intrusive"}
|
||||||
|
|
||||||
|
require "bin"
|
||||||
|
require 'msrpc'
|
||||||
|
require 'msrpcperformance'
|
||||||
|
require 'smb'
|
||||||
|
require 'stdnse'
|
||||||
|
|
||||||
|
-- Strings used to separate processes from one another.
|
||||||
|
local separators = {
|
||||||
|
first = "-+-";
|
||||||
|
last = " `-";
|
||||||
|
middle = " +-";
|
||||||
|
only = "---";
|
||||||
|
}
|
||||||
|
|
||||||
|
function psl_add (psl, ps)
|
||||||
|
-- Add process.
|
||||||
|
psl[ps.pid] = ps
|
||||||
|
|
||||||
|
-- Add dummy parent if no real one exists.
|
||||||
|
if psl[ps.ppid] == nil then
|
||||||
|
psl[ps.ppid] = {
|
||||||
|
name = 'Unknown';
|
||||||
|
pid = ps.ppid;
|
||||||
|
ppid = ps.ppid;
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function psl_mode (list, i)
|
||||||
|
local mode
|
||||||
|
|
||||||
|
-- Decide connector for process.
|
||||||
|
if table.maxn(list) == 1 then
|
||||||
|
mode = "only"
|
||||||
|
elseif i == 1 then
|
||||||
|
mode = "first"
|
||||||
|
elseif i == table.maxn(list) then
|
||||||
|
mode = "last"
|
||||||
|
else
|
||||||
|
mode = "middle"
|
||||||
|
end
|
||||||
|
|
||||||
|
return mode
|
||||||
|
end
|
||||||
|
|
||||||
|
function psl_print (psl)
|
||||||
|
local result = ""
|
||||||
|
|
||||||
|
-- Find how many root processes there are.
|
||||||
|
local roots = {}
|
||||||
|
for i,ps in pairs(psl) do
|
||||||
|
if psl[ps.ppid] == nil or ps.ppid == ps.pid then
|
||||||
|
table.insert(roots, i)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
table.sort(roots)
|
||||||
|
|
||||||
|
-- Create vertical sibling link.
|
||||||
|
local bars = {}
|
||||||
|
if table.maxn(roots) ~= 1 then
|
||||||
|
table.insert(bars, 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Print out each root of the tree.
|
||||||
|
for i,root in ipairs(roots) do
|
||||||
|
local mode = psl_mode(roots, i)
|
||||||
|
result = result .. psl_tree(psl, root, 0, bars, mode)
|
||||||
|
end
|
||||||
|
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
|
||||||
|
function psl_tree (psl, pid, column, bars, mode)
|
||||||
|
local ps = psl[pid]
|
||||||
|
|
||||||
|
-- Delete vertical sibling link.
|
||||||
|
if mode == 'last' then
|
||||||
|
table.remove(bars)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Print lead-in.
|
||||||
|
local prefix = ''
|
||||||
|
if mode == 'middle' or mode == 'last' then
|
||||||
|
prefix = '\n'
|
||||||
|
|
||||||
|
local i = 1
|
||||||
|
for j = 1, column do
|
||||||
|
if table.maxn(bars) >= i and
|
||||||
|
bars[i] == j then
|
||||||
|
prefix = prefix .. '|'
|
||||||
|
i = i + 1
|
||||||
|
else
|
||||||
|
prefix = prefix .. ' '
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Format process itself.
|
||||||
|
output = separators[mode] .. ps.name .. '(' .. ps.pid .. ')'
|
||||||
|
column = column + #output
|
||||||
|
local result = prefix .. output
|
||||||
|
|
||||||
|
-- Find process' children.
|
||||||
|
local children = {}
|
||||||
|
for child_pid,child in pairs(psl) do
|
||||||
|
if child_pid ~= pid and child.ppid == pid then
|
||||||
|
table.insert(children, child_pid)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
table.sort(children)
|
||||||
|
|
||||||
|
-- Create vertical sibling link between children.
|
||||||
|
if table.maxn(children) > 1 then
|
||||||
|
table.insert(bars, column + 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Format process' children.
|
||||||
|
for i,pid in ipairs(children) do
|
||||||
|
local mode = psl_mode(children, i)
|
||||||
|
result = result .. psl_tree(psl, pid, column, bars, mode)
|
||||||
|
end
|
||||||
|
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
|
||||||
|
hostrule = function(host)
|
||||||
|
return smb.get_port(host) ~= nil
|
||||||
|
end
|
||||||
|
|
||||||
|
action = function(host)
|
||||||
|
|
||||||
|
local status, result
|
||||||
|
local process
|
||||||
|
local response = " \n"
|
||||||
|
|
||||||
|
-- Get the process list
|
||||||
|
status, result = msrpcperformance.get_performance_data(host, "230")
|
||||||
|
if(status == false) then
|
||||||
|
if(nmap.debugging() > 0) then
|
||||||
|
return "ERROR: " .. result
|
||||||
|
else
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Get the process table
|
||||||
|
process = result['Process']
|
||||||
|
|
||||||
|
-- for i, v in pairs(result['Processor']['_Total']) do
|
||||||
|
-- io.write(string.format("i = %s\n", i))
|
||||||
|
-- end
|
||||||
|
|
||||||
|
-- Put the processes into an array, and sort them by process id
|
||||||
|
local names = {}
|
||||||
|
for i, v in pairs(process) do
|
||||||
|
if(i ~= '_Total') then
|
||||||
|
names[#names + 1] = i
|
||||||
|
end
|
||||||
|
end
|
||||||
|
table.sort(names, function (a, b) return process[a]['ID Process'] < process[b]['ID Process'] end)
|
||||||
|
|
||||||
|
-- Put the processes into an array indexed by process id and with a value equal to the name (so we can look it up
|
||||||
|
-- easily when we need to)
|
||||||
|
local process_id = {}
|
||||||
|
for i, v in pairs(process) do
|
||||||
|
process_id[v['ID Process']] = i
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
if(nmap.verbosity() == 1) then
|
||||||
|
local psl = {}
|
||||||
|
for i,name in ipairs(names) do
|
||||||
|
if(name ~= '_Total') then
|
||||||
|
psl_add(psl, {
|
||||||
|
name = name;
|
||||||
|
pid = process[name]['ID Process'];
|
||||||
|
ppid = process[name]['Creating Process ID'];
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
response = ' \n' .. psl_print(psl)
|
||||||
|
elseif(nmap.verbosity() > 0) then
|
||||||
|
for i = 1, #names, 1 do
|
||||||
|
local name = names[i]
|
||||||
|
if(name ~= '_Total') then
|
||||||
|
local parent = process_id[process[name]['Creating Process ID']]
|
||||||
|
if(parent == nil) then
|
||||||
|
parent = "n/a"
|
||||||
|
end
|
||||||
|
|
||||||
|
-- response = response .. string.format("%6d %24s (Parent: %24s, Priority: %4d, Threads: %4d, Handles: %4d)\n", process[name]['ID Process'], name, parent, process[name]['Priority Base'], process[name]['Thread Count'], process[name]['Handle Count'])
|
||||||
|
|
||||||
|
response = response .. string.format("%s [%d]\n", name, process[name]['ID Process'])
|
||||||
|
response = response .. string.format("| Parent: %s [%s]\n", process[name]['Creating Process ID'], parent)
|
||||||
|
response = response .. string.format("| Priority: %s, Thread Count: %s, Handle Count: %s\n", process[name]['Priority Base'], process[name]['Thread Count'], process[name]['Handle Count'])
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
else
|
||||||
|
response = stdnse.strjoin(", ", names)
|
||||||
|
end
|
||||||
|
|
||||||
|
return response
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
@@ -247,6 +247,7 @@ local function winreg_enum_rids(host)
|
|||||||
end
|
end
|
||||||
|
|
||||||
action = function(host)
|
action = function(host)
|
||||||
|
|
||||||
local response = " \n"
|
local response = " \n"
|
||||||
|
|
||||||
local status1, status2
|
local status1, status2
|
||||||
|
|||||||
@@ -191,7 +191,9 @@ function check_shares(host, shares)
|
|||||||
stdnse.print_debug(3, "EnumShares: Access was denied")
|
stdnse.print_debug(3, "EnumShares: Access was denied")
|
||||||
denied_shares[#denied_shares + 1] = shares[i]
|
denied_shares[#denied_shares + 1] = shares[i]
|
||||||
else
|
else
|
||||||
stdnse.print_debug(3, "ERROR: EnumShares: Share didn't pan out: %s", err)
|
-- If we're here, an error that we weren't prepared for came up.
|
||||||
|
smb.stop(smbstate)
|
||||||
|
return false, string.format("Error while checking shares: %s", err)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
-- Add it to allowed shares
|
-- Add it to allowed shares
|
||||||
@@ -244,6 +246,7 @@ local function get_share_info(host, name)
|
|||||||
end
|
end
|
||||||
|
|
||||||
action = function(host)
|
action = function(host)
|
||||||
|
|
||||||
local enum_result
|
local enum_result
|
||||||
local result, shared
|
local result, shared
|
||||||
local response = " \n"
|
local response = " \n"
|
||||||
|
|||||||
@@ -9,10 +9,10 @@ can be fine tuned using Nmap parameters. For the most possible information,
|
|||||||
leave the defaults; however, there are advantages to using them individually.
|
leave the defaults; however, there are advantages to using them individually.
|
||||||
|
|
||||||
Advantages of using SAMR enumeration:
|
Advantages of using SAMR enumeration:
|
||||||
* Stealthier (requires one packet/user account, whereas LSA uses at least 20
|
* Stealthier (requires one packet/user account, whereas LSA uses at least 10
|
||||||
packets; additionally, LSA makes a lot of noise in the Windows event log (LSA
|
packets while SAMR uses half that; additionally, LSA makes a lot of noise in
|
||||||
enumeration is the only script I (Ron Bowes) have been called on by the
|
the Windows event log (LSA enumeration is the only script I (Ron Bowes) have
|
||||||
administrator of a box I was testing against).
|
been called on by the administrator of a box I was testing against).
|
||||||
* More information is returned (more than just the username).
|
* More information is returned (more than just the username).
|
||||||
* Every account will be found, since they're being enumerated with a function
|
* Every account will be found, since they're being enumerated with a function
|
||||||
that's designed to enumerate users.
|
that's designed to enumerate users.
|
||||||
@@ -58,16 +58,11 @@ a user on a domain or system. An LSA function is exposed which lets us convert t
|
|||||||
(say, 1000) to the username (say, "Ron"). So, the technique will essentially try
|
(say, 1000) to the username (say, "Ron"). So, the technique will essentially try
|
||||||
converting 1000 to a name, then 1001, 1002, etc., until we think we're done.
|
converting 1000 to a name, then 1001, 1002, etc., until we think we're done.
|
||||||
|
|
||||||
To do this, this script breaks users into groups of five RIDs, then checked individually
|
To do this, the script breaks users into groups of RIDs based on the <code>LSA_GROUPSIZE</code>
|
||||||
(checking too many at once causes problems). We continue checking until we reach
|
constant. All members of this group are checked simultaneously, and the responses recorded.
|
||||||
1100, and get an empty group of five. This probably isn't the most effective way, but it
|
When a series of empty groups are found (<code>LSA_MINEMPTY</code> groups, specifically),
|
||||||
seems to work. It might be a good idea to modify this, in the future, with some more
|
the scan ends. As long as you are getting a few groups with active accounts, the scan will
|
||||||
intelligence. I (Ron Bowes) performed a test on an old server with a lot of accounts,
|
continue.
|
||||||
and these were the active RIDs: 500, 501, 1000, 1030, 1031, 1053, 1054, 1055,
|
|
||||||
1056, 1057, 1058, 1059, 1060, 1061, 1062, 1063, 1064, 1065, 1066, 1067, 1070,
|
|
||||||
1075, 1081, 1088, 1090. The jump from 1000 to 1030 is quite large and can easily
|
|
||||||
result in missing accounts, in an automated check. An ideal solution might be to continue
|
|
||||||
doing groups of 5, but wait until we get 5-10 consecutive empty groups before giving up.
|
|
||||||
|
|
||||||
Before attempting this conversion, the SID of the server has to be determined.
|
Before attempting this conversion, the SID of the server has to be determined.
|
||||||
The SID is determined by doing the reverse operation; that is, by converting a name into
|
The SID is determined by doing the reverse operation; that is, by converting a name into
|
||||||
@@ -156,284 +151,8 @@ hostrule = function(host)
|
|||||||
return smb.get_port(host) ~= nil
|
return smb.get_port(host) ~= nil
|
||||||
end
|
end
|
||||||
|
|
||||||
---Attempt to enumerate users through SAMR methods. See the file description for more information.
|
|
||||||
--
|
|
||||||
--@param host The host object.
|
|
||||||
--@return Status (true or false).
|
|
||||||
--@return Array of user tables (if status is true) or an an error string (if
|
|
||||||
--status is false). Each user table contains the fields <code>name</code>,
|
|
||||||
--<code>domain</code>, <code>fullname</code>, <code>rid</code>, and
|
|
||||||
--<code>description</code>.
|
|
||||||
local function enum_samr(host)
|
|
||||||
local i, j
|
|
||||||
|
|
||||||
stdnse.print_debug(3, "Entering enum_samr()")
|
|
||||||
|
|
||||||
local smbstate
|
|
||||||
local bind_result, connect4_result, enumdomains_result
|
|
||||||
local connect_handle
|
|
||||||
local status, smbstate
|
|
||||||
local response = {}
|
|
||||||
|
|
||||||
-- Create the SMB session
|
|
||||||
status, smbstate = msrpc.start_smb(host, msrpc.SAMR_PATH)
|
|
||||||
|
|
||||||
if(status == false) then
|
|
||||||
return false, smbstate
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Bind to SAMR service
|
|
||||||
status, bind_result = msrpc.bind(smbstate, msrpc.SAMR_UUID, msrpc.SAMR_VERSION, nil)
|
|
||||||
if(status == false) then
|
|
||||||
msrpc.stop_smb(smbstate)
|
|
||||||
return false, bind_result
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Call connect4()
|
|
||||||
status, connect4_result = msrpc.samr_connect4(smbstate, host.ip)
|
|
||||||
if(status == false) then
|
|
||||||
msrpc.stop_smb(smbstate)
|
|
||||||
return false, connect4_result
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Save the connect_handle
|
|
||||||
connect_handle = connect4_result['connect_handle']
|
|
||||||
|
|
||||||
-- Call EnumDomains()
|
|
||||||
status, enumdomains_result = msrpc.samr_enumdomains(smbstate, connect_handle)
|
|
||||||
if(status == false) then
|
|
||||||
msrpc.stop_smb(smbstate)
|
|
||||||
return false, enumdomains_result
|
|
||||||
end
|
|
||||||
|
|
||||||
-- If no domains were returned, go back with an error
|
|
||||||
if(#enumdomains_result['sam']['entries'] == 0) then
|
|
||||||
msrpc.stop_smb(smbstate)
|
|
||||||
return false, "Couldn't find any domains"
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Now, loop through the domains and find the users
|
|
||||||
for i = 1, #enumdomains_result['sam']['entries'], 1 do
|
|
||||||
|
|
||||||
local domain = enumdomains_result['sam']['entries'][i]['name']
|
|
||||||
-- We don't care about the 'builtin' domain, in all my tests it's empty
|
|
||||||
if(domain ~= 'Builtin') then
|
|
||||||
local sid
|
|
||||||
local domain_handle
|
|
||||||
local opendomain_result, querydisplayinfo_result
|
|
||||||
|
|
||||||
-- Call LookupDomain()
|
|
||||||
status, lookupdomain_result = msrpc.samr_lookupdomain(smbstate, connect_handle, domain)
|
|
||||||
if(status == false) then
|
|
||||||
msrpc.stop_smb(smbstate)
|
|
||||||
return false, lookupdomain_result
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Save the sid
|
|
||||||
sid = lookupdomain_result['sid']
|
|
||||||
|
|
||||||
-- Call OpenDomain()
|
|
||||||
status, opendomain_result = msrpc.samr_opendomain(smbstate, connect_handle, sid)
|
|
||||||
if(status == false) then
|
|
||||||
msrpc.stop_smb(smbstate)
|
|
||||||
return false, opendomain_result
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Save the domain handle
|
|
||||||
domain_handle = opendomain_result['domain_handle']
|
|
||||||
|
|
||||||
-- Loop as long as we're getting valid results
|
|
||||||
j = 0
|
|
||||||
repeat
|
|
||||||
-- Call QueryDisplayInfo()
|
|
||||||
status, querydisplayinfo_result = msrpc.samr_querydisplayinfo(smbstate, domain_handle, j)
|
|
||||||
if(status == false) then
|
|
||||||
msrpc.stop_smb(smbstate)
|
|
||||||
return false, querydisplayinfo_result
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Save the response
|
|
||||||
if(querydisplayinfo_result['return'] ~= 0 and querydisplayinfo_result['info'] ~= nil and querydisplayinfo_result['info']['entries'] ~= nil and querydisplayinfo_result['info']['entries'][1] ~= nil) then
|
|
||||||
local array = {}
|
|
||||||
local k
|
|
||||||
|
|
||||||
-- The reason these are all indexed from '1' is because we request names one at a time.
|
|
||||||
array['domain'] = domain
|
|
||||||
array['name'] = querydisplayinfo_result['info']['entries'][1]['account_name']
|
|
||||||
array['fullname'] = querydisplayinfo_result['info']['entries'][1]['full_name']
|
|
||||||
array['description'] = querydisplayinfo_result['info']['entries'][1]['description']
|
|
||||||
array['rid'] = querydisplayinfo_result['info']['entries'][1]['rid']
|
|
||||||
array['flags'] = querydisplayinfo_result['info']['entries'][1]['acct_flags']
|
|
||||||
array['source'] = "SAMR Enumeration"
|
|
||||||
|
|
||||||
-- Clean up the 'flags' array
|
|
||||||
for k = 1, #array['flags'], 1 do
|
|
||||||
array['flags'][k] = msrpc.samr_AcctFlags_tostr(array['flags'][k])
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Add it to the array
|
|
||||||
response[#response + 1] = array
|
|
||||||
end
|
|
||||||
j = j + 1
|
|
||||||
until querydisplayinfo_result['return'] == 0
|
|
||||||
|
|
||||||
-- Close the domain handle
|
|
||||||
msrpc.samr_close(smbstate, domain_handle)
|
|
||||||
|
|
||||||
-- Finally, fill in the response!
|
|
||||||
-- for i = 1, #querydisplayinfo_result['details'], 1 do
|
|
||||||
-- querydisplayinfo_result['details'][i]['domain'] = domain
|
|
||||||
-- -- All we get from this is users
|
|
||||||
-- querydisplayinfo_result['details'][i]['typestr'] = "User"
|
|
||||||
-- querydisplayinfo_result['details'][i]['source'] = "SAMR Enumeration"
|
|
||||||
-- response[#response + 1] = querydisplayinfo_result['details'][i]
|
|
||||||
-- end
|
|
||||||
end -- Checking for 'builtin'
|
|
||||||
end -- Domain loop
|
|
||||||
|
|
||||||
-- Close the connect handle
|
|
||||||
msrpc.samr_close(smbstate, connect_handle)
|
|
||||||
|
|
||||||
-- Stop the SAMR SMB
|
|
||||||
msrpc.stop_smb(smbstate)
|
|
||||||
|
|
||||||
stdnse.print_debug(3, "Leaving enum_samr()")
|
|
||||||
|
|
||||||
return true, response
|
|
||||||
end
|
|
||||||
|
|
||||||
---Attempt to enumerate users through LSA methods. See the file description for more information.
|
|
||||||
--
|
|
||||||
--@param host The host object.
|
|
||||||
--@return Status (true or false).
|
|
||||||
--@return Array of user tables (if status is true) or an an error string (if
|
|
||||||
--status is false). Each user table contains the fields <code>name</code>,
|
|
||||||
--<code>domain</code>, and <code>rid</code>.
|
|
||||||
local function enum_lsa(host)
|
|
||||||
|
|
||||||
local smbstate
|
|
||||||
local status
|
|
||||||
local response = {}
|
|
||||||
|
|
||||||
stdnse.print_debug(3, "Entering enum_lsa()")
|
|
||||||
|
|
||||||
-- Create the SMB session
|
|
||||||
status, smbstate = msrpc.start_smb(host, msrpc.LSA_PATH)
|
|
||||||
if(status == false) then
|
|
||||||
return false, smbstate
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Bind to LSA service
|
|
||||||
status, bind_result = msrpc.bind(smbstate, msrpc.LSA_UUID, msrpc.LSA_VERSION, nil)
|
|
||||||
if(status == false) then
|
|
||||||
msrpc.stop_smb(smbstate)
|
|
||||||
return false, bind_result
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Open the LSA policy
|
|
||||||
status, openpolicy2_result = msrpc.lsa_openpolicy2(smbstate, host.ip)
|
|
||||||
if(status == false) then
|
|
||||||
msrpc.stop_smb(smbstate)
|
|
||||||
return false, openpolicy2_result
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Start with some common names, as well as the name returned by the negotiate call
|
|
||||||
-- Vista doesn't like a 'null' after the server name, so fix that (TODO: the way I strip the null here feels hackish, is there a better way?)
|
|
||||||
names = {"administrator", "guest", "test", smbstate['domain'], string.sub(smbstate['server'], 1, #smbstate['server'] - 1) }
|
|
||||||
|
|
||||||
-- Get the server's name from nbstat
|
|
||||||
local result, server_name = netbios.get_server_name(host.ip)
|
|
||||||
if(result == true) then
|
|
||||||
names[#names + 1] = server_name
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Get the logged in user from nbstat
|
|
||||||
local result, user_name = netbios.get_user_name(host.ip)
|
|
||||||
if(result == true) then
|
|
||||||
names[#names + 1] = user_name
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Look up the names, if any are valid than the server's SID will be returned
|
|
||||||
status, lookupnames2_result = msrpc.lsa_lookupnames2(smbstate, openpolicy2_result['policy_handle'], names)
|
|
||||||
if(status == false) then
|
|
||||||
msrpc.stop_smb(smbstate)
|
|
||||||
return false, lookupnames2_result
|
|
||||||
end
|
|
||||||
-- Loop through the domains returned and find the users in each
|
|
||||||
for i = 1, #lookupnames2_result['domains']['domains'], 1 do
|
|
||||||
local domain = lookupnames2_result['domains']['domains'][i]['name']
|
|
||||||
local sid = lookupnames2_result['domains']['domains'][i]['sid']
|
|
||||||
local sids = { }
|
|
||||||
local start = 1000
|
|
||||||
|
|
||||||
-- Start by looking up 500 - 505 (will likely be Administrator + guest)
|
|
||||||
for j = 500, 505, 1 do
|
|
||||||
sids[#sids + 1] = sid .. "-" .. j
|
|
||||||
end
|
|
||||||
status, lookupsids2_result = msrpc.lsa_lookupsids2(smbstate, openpolicy2_result['policy_handle'], sids)
|
|
||||||
if(status == false) then
|
|
||||||
stdnse.print_debug(1, string.format("Error looking up RIDs: %s", lookupsids2_result))
|
|
||||||
else
|
|
||||||
-- Put the details for each name into an array
|
|
||||||
-- NOTE: Be sure to mirror any changes here in the next bit!
|
|
||||||
for j = 1, #lookupsids2_result['names']['names'], 1 do
|
|
||||||
if(lookupsids2_result['names']['names'][j]['sid_type'] ~= "SID_NAME_UNKNOWN") then
|
|
||||||
local result = {}
|
|
||||||
result['name'] = lookupsids2_result['names']['names'][j]['name']
|
|
||||||
result['rid'] = 500 + j - 1
|
|
||||||
result['domain'] = domain
|
|
||||||
result['typestr'] = msrpc.lsa_SidType_tostr(lookupsids2_result['names']['names'][j]['sid_type'])
|
|
||||||
result['source'] = "LSA Bruteforce"
|
|
||||||
response[#response + 1] = result
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Now do groups of 5 users, until we get past 1100 and have an empty group
|
|
||||||
repeat
|
|
||||||
local used_names = 0
|
|
||||||
local sids = {}
|
|
||||||
for j = start, start + 4, 1 do
|
|
||||||
sids[#sids + 1] = sid .. "-" .. j
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Try converting this group of RIDs into names
|
|
||||||
status, lookupsids2_result = msrpc.lsa_lookupsids2(smbstate, openpolicy2_result['policy_handle'], sids)
|
|
||||||
if(status == false) then
|
|
||||||
stdnse.print_debug(1, string.format("Error looking up RIDs: %s", lookupsids2_result))
|
|
||||||
else
|
|
||||||
-- Put the details for each name into an array
|
|
||||||
for j = 1, #lookupsids2_result['names']['names'], 1 do
|
|
||||||
if(lookupsids2_result['names']['names'][j]['sid_type'] ~= "SID_NAME_UNKNOWN") then
|
|
||||||
local result = {}
|
|
||||||
result['name'] = lookupsids2_result['names']['names'][j]['name']
|
|
||||||
result['rid'] = start + j - 1
|
|
||||||
result['domain'] = domain
|
|
||||||
result['typestr'] = msrpc.lsa_SidType_tostr(lookupsids2_result['names']['names'][j]['sid_type'])
|
|
||||||
result['source'] = "LSA Bruteforce"
|
|
||||||
response[#response + 1] = result
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Go to the next set of RIDs
|
|
||||||
start = start + 5
|
|
||||||
until status == false or (used_names == 0 and start > 1100)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Close the handle
|
|
||||||
msrpc.lsa_close(smbstate, openpolicy2_result['policy_handle'])
|
|
||||||
|
|
||||||
msrpc.stop_smb(smbstate)
|
|
||||||
|
|
||||||
stdnse.print_debug(3, "Leaving enum_lsa()")
|
|
||||||
|
|
||||||
return true, response
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
action = function(host)
|
action = function(host)
|
||||||
|
|
||||||
local i, j
|
local i, j
|
||||||
local samr_status = false
|
local samr_status = false
|
||||||
local lsa_status = false
|
local lsa_status = false
|
||||||
@@ -450,7 +169,7 @@ action = function(host)
|
|||||||
-- Try enumerating through LSA first. Since LSA provides less information, we want the
|
-- Try enumerating through LSA first. Since LSA provides less information, we want the
|
||||||
-- SAMR result to overwrite it.
|
-- SAMR result to overwrite it.
|
||||||
if(do_lsa) then
|
if(do_lsa) then
|
||||||
lsa_status, lsa_result = enum_lsa(host)
|
lsa_status, lsa_result = msrpc.lsa_enum_users(host)
|
||||||
if(lsa_status == false) then
|
if(lsa_status == false) then
|
||||||
if(nmap.debugging() > 0) then
|
if(nmap.debugging() > 0) then
|
||||||
response = response .. "ERROR: Couldn't enumerate through LSA: " .. lsa_result .. "\n"
|
response = response .. "ERROR: Couldn't enumerate through LSA: " .. lsa_result .. "\n"
|
||||||
@@ -468,7 +187,7 @@ action = function(host)
|
|||||||
|
|
||||||
-- Try enumerating through SAMR
|
-- Try enumerating through SAMR
|
||||||
if(do_samr) then
|
if(do_samr) then
|
||||||
samr_status, samr_result = enum_samr(host)
|
samr_status, samr_result = msrpc.samr_enum_users(host)
|
||||||
if(samr_status == false) then
|
if(samr_status == false) then
|
||||||
if(nmap.debugging() > 0) then
|
if(nmap.debugging() > 0) then
|
||||||
response = response .. "ERROR: Couldn't enumerate through SAMR: " .. samr_result .. "\n"
|
response = response .. "ERROR: Couldn't enumerate through SAMR: " .. samr_result .. "\n"
|
||||||
@@ -523,6 +242,7 @@ action = function(host)
|
|||||||
end
|
end
|
||||||
if(names[name]['fullname'] ~= nil) then response = response .. string.format(" |_ Full name: %s\n", names[name]['fullname']) end
|
if(names[name]['fullname'] ~= nil) then response = response .. string.format(" |_ Full name: %s\n", names[name]['fullname']) end
|
||||||
if(names[name]['description'] ~= nil) then response = response .. string.format(" |_ Description: %s\n", names[name]['description']) end
|
if(names[name]['description'] ~= nil) then response = response .. string.format(" |_ Description: %s\n", names[name]['description']) end
|
||||||
|
|
||||||
if(names[name]['flags'] ~= nil) then response = response .. string.format(" |_ Flags: %s\n", stdnse.strjoin(", ", names[name]['flags'])) end
|
if(names[name]['flags'] ~= nil) then response = response .. string.format(" |_ Flags: %s\n", stdnse.strjoin(", ", names[name]['flags'])) end
|
||||||
|
|
||||||
if(nmap.verbosity() > 1) then
|
if(nmap.verbosity() > 1) then
|
||||||
|
|||||||
Reference in New Issue
Block a user