diff --git a/CHANGELOG b/CHANGELOG index 90c5aa191..3f2463ba8 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -55,11 +55,15 @@ o Enhanced the AS Numbers script (ASN.nse) to better consolidate o Made DNS timeouts in NSE dependent on the timing template [Jah] -o Added two new nselib modules, netbios and smb, that contain common - code for scripts using NetBIOS and SMB. Also added or updated four - scripts that use the new modules: +o Added three new nselib modules: msrpc, netbios, and smb. As the + names suggest, they contain common code for scripts using MSRPC, + NetBIOS, and SMB. These modules allow scripts to extract a great + deal of information from hosts running Windows, particularly Windows + 2000. New or updated scripts using the modules are: nbstat.nse: get NetBIOS names and MAC address. - smb-enum.nse: enumerate SMB users and shares. + smb-enumdomain.nse: enumerate domains and policies. + smb-enumshares.nse: enumerate network shares. + smb-enumusers.nse: enumerate users and information about them. smb-os-discovery.nse: get operating system over SMB (replaces netbios-smb-os-discovery.nse). smb-security-mode.nse: determine if a host uses user-level or diff --git a/nselib/msrpc.lua b/nselib/msrpc.lua new file mode 100644 index 000000000..f1e7848c0 --- /dev/null +++ b/nselib/msrpc.lua @@ -0,0 +1,1695 @@ +--- By making heavy use of the 'smb' library, this library will call various MSRPC +-- functions. The functions used here can be access over TCP ports 445 and 139, +-- with an established session. A NULL session (the default) will work for some +-- functions and operating systems (or configurations), but not for others. \n +--\n +-- To make use of these function calls, a SMB session with the server has to be +-- established. This can be done manually with the 'smb' library, or the function +-- start_smb() can be called. \n +--\n +-- Next, the interface has to be bound. The bind() function will take care of that. \n +--\n +-- After that, you're free to call any function that's part of that interface. In +-- other words, if you bind to the SAMR interface, you can only call the samr_ +-- functions.\n +--\n +-- Although functions can be called in any order, many functions depend on the +-- value returned by other functions. I indicate those in the function comments, +-- so keep an eye out. \n +--\n +-- Something to note is that these functions, for the most part, return a whole ton +-- of stuff in an array. I basically wrote them to return every possible value +-- they get their hands on. I don't expect that most of them will be used, and I'm +-- not going to document them all in the function header; rather, I will document +-- the elements in the table that are more useful, you'll have to look at the actual +-- code (or the table) to see what else is available. \n +-- +--@author Ron Bowes +--@copyright See nmap's COPYING for licence +----------------------------------------------------------------------- +module(... or "msrpc", package.seeall) + +require 'bit' +require 'bin' +require 'netbios' +require 'smb' +require 'stdnse' + +-- The path, UUID, and version for SAMR +SAMR_PATH = "\\samr" +SAMR_UUID = bin.pack("CCCCCCCCCCCCCCCC", 0x78, 0x57, 0x34, 0x12, 0x34, 0x12, 0xcd, 0xab, 0xef, 0x00, 0x01, 0x23, 0x45, 0x67, 0x89, 0xac) +SAMR_VERSION = 0x01 + +-- The path, UUID, and version for SRVSVC +SRVSVC_PATH = "\\srvsvc" +SRVSVC_UUID = bin.pack("CCCCCCCCCCCCCCCC", 0xc8, 0x4f, 0x32, 0x4b, 0x70, 0x16, 0xd3, 0x01, 0x12, 0x78, 0x5a, 0x47, 0xbf, 0x6e, 0xe1, 0x88) +SRVSVC_VERSION = 0x03 + +-- The path, UUID, and version for LSA +LSA_PATH = "\\lsarpc" +LSA_UUID = bin.pack("CCCCCCCCCCCCCCCC", 0x78, 0x57, 0x34, 0x12, 0x34, 0x12, 0xcd, 0xab, 0xef, 0x00, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab) +LSA_VERSION = 0 + +-- This is the only transfer syntax I've seen in the wild, not that I've looked hard. It seems to work well. +TRANSFER_SYNTAX = bin.pack("CCCCCCCCCCCCCCCC", 0x04, 0x5d, 0x88, 0x8a, 0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00, 0x2b, 0x10, 0x48, 0x60) + +-- 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 + +-- A few error codes +STATUS_SOME_NOT_MAPPED = 0x00000107 +STATUS_INVALID_PARAMETER = 0xC000000D +STATUS_ACCESS_DENIED = 0xC0000022 +STATUS_BUFFER_TOO_SMALL = 0xC0000023 +STATUS_NONE_MAPPED = 0xC0000073 +STATUS_INSUFFICIENT_RESOURCES = 0xC000009A +STATUS_MORE_ENTRIES = 0x00000105 +STATUS_TOO_MANY_CONTEXT_IDS = 0xC000015A + +---Convert the return status of a function into a string. This isn't nearly an +-- exhaustive list, if I decide to go all out then I'll probably move this into +-- its own library. +--@param status the status to convert +--@return The string equivalent to the status. +local function status_to_string(status) + if(status == STATUS_ACCESS_DENIED) then + return "STATUS_ACCESS_DENIED" + elseif(status == STATUS_MORE_ENTRIES) then + return "STATUS_MORE_ENTRIES" + elseif(status == STATUS_TOO_MANY_CONTEXT_IDS) then + return "STATUS_TOO_MANY_CONTEXT_IDS" + elseif(status == STATUS_INSUFFICIENT_RESOURCES) then + return "STATUS_INSUFFICIENT_RESOURCES" + elseif(status == STATUS_BUFFER_TOO_SMALL) then + return "STATUS_BUFFER_TOO_SMALL" + elseif(status == STATUS_INVALID_PARAMETER) then + return "STATUS_INVALID_PARAMETER" + elseif(status == STATUS_SOME_NOT_MAPPED) then + return "STATUS_SOME_NOT_MAPPED" + elseif(status == STATUS_NONE_MAPPED) then + return "STATUS_NONE_MAPPED" + else + return string.format("STATUS_UNKNOWN_ERROR (0x%08x)", status) + end +end + +--- Convert a string to fake unicode (ascii with null characters between them), optionally add a null terminator, +-- and optionally align it to 4-byte boundaries. This is frequently used in MSRPC calls, so I put it here, but +-- it might be a good idea to move this function (and the converse one below) into a separate library. +--@param string The string to convert. +--@param do_null [optional] Add a null-terminator to the unicode string. Default false. +--@param do_align [optional] Align the string to a multiple of 4 bytes. Default false. +--@return The unicode version of the string. +local function string_to_unicode(string, do_null, do_align) + local i + local result = "" + + if(do_null == nil) then + do_null = false + end + if(do_align == nil) then + do_align = false + end + + -- Loop through the string, adding each character followed by a char(0) + for i = 1, string.len(string), 1 do + result = result .. string.sub(string, i, i) .. string.char(0) + end + + -- Add a null, if the caller requestd it + if(do_null == true) then + result = result .. string.char(0) .. string.char(0) + end + + -- Align it to a multiple of 4, if necessary + if(do_align) then + if(string.len(result) % 4 ~= 0) then + result = result .. string.char(0) .. string.char(0) + end + end + + return result +end + +--- Read a unicode string from a buffer, similar to how bin.unpack() would, optionally eat the null terminator, +-- and optionally align it to 4-byte boundaries. +--@param buffer The buffer to read from, typically the full 'arguments' value for MSRPC +--@param pos The position in the buffer to start (just like bin.unpack()) +--@param length The number of ascii characters that will be read (including the null, if do_null is set). +--@param do_null [optional] Remove a null terminator from the string as the last character. Default false. +--@param do_align [optional] Ensure that the number of bytes removed is a multiple of 4. +--@return (pos, string) The new position and the string read, again imitating bin.unpack(). +local function unicode_to_string(buffer, pos, length, do_null, do_align) + local i, ch, dummy + local string = "" + +stdnse.print_debug(5, "MSRPC: Entering unicode_to_string(pos = %d, length = %d)", pos, length) + + if(do_null == nil) then + do_null = false + end + if(do_align == nil) then + do_align = false + end + + if(do_null == true) then + length = length - 1 + end + + for j = 1, length, 1 do + + pos, ch, dummy = bin.unpack("--). +-- +--@param sid A SID object. +--@return A string representing the SID. +function sid_to_string(sid) + local i + local str + + local authority = bit.bor(bit.lshift(sid['authority_high'], 32), sid['authority']) + + str = string.format("S-%u-%u", sid['revision'], sid['authority']) + + for i = 1, sid['count'], 1 do + str = str .. string.format("-%u", sid['subauthorities'][i]) + end + + return str +end + + +--- This is a wrapper around the SMB class, designed to get SMB going quickly in a script. This will +-- connect to the SMB server, negotiate the protocol, open a session, connect to the IPC$ share, and +-- open the named pipe given by 'path'. When this successfully returns, the socket can be immediately +-- used for MSRPC. +-- +--@param host The host object. +--@param path The path to the named pipe; for example, msrpc.SAMR_PATH or msrpc.SRVSVC_PATH. +--@return (status, socket, uid, tid, fid, negotiate_result, session_result, tree_result, create_result) +-- if status is false, socket is an error message. Otherwise, the rest of the results are +-- returned. +function start_smb(host, path) + local status, socket, negotiate_result, session_result, tree_result, create_result + + -- Begin the SMB session + status, socket = smb.start(host) + if(status == false) then + return false, socket + end + + -- Negotiate the protocol + status, negotiate_result = smb.negotiate_protocol(socket) + if(status == false) then + smb.stop(socket) + return false, negotiate_result + end + + -- Start up a null session + status, session_result = smb.start_session(socket, "", negotiate_result['session_key'], negotiate_result['capabilities']) + if(status == false) then + smb.stop(socket) + return false, session_result + end + + -- Connect to IPC$ share + status, tree_result = smb.tree_connect(socket, "IPC$", session_result['uid']) + if(status == false) then + smb.stop(socket, session_result['uid']) + return false, tree_result + end + + -- Try to connect to requested pipe + status, create_result = smb.create_file(socket, path, session_result['uid'], tree_result['tid']) + if(status == false) then + smb.stop(socket, session_result['uid'], tree_result['tid']) + return false, create_result + end + + -- Return everything + return true, socket, session_result['uid'], tree_result['tid'], create_result['fid'], negotiate_result, session_result, tree_result, create_result +end + +--- A wrapper around the smb.stop() function. I only created it to add symmetry, so the code uses +-- the same class to start/stop the session. In the future, this may be expanded to close +-- handles before exiting. +-- +--@param socket The socket to close. +--@param uid The UserID, which will be logged off before closing the socket. +--@param tid The TreeID, which will be disconnected before closing the socket. +function stop_smb(socket, uid, tid) + smb.stop(socket, uid, tid) +end + +--- Bind to a MSRPC interface. Two common interfaces are SAML and SRVSVC, and can be found as +-- constants at the top of this file. Once this function has successfully returned, any MSRPC +-- call can be made (provided it doesn't depend on results from other MSRPC calls). +-- +--@param socket The socket in the appropriate state +--@param interface_uuid The interface to bind to. There are constants defined for these (SAMR_UUID, +-- etc.) +--@param interface_version The interface version to use. There are constants at the top (SAMR_VERSION, +-- etc.) +--@param transfer_syntax The transfer syntax to use. I don't really know what this is, but the value +-- was always the same on my tests. You can use the constant at the top (TRANSFER_SYNTAX), or +-- just set this parameter to 'nil'. +--@param uid The UserID we're sending the packets as +--@param tid The TreeID we're sending the packets to +--@param fid The FileID we're sending the packets to +--@return (status, result) If status is false, result is an error message. Otherwise, result is a +-- table of values, none of which are especially useful. +function bind(socket, interface_uuid, interface_version, transfer_syntax, uid, tid, fid) + local i + local status, result + local parameters, data + local pos, align + local response = {} + + stdnse.print_debug(2, "MSRPC: Sending Bind() request") + + -- Use the only transfer_syntax value I know of. + if(transfer_syntax == nil) then + transfer_syntax = TRANSFER_SYNTAX + end + + data = bin.pack("IIIISI<", arguments) + response['sid']['subauthorities'] = {} + for i = 1, response['sid']['count'], 1 do + pos, response['sid']['subauthorities'][i] = bin.unpack("SI<", sid['count'], sid['revision'], sid['count'], sid['authority_high'], sid['authority']) + for i = 1, sid['count'], 1 do + arguments = arguments .. bin.pack("SI<", arguments, pos) + sid['subauthorities'] = {} + for i = 1, sid['count'], 1 do + pos, sid['subauthorities'][i] = bin.unpack("SI<", sid['count'] + 1, sid['revision'], sid['count'] + 1, sid['authority_high'], sid['authority']) + for j = 1, sid['count'], 1 do + arguments = arguments .. bin.pack("SI<", arguments, pos) + sid['subauthorities'] = {} + for i = 1, sid['count'], 1 do + pos, sid['subauthorities'][i] = bin.unpack("S SMB_COM_NEGOTIATE_PROTOCOL --- S->C SMB_COM_NEGOTIATE_PROTOCOL --- C->S SMB_COM_SESSION_SETUP_ANDX --- S->C SMB_COM_SESSION_SETUP_ANDX --- C->S SMB_COM_TREE_CONNCT_ANDX --- S->C SMB_COM_TREE_CONNCT_ANDX --- --- In terms of functions here, the protocol is: --- status, socket = smb.start(host) --- status, negotiate_result = smb.negotiate_protocol(socket) --- status, session_result = smb.start_session(socket, username, negotiate_result['session_key'], negotiate_result['capabilities']) --- status, tree_result = smb.tree_connect(socket, path, session_result['uid']) --- --- To initially begin the connection, there are two options: +-- this. The basic login is this:\n +--\n +-- [connect]\n +-- C->S SMB_COM_NEGOTIATE\n +-- S->C SMB_COM_NEGOTIATE\n +-- C->S SMB_COM_SESSION_SETUP_ANDX\n +-- S->C SMB_COM_SESSION_SETUP_ANDX\n +-- C->S SMB_COM_TREE_CONNECT_ANDX\n +-- S->C SMB_COM_TREE_CONNECT_ANDX\n +-- ...\n +-- C->S SMB_COM_TREE_DISCONNECT\n +-- S->C SMB_COM_TREE_DISCONNECT\n +-- C->S SMB_COM_LOGOFF_ANDX\n +-- S->C SMB_COM_LOGOFF_ANDX\n +--\n +-- In terms of functions here, the protocol is:\n +-- status, socket = smb.start(host)\n +-- status, negotiate_result = smb.negotiate_protocol(socket)\n +-- status, session_result = smb.start_session(socket, username, negotiate_result['session_key'], negotiate_result['capabilities'])\n +-- status, tree_result = smb.tree_connect(socket, path, session_result['uid'])\n +-- status, disconnect_result = smb.tree_disconnect(socket, session_result['uid'], tree_result['tid'])\n +-- status, logoff_result = smb.logoff(socket, session_result['uid'])\n +-- status, err = smb.stop(socket)\n +--\n +-- Optionally, the 'stop' function can also call tree_disconnect and logoff, by giving it extra parameters:\n +-- status, err = smb.stop(socket, session_result['uid'], tree_result['tid'])\n +-- \n +-- To initially begin the connection, there are two options:\n -- 1) Attempt to start a raw session over 445, if it's open. \n -- 2) Attempt to start a NetBIOS session over 139. Although the -- protocol's the same, it requires a "session request" packet. -- That packet requires the computer's name, which is requested -- using a NBSTAT probe over UDP port 137. \n -- --- Once it's connected, a SMB_COM_NEGOTIATE_PROTOCOL packet is sent, +-- Once it's connected, a SMB_COM_NEGOTIATE packet is sent, -- requesting the protocol "NT LM 0.12", which is the most commonly -- supported one. Among other things, the server's response contains -- the host's security level, the system time, and the computer/domain --- name. --- +-- name.\n +--\n -- If that's successful, SMB_COM_SESSION_SETUP_ANDX is sent. It is essentially the logon -- packet, where the username, domain, and password are sent to the server for verification. -- The response to SMB_COM_SESSION_SETUP_ANDX is fairly simple, containing a boolean for --- success, along with the operating system and the lan manager name. --- --- After a successful SMB_COM_SESSION_START_ANDX has been made, a +-- success, along with the operating system and the lan manager name. \n +--\n +-- After a successful SMB_COM_SESSION_SETUP_ANDX has been made, a -- SMB_COM_TREE_CONNECT_ANDX packet can be sent. This is what connects to a share. --- The server responds to this with a boolean answer, and little more information. - +-- The server responds to this with a boolean answer, and little more information. \n +--\n -- Each share will either return STATUS_BAD_NETWORK_NAME if the share doesn't -- exist, STATUS_ACCESS_DENIED if it exists but we don't have access, or --- STATUS_SUCCESS if exists and we do have access. --- +-- STATUS_SUCCESS if exists and we do have access. \n +--\n -- Thanks go to Christopher R. Hertel and Implementing CIFS, which --- taught me everything I know about Microsoft's protocols. +-- taught me everything I know about Microsoft's protocols. \n -- --@author Ron Bowes --@copyright See nmaps COPYING for licence @@ -60,7 +71,610 @@ require 'bin' require 'netbios' require 'stdnse' -mutex_id = "SMB" +local mutex = nmap.mutex("SMB") + +local command_codes = {} +local command_names = {} +command_codes['SMB_COM_CREATE_DIRECTORY'] = 0x00 +command_codes['SMB_COM_DELETE_DIRECTORY'] = 0x01 +command_codes['SMB_COM_OPEN'] = 0x02 +command_codes['SMB_COM_CREATE'] = 0x03 +command_codes['SMB_COM_CLOSE'] = 0x04 +command_codes['SMB_COM_FLUSH'] = 0x05 +command_codes['SMB_COM_DELETE'] = 0x06 +command_codes['SMB_COM_RENAME'] = 0x07 +command_codes['SMB_COM_QUERY_INFORMATION'] = 0x08 +command_codes['SMB_COM_SET_INFORMATION'] = 0x09 +command_codes['SMB_COM_READ'] = 0x0A +command_codes['SMB_COM_WRITE'] = 0x0B +command_codes['SMB_COM_LOCK_BYTE_RANGE'] = 0x0C +command_codes['SMB_COM_UNLOCK_BYTE_RANGE'] = 0x0D +command_codes['SMB_COM_CREATE_TEMPORARY'] = 0x0E +command_codes['SMB_COM_CREATE_NEW'] = 0x0F +command_codes['SMB_COM_CHECK_DIRECTORY'] = 0x10 +command_codes['SMB_COM_PROCESS_EXIT'] = 0x11 +command_codes['SMB_COM_SEEK'] = 0x12 +command_codes['SMB_COM_LOCK_AND_READ'] = 0x13 +command_codes['SMB_COM_WRITE_AND_UNLOCK'] = 0x14 +command_codes['SMB_COM_READ_RAW'] = 0x1A +command_codes['SMB_COM_READ_MPX'] = 0x1B +command_codes['SMB_COM_READ_MPX_SECONDARY'] = 0x1C +command_codes['SMB_COM_WRITE_RAW'] = 0x1D +command_codes['SMB_COM_WRITE_MPX'] = 0x1E +command_codes['SMB_COM_WRITE_MPX_SECONDARY'] = 0x1F +command_codes['SMB_COM_WRITE_COMPLETE'] = 0x20 +command_codes['SMB_COM_QUERY_SERVER'] = 0x21 +command_codes['SMB_COM_SET_INFORMATION2'] = 0x22 +command_codes['SMB_COM_QUERY_INFORMATION2'] = 0x23 +command_codes['SMB_COM_LOCKING_ANDX'] = 0x24 +command_codes['SMB_COM_TRANSACTION'] = 0x25 +command_codes['SMB_COM_TRANSACTION_SECONDARY'] = 0x26 +command_codes['SMB_COM_IOCTL'] = 0x27 +command_codes['SMB_COM_IOCTL_SECONDARY'] = 0x28 +command_codes['SMB_COM_COPY'] = 0x29 +command_codes['SMB_COM_MOVE'] = 0x2A +command_codes['SMB_COM_ECHO'] = 0x2B +command_codes['SMB_COM_WRITE_AND_CLOSE'] = 0x2C +command_codes['SMB_COM_OPEN_ANDX'] = 0x2D +command_codes['SMB_COM_READ_ANDX'] = 0x2E +command_codes['SMB_COM_WRITE_ANDX'] = 0x2F +command_codes['SMB_COM_NEW_FILE_SIZE'] = 0x30 +command_codes['SMB_COM_CLOSE_AND_TREE_DISC'] = 0x31 +command_codes['SMB_COM_TRANSACTION2'] = 0x32 +command_codes['SMB_COM_TRANSACTION2_SECONDARY'] = 0x33 +command_codes['SMB_COM_FIND_CLOSE2'] = 0x34 +command_codes['SMB_COM_FIND_NOTIFY_CLOSE'] = 0x35 +command_codes['SMB_COM_TREE_CONNECT'] = 0x70 +command_codes['SMB_COM_TREE_DISCONNECT'] = 0x71 +command_codes['SMB_COM_NEGOTIATE'] = 0x72 +command_codes['SMB_COM_SESSION_SETUP_ANDX'] = 0x73 +command_codes['SMB_COM_LOGOFF_ANDX'] = 0x74 +command_codes['SMB_COM_TREE_CONNECT_ANDX'] = 0x75 +command_codes['SMB_COM_QUERY_INFORMATION_DISK'] = 0x80 +command_codes['SMB_COM_SEARCH'] = 0x81 +command_codes['SMB_COM_FIND'] = 0x82 +command_codes['SMB_COM_FIND_UNIQUE'] = 0x83 +command_codes['SMB_COM_FIND_CLOSE'] = 0x84 +command_codes['SMB_COM_NT_TRANSACT'] = 0xA0 +command_codes['SMB_COM_NT_TRANSACT_SECONDARY'] = 0xA1 +command_codes['SMB_COM_NT_CREATE_ANDX'] = 0xA2 +command_codes['SMB_COM_NT_CANCEL'] = 0xA4 +command_codes['SMB_COM_NT_RENAME'] = 0xA5 +command_codes['SMB_COM_OPEN_PRINT_FILE'] = 0xC0 +command_codes['SMB_COM_WRITE_PRINT_FILE'] = 0xC1 +command_codes['SMB_COM_CLOSE_PRINT_FILE'] = 0xC2 +command_codes['SMB_COM_GET_PRINT_QUEUE'] = 0xC3 +command_codes['SMB_COM_READ_BULK'] = 0xD8 +command_codes['SMB_COM_WRITE_BULK'] = 0xD9 +command_codes['SMB_COM_WRITE_BULK_DATA'] = 0xDA +command_codes['SMB_NO_FURTHER_COMMANDS'] = 0xFF + +for i, v in pairs(command_codes) do + command_names[v] = i +end + + + +local status_codes = {} +local status_names = {} +status_codes['NT_STATUS_OK'] = 0x0000 +status_codes['NT_STATUS_BUFFER_OVERFLOW'] = 0x80000005 +status_codes['NT_STATUS_UNSUCCESSFUL'] = 0xc0000001 +status_codes['NT_STATUS_NOT_IMPLEMENTED'] = 0xc0000002 +status_codes['NT_STATUS_INVALID_INFO_CLASS'] = 0xc0000003 +status_codes['NT_STATUS_INFO_LENGTH_MISMATCH'] = 0xc0000004 +status_codes['NT_STATUS_ACCESS_VIOLATION'] = 0xc0000005 +status_codes['NT_STATUS_IN_PAGE_ERROR'] = 0xc0000006 +status_codes['NT_STATUS_PAGEFILE_QUOTA'] = 0xc0000007 +status_codes['NT_STATUS_INVALID_HANDLE'] = 0xc0000008 +status_codes['NT_STATUS_BAD_INITIAL_STACK'] = 0xc0000009 +status_codes['NT_STATUS_BAD_INITIAL_PC'] = 0xc000000a +status_codes['NT_STATUS_INVALID_CID'] = 0xc000000b +status_codes['NT_STATUS_TIMER_NOT_CANCELED'] = 0xc000000c +status_codes['NT_STATUS_INVALID_PARAMETER'] = 0xc000000d +status_codes['NT_STATUS_NO_SUCH_DEVICE'] = 0xc000000e +status_codes['NT_STATUS_NO_SUCH_FILE'] = 0xc000000f +status_codes['NT_STATUS_INVALID_DEVICE_REQUEST'] = 0xc0000010 +status_codes['NT_STATUS_END_OF_FILE'] = 0xc0000011 +status_codes['NT_STATUS_WRONG_VOLUME'] = 0xc0000012 +status_codes['NT_STATUS_NO_MEDIA_IN_DEVICE'] = 0xc0000013 +status_codes['NT_STATUS_UNRECOGNIZED_MEDIA'] = 0xc0000014 +status_codes['NT_STATUS_NONEXISTENT_SECTOR'] = 0xc0000015 +status_codes['NT_STATUS_MORE_PROCESSING_REQUIRED'] = 0xc0000016 +status_codes['NT_STATUS_NO_MEMORY'] = 0xc0000017 +status_codes['NT_STATUS_CONFLICTING_ADDRESSES'] = 0xc0000018 +status_codes['NT_STATUS_NOT_MAPPED_VIEW'] = 0xc0000019 +status_codes['NT_STATUS_UNABLE_TO_FREE_VM'] = 0xc000001a +status_codes['NT_STATUS_UNABLE_TO_DELETE_SECTION'] = 0xc000001b +status_codes['NT_STATUS_INVALID_SYSTEM_SERVICE'] = 0xc000001c +status_codes['NT_STATUS_ILLEGAL_INSTRUCTION'] = 0xc000001d +status_codes['NT_STATUS_INVALID_LOCK_SEQUENCE'] = 0xc000001e +status_codes['NT_STATUS_INVALID_VIEW_SIZE'] = 0xc000001f +status_codes['NT_STATUS_INVALID_FILE_FOR_SECTION'] = 0xc0000020 +status_codes['NT_STATUS_ALREADY_COMMITTED'] = 0xc0000021 +status_codes['NT_STATUS_ACCESS_DENIED'] = 0xc0000022 +status_codes['NT_STATUS_BUFFER_TOO_SMALL'] = 0xc0000023 +status_codes['NT_STATUS_OBJECT_TYPE_MISMATCH'] = 0xc0000024 +status_codes['NT_STATUS_NONCONTINUABLE_EXCEPTION'] = 0xc0000025 +status_codes['NT_STATUS_INVALID_DISPOSITION'] = 0xc0000026 +status_codes['NT_STATUS_UNWIND'] = 0xc0000027 +status_codes['NT_STATUS_BAD_STACK'] = 0xc0000028 +status_codes['NT_STATUS_INVALID_UNWIND_TARGET'] = 0xc0000029 +status_codes['NT_STATUS_NOT_LOCKED'] = 0xc000002a +status_codes['NT_STATUS_PARITY_ERROR'] = 0xc000002b +status_codes['NT_STATUS_UNABLE_TO_DECOMMIT_VM'] = 0xc000002c +status_codes['NT_STATUS_NOT_COMMITTED'] = 0xc000002d +status_codes['NT_STATUS_INVALID_PORT_ATTRIBUTES'] = 0xc000002e +status_codes['NT_STATUS_PORT_MESSAGE_TOO_LONG'] = 0xc000002f +status_codes['NT_STATUS_INVALID_PARAMETER_MIX'] = 0xc0000030 +status_codes['NT_STATUS_INVALID_QUOTA_LOWER'] = 0xc0000031 +status_codes['NT_STATUS_DISK_CORRUPT_ERROR'] = 0xc0000032 +status_codes['NT_STATUS_OBJECT_NAME_INVALID'] = 0xc0000033 +status_codes['NT_STATUS_OBJECT_NAME_NOT_FOUND'] = 0xc0000034 +status_codes['NT_STATUS_OBJECT_NAME_COLLISION'] = 0xc0000035 +status_codes['NT_STATUS_HANDLE_NOT_WAITABLE'] = 0xc0000036 +status_codes['NT_STATUS_PORT_DISCONNECTED'] = 0xc0000037 +status_codes['NT_STATUS_DEVICE_ALREADY_ATTACHED'] = 0xc0000038 +status_codes['NT_STATUS_OBJECT_PATH_INVALID'] = 0xc0000039 +status_codes['NT_STATUS_OBJECT_PATH_NOT_FOUND'] = 0xc000003a +status_codes['NT_STATUS_OBJECT_PATH_SYNTAX_BAD'] = 0xc000003b +status_codes['NT_STATUS_DATA_OVERRUN'] = 0xc000003c +status_codes['NT_STATUS_DATA_LATE_ERROR'] = 0xc000003d +status_codes['NT_STATUS_DATA_ERROR'] = 0xc000003e +status_codes['NT_STATUS_CRC_ERROR'] = 0xc000003f +status_codes['NT_STATUS_SECTION_TOO_BIG'] = 0xc0000040 +status_codes['NT_STATUS_PORT_CONNECTION_REFUSED'] = 0xc0000041 +status_codes['NT_STATUS_INVALID_PORT_HANDLE'] = 0xc0000042 +status_codes['NT_STATUS_SHARING_VIOLATION'] = 0xc0000043 +status_codes['NT_STATUS_QUOTA_EXCEEDED'] = 0xc0000044 +status_codes['NT_STATUS_INVALID_PAGE_PROTECTION'] = 0xc0000045 +status_codes['NT_STATUS_MUTANT_NOT_OWNED'] = 0xc0000046 +status_codes['NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED'] = 0xc0000047 +status_codes['NT_STATUS_PORT_ALREADY_SET'] = 0xc0000048 +status_codes['NT_STATUS_SECTION_NOT_IMAGE'] = 0xc0000049 +status_codes['NT_STATUS_SUSPEND_COUNT_EXCEEDED'] = 0xc000004a +status_codes['NT_STATUS_THREAD_IS_TERMINATING'] = 0xc000004b +status_codes['NT_STATUS_BAD_WORKING_SET_LIMIT'] = 0xc000004c +status_codes['NT_STATUS_INCOMPATIBLE_FILE_MAP'] = 0xc000004d +status_codes['NT_STATUS_SECTION_PROTECTION'] = 0xc000004e +status_codes['NT_STATUS_EAS_NOT_SUPPORTED'] = 0xc000004f +status_codes['NT_STATUS_EA_TOO_LARGE'] = 0xc0000050 +status_codes['NT_STATUS_NONEXISTENT_EA_ENTRY'] = 0xc0000051 +status_codes['NT_STATUS_NO_EAS_ON_FILE'] = 0xc0000052 +status_codes['NT_STATUS_EA_CORRUPT_ERROR'] = 0xc0000053 +status_codes['NT_STATUS_FILE_LOCK_CONFLICT'] = 0xc0000054 +status_codes['NT_STATUS_LOCK_NOT_GRANTED'] = 0xc0000055 +status_codes['NT_STATUS_DELETE_PENDING'] = 0xc0000056 +status_codes['NT_STATUS_CTL_FILE_NOT_SUPPORTED'] = 0xc0000057 +status_codes['NT_STATUS_UNKNOWN_REVISION'] = 0xc0000058 +status_codes['NT_STATUS_REVISION_MISMATCH'] = 0xc0000059 +status_codes['NT_STATUS_INVALID_OWNER'] = 0xc000005a +status_codes['NT_STATUS_INVALID_PRIMARY_GROUP'] = 0xc000005b +status_codes['NT_STATUS_NO_IMPERSONATION_TOKEN'] = 0xc000005c +status_codes['NT_STATUS_CANT_DISABLE_MANDATORY'] = 0xc000005d +status_codes['NT_STATUS_NO_LOGON_SERVERS'] = 0xc000005e +status_codes['NT_STATUS_NO_SUCH_LOGON_SESSION'] = 0xc000005f +status_codes['NT_STATUS_NO_SUCH_PRIVILEGE'] = 0xc0000060 +status_codes['NT_STATUS_PRIVILEGE_NOT_HELD'] = 0xc0000061 +status_codes['NT_STATUS_INVALID_ACCOUNT_NAME'] = 0xc0000062 +status_codes['NT_STATUS_USER_EXISTS'] = 0xc0000063 +status_codes['NT_STATUS_NO_SUCH_USER'] = 0xc0000064 +status_codes['NT_STATUS_GROUP_EXISTS'] = 0xc0000065 +status_codes['NT_STATUS_NO_SUCH_GROUP'] = 0xc0000066 +status_codes['NT_STATUS_MEMBER_IN_GROUP'] = 0xc0000067 +status_codes['NT_STATUS_MEMBER_NOT_IN_GROUP'] = 0xc0000068 +status_codes['NT_STATUS_LAST_ADMIN'] = 0xc0000069 +status_codes['NT_STATUS_WRONG_PASSWORD'] = 0xc000006a +status_codes['NT_STATUS_ILL_FORMED_PASSWORD'] = 0xc000006b +status_codes['NT_STATUS_PASSWORD_RESTRICTION'] = 0xc000006c +status_codes['NT_STATUS_LOGON_FAILURE'] = 0xc000006d +status_codes['NT_STATUS_ACCOUNT_RESTRICTION'] = 0xc000006e +status_codes['NT_STATUS_INVALID_LOGON_HOURS'] = 0xc000006f +status_codes['NT_STATUS_INVALID_WORKSTATION'] = 0xc0000070 +status_codes['NT_STATUS_PASSWORD_EXPIRED'] = 0xc0000071 +status_codes['NT_STATUS_ACCOUNT_DISABLED'] = 0xc0000072 +status_codes['NT_STATUS_NONE_MAPPED'] = 0xc0000073 +status_codes['NT_STATUS_TOO_MANY_LUIDS_REQUESTED'] = 0xc0000074 +status_codes['NT_STATUS_LUIDS_EXHAUSTED'] = 0xc0000075 +status_codes['NT_STATUS_INVALID_SUB_AUTHORITY'] = 0xc0000076 +status_codes['NT_STATUS_INVALID_ACL'] = 0xc0000077 +status_codes['NT_STATUS_INVALID_SID'] = 0xc0000078 +status_codes['NT_STATUS_INVALID_SECURITY_DESCR'] = 0xc0000079 +status_codes['NT_STATUS_PROCEDURE_NOT_FOUND'] = 0xc000007a +status_codes['NT_STATUS_INVALID_IMAGE_FORMAT'] = 0xc000007b +status_codes['NT_STATUS_NO_TOKEN'] = 0xc000007c +status_codes['NT_STATUS_BAD_INHERITANCE_ACL'] = 0xc000007d +status_codes['NT_STATUS_RANGE_NOT_LOCKED'] = 0xc000007e +status_codes['NT_STATUS_DISK_FULL'] = 0xc000007f +status_codes['NT_STATUS_SERVER_DISABLED'] = 0xc0000080 +status_codes['NT_STATUS_SERVER_NOT_DISABLED'] = 0xc0000081 +status_codes['NT_STATUS_TOO_MANY_GUIDS_REQUESTED'] = 0xc0000082 +status_codes['NT_STATUS_GUIDS_EXHAUSTED'] = 0xc0000083 +status_codes['NT_STATUS_INVALID_ID_AUTHORITY'] = 0xc0000084 +status_codes['NT_STATUS_AGENTS_EXHAUSTED'] = 0xc0000085 +status_codes['NT_STATUS_INVALID_VOLUME_LABEL'] = 0xc0000086 +status_codes['NT_STATUS_SECTION_NOT_EXTENDED'] = 0xc0000087 +status_codes['NT_STATUS_NOT_MAPPED_DATA'] = 0xc0000088 +status_codes['NT_STATUS_RESOURCE_DATA_NOT_FOUND'] = 0xc0000089 +status_codes['NT_STATUS_RESOURCE_TYPE_NOT_FOUND'] = 0xc000008a +status_codes['NT_STATUS_RESOURCE_NAME_NOT_FOUND'] = 0xc000008b +status_codes['NT_STATUS_ARRAY_BOUNDS_EXCEEDED'] = 0xc000008c +status_codes['NT_STATUS_FLOAT_DENORMAL_OPERAND'] = 0xc000008d +status_codes['NT_STATUS_FLOAT_DIVIDE_BY_ZERO'] = 0xc000008e +status_codes['NT_STATUS_FLOAT_INEXACT_RESULT'] = 0xc000008f +status_codes['NT_STATUS_FLOAT_INVALID_OPERATION'] = 0xc0000090 +status_codes['NT_STATUS_FLOAT_OVERFLOW'] = 0xc0000091 +status_codes['NT_STATUS_FLOAT_STACK_CHECK'] = 0xc0000092 +status_codes['NT_STATUS_FLOAT_UNDERFLOW'] = 0xc0000093 +status_codes['NT_STATUS_INTEGER_DIVIDE_BY_ZERO'] = 0xc0000094 +status_codes['NT_STATUS_INTEGER_OVERFLOW'] = 0xc0000095 +status_codes['NT_STATUS_PRIVILEGED_INSTRUCTION'] = 0xc0000096 +status_codes['NT_STATUS_TOO_MANY_PAGING_FILES'] = 0xc0000097 +status_codes['NT_STATUS_FILE_INVALID'] = 0xc0000098 +status_codes['NT_STATUS_ALLOTTED_SPACE_EXCEEDED'] = 0xc0000099 +status_codes['NT_STATUS_INSUFFICIENT_RESOURCES'] = 0xc000009a +status_codes['NT_STATUS_DFS_EXIT_PATH_FOUND'] = 0xc000009b +status_codes['NT_STATUS_DEVICE_DATA_ERROR'] = 0xc000009c +status_codes['NT_STATUS_DEVICE_NOT_CONNECTED'] = 0xc000009d +status_codes['NT_STATUS_DEVICE_POWER_FAILURE'] = 0xc000009e +status_codes['NT_STATUS_FREE_VM_NOT_AT_BASE'] = 0xc000009f +status_codes['NT_STATUS_MEMORY_NOT_ALLOCATED'] = 0xc00000a0 +status_codes['NT_STATUS_WORKING_SET_QUOTA'] = 0xc00000a1 +status_codes['NT_STATUS_MEDIA_WRITE_PROTECTED'] = 0xc00000a2 +status_codes['NT_STATUS_DEVICE_NOT_READY'] = 0xc00000a3 +status_codes['NT_STATUS_INVALID_GROUP_ATTRIBUTES'] = 0xc00000a4 +status_codes['NT_STATUS_BAD_IMPERSONATION_LEVEL'] = 0xc00000a5 +status_codes['NT_STATUS_CANT_OPEN_ANONYMOUS'] = 0xc00000a6 +status_codes['NT_STATUS_BAD_VALIDATION_CLASS'] = 0xc00000a7 +status_codes['NT_STATUS_BAD_TOKEN_TYPE'] = 0xc00000a8 +status_codes['NT_STATUS_BAD_MASTER_BOOT_RECORD'] = 0xc00000a9 +status_codes['NT_STATUS_INSTRUCTION_MISALIGNMENT'] = 0xc00000aa +status_codes['NT_STATUS_INSTANCE_NOT_AVAILABLE'] = 0xc00000ab +status_codes['NT_STATUS_PIPE_NOT_AVAILABLE'] = 0xc00000ac +status_codes['NT_STATUS_INVALID_PIPE_STATE'] = 0xc00000ad +status_codes['NT_STATUS_PIPE_BUSY'] = 0xc00000ae +status_codes['NT_STATUS_ILLEGAL_FUNCTION'] = 0xc00000af +status_codes['NT_STATUS_PIPE_DISCONNECTED'] = 0xc00000b0 +status_codes['NT_STATUS_PIPE_CLOSING'] = 0xc00000b1 +status_codes['NT_STATUS_PIPE_CONNECTED'] = 0xc00000b2 +status_codes['NT_STATUS_PIPE_LISTENING'] = 0xc00000b3 +status_codes['NT_STATUS_INVALID_READ_MODE'] = 0xc00000b4 +status_codes['NT_STATUS_IO_TIMEOUT'] = 0xc00000b5 +status_codes['NT_STATUS_FILE_FORCED_CLOSED'] = 0xc00000b6 +status_codes['NT_STATUS_PROFILING_NOT_STARTED'] = 0xc00000b7 +status_codes['NT_STATUS_PROFILING_NOT_STOPPED'] = 0xc00000b8 +status_codes['NT_STATUS_COULD_NOT_INTERPRET'] = 0xc00000b9 +status_codes['NT_STATUS_FILE_IS_A_DIRECTORY'] = 0xc00000ba +status_codes['NT_STATUS_NOT_SUPPORTED'] = 0xc00000bb +status_codes['NT_STATUS_REMOTE_NOT_LISTENING'] = 0xc00000bc +status_codes['NT_STATUS_DUPLICATE_NAME'] = 0xc00000bd +status_codes['NT_STATUS_BAD_NETWORK_PATH'] = 0xc00000be +status_codes['NT_STATUS_NETWORK_BUSY'] = 0xc00000bf +status_codes['NT_STATUS_DEVICE_DOES_NOT_EXIST'] = 0xc00000c0 +status_codes['NT_STATUS_TOO_MANY_COMMANDS'] = 0xc00000c1 +status_codes['NT_STATUS_ADAPTER_HARDWARE_ERROR'] = 0xc00000c2 +status_codes['NT_STATUS_INVALID_NETWORK_RESPONSE'] = 0xc00000c3 +status_codes['NT_STATUS_UNEXPECTED_NETWORK_ERROR'] = 0xc00000c4 +status_codes['NT_STATUS_BAD_REMOTE_ADAPTER'] = 0xc00000c5 +status_codes['NT_STATUS_PRINT_QUEUE_FULL'] = 0xc00000c6 +status_codes['NT_STATUS_NO_SPOOL_SPACE'] = 0xc00000c7 +status_codes['NT_STATUS_PRINT_CANCELLED'] = 0xc00000c8 +status_codes['NT_STATUS_NETWORK_NAME_DELETED'] = 0xc00000c9 +status_codes['NT_STATUS_NETWORK_ACCESS_DENIED'] = 0xc00000ca +status_codes['NT_STATUS_BAD_DEVICE_TYPE'] = 0xc00000cb +status_codes['NT_STATUS_BAD_NETWORK_NAME'] = 0xc00000cc +status_codes['NT_STATUS_TOO_MANY_NAMES'] = 0xc00000cd +status_codes['NT_STATUS_TOO_MANY_SESSIONS'] = 0xc00000ce +status_codes['NT_STATUS_SHARING_PAUSED'] = 0xc00000cf +status_codes['NT_STATUS_REQUEST_NOT_ACCEPTED'] = 0xc00000d0 +status_codes['NT_STATUS_REDIRECTOR_PAUSED'] = 0xc00000d1 +status_codes['NT_STATUS_NET_WRITE_FAULT'] = 0xc00000d2 +status_codes['NT_STATUS_PROFILING_AT_LIMIT'] = 0xc00000d3 +status_codes['NT_STATUS_NOT_SAME_DEVICE'] = 0xc00000d4 +status_codes['NT_STATUS_FILE_RENAMED'] = 0xc00000d5 +status_codes['NT_STATUS_VIRTUAL_CIRCUIT_CLOSED'] = 0xc00000d6 +status_codes['NT_STATUS_NO_SECURITY_ON_OBJECT'] = 0xc00000d7 +status_codes['NT_STATUS_CANT_WAIT'] = 0xc00000d8 +status_codes['NT_STATUS_PIPE_EMPTY'] = 0xc00000d9 +status_codes['NT_STATUS_CANT_ACCESS_DOMAIN_INFO'] = 0xc00000da +status_codes['NT_STATUS_CANT_TERMINATE_SELF'] = 0xc00000db +status_codes['NT_STATUS_INVALID_SERVER_STATE'] = 0xc00000dc +status_codes['NT_STATUS_INVALID_DOMAIN_STATE'] = 0xc00000dd +status_codes['NT_STATUS_INVALID_DOMAIN_ROLE'] = 0xc00000de +status_codes['NT_STATUS_NO_SUCH_DOMAIN'] = 0xc00000df +status_codes['NT_STATUS_DOMAIN_EXISTS'] = 0xc00000e0 +status_codes['NT_STATUS_DOMAIN_LIMIT_EXCEEDED'] = 0xc00000e1 +status_codes['NT_STATUS_OPLOCK_NOT_GRANTED'] = 0xc00000e2 +status_codes['NT_STATUS_INVALID_OPLOCK_PROTOCOL'] = 0xc00000e3 +status_codes['NT_STATUS_INTERNAL_DB_CORRUPTION'] = 0xc00000e4 +status_codes['NT_STATUS_INTERNAL_ERROR'] = 0xc00000e5 +status_codes['NT_STATUS_GENERIC_NOT_MAPPED'] = 0xc00000e6 +status_codes['NT_STATUS_BAD_DESCRIPTOR_FORMAT'] = 0xc00000e7 +status_codes['NT_STATUS_INVALID_USER_BUFFER'] = 0xc00000e8 +status_codes['NT_STATUS_UNEXPECTED_IO_ERROR'] = 0xc00000e9 +status_codes['NT_STATUS_UNEXPECTED_MM_CREATE_ERR'] = 0xc00000ea +status_codes['NT_STATUS_UNEXPECTED_MM_MAP_ERROR'] = 0xc00000eb +status_codes['NT_STATUS_UNEXPECTED_MM_EXTEND_ERR'] = 0xc00000ec +status_codes['NT_STATUS_NOT_LOGON_PROCESS'] = 0xc00000ed +status_codes['NT_STATUS_LOGON_SESSION_EXISTS'] = 0xc00000ee +status_codes['NT_STATUS_INVALID_PARAMETER_1'] = 0xc00000ef +status_codes['NT_STATUS_INVALID_PARAMETER_2'] = 0xc00000f0 +status_codes['NT_STATUS_INVALID_PARAMETER_3'] = 0xc00000f1 +status_codes['NT_STATUS_INVALID_PARAMETER_4'] = 0xc00000f2 +status_codes['NT_STATUS_INVALID_PARAMETER_5'] = 0xc00000f3 +status_codes['NT_STATUS_INVALID_PARAMETER_6'] = 0xc00000f4 +status_codes['NT_STATUS_INVALID_PARAMETER_7'] = 0xc00000f5 +status_codes['NT_STATUS_INVALID_PARAMETER_8'] = 0xc00000f6 +status_codes['NT_STATUS_INVALID_PARAMETER_9'] = 0xc00000f7 +status_codes['NT_STATUS_INVALID_PARAMETER_10'] = 0xc00000f8 +status_codes['NT_STATUS_INVALID_PARAMETER_11'] = 0xc00000f9 +status_codes['NT_STATUS_INVALID_PARAMETER_12'] = 0xc00000fa +status_codes['NT_STATUS_REDIRECTOR_NOT_STARTED'] = 0xc00000fb +status_codes['NT_STATUS_REDIRECTOR_STARTED'] = 0xc00000fc +status_codes['NT_STATUS_STACK_OVERFLOW'] = 0xc00000fd +status_codes['NT_STATUS_NO_SUCH_PACKAGE'] = 0xc00000fe +status_codes['NT_STATUS_BAD_FUNCTION_TABLE'] = 0xc00000ff +status_codes['NT_STATUS_DIRECTORY_NOT_EMPTY'] = 0xc0000101 +status_codes['NT_STATUS_FILE_CORRUPT_ERROR'] = 0xc0000102 +status_codes['NT_STATUS_NOT_A_DIRECTORY'] = 0xc0000103 +status_codes['NT_STATUS_BAD_LOGON_SESSION_STATE'] = 0xc0000104 +status_codes['NT_STATUS_LOGON_SESSION_COLLISION'] = 0xc0000105 +status_codes['NT_STATUS_NAME_TOO_LONG'] = 0xc0000106 +status_codes['NT_STATUS_FILES_OPEN'] = 0xc0000107 +status_codes['NT_STATUS_CONNECTION_IN_USE'] = 0xc0000108 +status_codes['NT_STATUS_MESSAGE_NOT_FOUND'] = 0xc0000109 +status_codes['NT_STATUS_PROCESS_IS_TERMINATING'] = 0xc000010a +status_codes['NT_STATUS_INVALID_LOGON_TYPE'] = 0xc000010b +status_codes['NT_STATUS_NO_GUID_TRANSLATION'] = 0xc000010c +status_codes['NT_STATUS_CANNOT_IMPERSONATE'] = 0xc000010d +status_codes['NT_STATUS_IMAGE_ALREADY_LOADED'] = 0xc000010e +status_codes['NT_STATUS_ABIOS_NOT_PRESENT'] = 0xc000010f +status_codes['NT_STATUS_ABIOS_LID_NOT_EXIST'] = 0xc0000110 +status_codes['NT_STATUS_ABIOS_LID_ALREADY_OWNED'] = 0xc0000111 +status_codes['NT_STATUS_ABIOS_NOT_LID_OWNER'] = 0xc0000112 +status_codes['NT_STATUS_ABIOS_INVALID_COMMAND'] = 0xc0000113 +status_codes['NT_STATUS_ABIOS_INVALID_LID'] = 0xc0000114 +status_codes['NT_STATUS_ABIOS_SELECTOR_NOT_AVAILABLE'] = 0xc0000115 +status_codes['NT_STATUS_ABIOS_INVALID_SELECTOR'] = 0xc0000116 +status_codes['NT_STATUS_NO_LDT'] = 0xc0000117 +status_codes['NT_STATUS_INVALID_LDT_SIZE'] = 0xc0000118 +status_codes['NT_STATUS_INVALID_LDT_OFFSET'] = 0xc0000119 +status_codes['NT_STATUS_INVALID_LDT_DESCRIPTOR'] = 0xc000011a +status_codes['NT_STATUS_INVALID_IMAGE_NE_FORMAT'] = 0xc000011b +status_codes['NT_STATUS_RXACT_INVALID_STATE'] = 0xc000011c +status_codes['NT_STATUS_RXACT_COMMIT_FAILURE'] = 0xc000011d +status_codes['NT_STATUS_MAPPED_FILE_SIZE_ZERO'] = 0xc000011e +status_codes['NT_STATUS_TOO_MANY_OPENED_FILES'] = 0xc000011f +status_codes['NT_STATUS_CANCELLED'] = 0xc0000120 +status_codes['NT_STATUS_CANNOT_DELETE'] = 0xc0000121 +status_codes['NT_STATUS_INVALID_COMPUTER_NAME'] = 0xc0000122 +status_codes['NT_STATUS_FILE_DELETED'] = 0xc0000123 +status_codes['NT_STATUS_SPECIAL_ACCOUNT'] = 0xc0000124 +status_codes['NT_STATUS_SPECIAL_GROUP'] = 0xc0000125 +status_codes['NT_STATUS_SPECIAL_USER'] = 0xc0000126 +status_codes['NT_STATUS_MEMBERS_PRIMARY_GROUP'] = 0xc0000127 +status_codes['NT_STATUS_FILE_CLOSED'] = 0xc0000128 +status_codes['NT_STATUS_TOO_MANY_THREADS'] = 0xc0000129 +status_codes['NT_STATUS_THREAD_NOT_IN_PROCESS'] = 0xc000012a +status_codes['NT_STATUS_TOKEN_ALREADY_IN_USE'] = 0xc000012b +status_codes['NT_STATUS_PAGEFILE_QUOTA_EXCEEDED'] = 0xc000012c +status_codes['NT_STATUS_COMMITMENT_LIMIT'] = 0xc000012d +status_codes['NT_STATUS_INVALID_IMAGE_LE_FORMAT'] = 0xc000012e +status_codes['NT_STATUS_INVALID_IMAGE_NOT_MZ'] = 0xc000012f +status_codes['NT_STATUS_INVALID_IMAGE_PROTECT'] = 0xc0000130 +status_codes['NT_STATUS_INVALID_IMAGE_WIN_16'] = 0xc0000131 +status_codes['NT_STATUS_LOGON_SERVER_CONFLICT'] = 0xc0000132 +status_codes['NT_STATUS_TIME_DIFFERENCE_AT_DC'] = 0xc0000133 +status_codes['NT_STATUS_SYNCHRONIZATION_REQUIRED'] = 0xc0000134 +status_codes['NT_STATUS_DLL_NOT_FOUND'] = 0xc0000135 +status_codes['NT_STATUS_OPEN_FAILED'] = 0xc0000136 +status_codes['NT_STATUS_IO_PRIVILEGE_FAILED'] = 0xc0000137 +status_codes['NT_STATUS_ORDINAL_NOT_FOUND'] = 0xc0000138 +status_codes['NT_STATUS_ENTRYPOINT_NOT_FOUND'] = 0xc0000139 +status_codes['NT_STATUS_CONTROL_C_EXIT'] = 0xc000013a +status_codes['NT_STATUS_LOCAL_DISCONNECT'] = 0xc000013b +status_codes['NT_STATUS_REMOTE_DISCONNECT'] = 0xc000013c +status_codes['NT_STATUS_REMOTE_RESOURCES'] = 0xc000013d +status_codes['NT_STATUS_LINK_FAILED'] = 0xc000013e +status_codes['NT_STATUS_LINK_TIMEOUT'] = 0xc000013f +status_codes['NT_STATUS_INVALID_CONNECTION'] = 0xc0000140 +status_codes['NT_STATUS_INVALID_ADDRESS'] = 0xc0000141 +status_codes['NT_STATUS_DLL_INIT_FAILED'] = 0xc0000142 +status_codes['NT_STATUS_MISSING_SYSTEMFILE'] = 0xc0000143 +status_codes['NT_STATUS_UNHANDLED_EXCEPTION'] = 0xc0000144 +status_codes['NT_STATUS_APP_INIT_FAILURE'] = 0xc0000145 +status_codes['NT_STATUS_PAGEFILE_CREATE_FAILED'] = 0xc0000146 +status_codes['NT_STATUS_NO_PAGEFILE'] = 0xc0000147 +status_codes['NT_STATUS_INVALID_LEVEL'] = 0xc0000148 +status_codes['NT_STATUS_WRONG_PASSWORD_CORE'] = 0xc0000149 +status_codes['NT_STATUS_ILLEGAL_FLOAT_CONTEXT'] = 0xc000014a +status_codes['NT_STATUS_PIPE_BROKEN'] = 0xc000014b +status_codes['NT_STATUS_REGISTRY_CORRUPT'] = 0xc000014c +status_codes['NT_STATUS_REGISTRY_IO_FAILED'] = 0xc000014d +status_codes['NT_STATUS_NO_EVENT_PAIR'] = 0xc000014e +status_codes['NT_STATUS_UNRECOGNIZED_VOLUME'] = 0xc000014f +status_codes['NT_STATUS_SERIAL_NO_DEVICE_INITED'] = 0xc0000150 +status_codes['NT_STATUS_NO_SUCH_ALIAS'] = 0xc0000151 +status_codes['NT_STATUS_MEMBER_NOT_IN_ALIAS'] = 0xc0000152 +status_codes['NT_STATUS_MEMBER_IN_ALIAS'] = 0xc0000153 +status_codes['NT_STATUS_ALIAS_EXISTS'] = 0xc0000154 +status_codes['NT_STATUS_LOGON_NOT_GRANTED'] = 0xc0000155 +status_codes['NT_STATUS_TOO_MANY_SECRETS'] = 0xc0000156 +status_codes['NT_STATUS_SECRET_TOO_LONG'] = 0xc0000157 +status_codes['NT_STATUS_INTERNAL_DB_ERROR'] = 0xc0000158 +status_codes['NT_STATUS_FULLSCREEN_MODE'] = 0xc0000159 +status_codes['NT_STATUS_TOO_MANY_CONTEXT_IDS'] = 0xc000015a +status_codes['NT_STATUS_LOGON_TYPE_NOT_GRANTED'] = 0xc000015b +status_codes['NT_STATUS_NOT_REGISTRY_FILE'] = 0xc000015c +status_codes['NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED'] = 0xc000015d +status_codes['NT_STATUS_DOMAIN_CTRLR_CONFIG_ERROR'] = 0xc000015e +status_codes['NT_STATUS_FT_MISSING_MEMBER'] = 0xc000015f +status_codes['NT_STATUS_ILL_FORMED_SERVICE_ENTRY'] = 0xc0000160 +status_codes['NT_STATUS_ILLEGAL_CHARACTER'] = 0xc0000161 +status_codes['NT_STATUS_UNMAPPABLE_CHARACTER'] = 0xc0000162 +status_codes['NT_STATUS_UNDEFINED_CHARACTER'] = 0xc0000163 +status_codes['NT_STATUS_FLOPPY_VOLUME'] = 0xc0000164 +status_codes['NT_STATUS_FLOPPY_ID_MARK_NOT_FOUND'] = 0xc0000165 +status_codes['NT_STATUS_FLOPPY_WRONG_CYLINDER'] = 0xc0000166 +status_codes['NT_STATUS_FLOPPY_UNKNOWN_ERROR'] = 0xc0000167 +status_codes['NT_STATUS_FLOPPY_BAD_REGISTERS'] = 0xc0000168 +status_codes['NT_STATUS_DISK_RECALIBRATE_FAILED'] = 0xc0000169 +status_codes['NT_STATUS_DISK_OPERATION_FAILED'] = 0xc000016a +status_codes['NT_STATUS_DISK_RESET_FAILED'] = 0xc000016b +status_codes['NT_STATUS_SHARED_IRQ_BUSY'] = 0xc000016c +status_codes['NT_STATUS_FT_ORPHANING'] = 0xc000016d +status_codes['NT_STATUS_PARTITION_FAILURE'] = 0xc0000172 +status_codes['NT_STATUS_INVALID_BLOCK_LENGTH'] = 0xc0000173 +status_codes['NT_STATUS_DEVICE_NOT_PARTITIONED'] = 0xc0000174 +status_codes['NT_STATUS_UNABLE_TO_LOCK_MEDIA'] = 0xc0000175 +status_codes['NT_STATUS_UNABLE_TO_UNLOAD_MEDIA'] = 0xc0000176 +status_codes['NT_STATUS_EOM_OVERFLOW'] = 0xc0000177 +status_codes['NT_STATUS_NO_MEDIA'] = 0xc0000178 +status_codes['NT_STATUS_NO_SUCH_MEMBER'] = 0xc000017a +status_codes['NT_STATUS_INVALID_MEMBER'] = 0xc000017b +status_codes['NT_STATUS_KEY_DELETED'] = 0xc000017c +status_codes['NT_STATUS_NO_LOG_SPACE'] = 0xc000017d +status_codes['NT_STATUS_TOO_MANY_SIDS'] = 0xc000017e +status_codes['NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED'] = 0xc000017f +status_codes['NT_STATUS_KEY_HAS_CHILDREN'] = 0xc0000180 +status_codes['NT_STATUS_CHILD_MUST_BE_VOLATILE'] = 0xc0000181 +status_codes['NT_STATUS_DEVICE_CONFIGURATION_ERROR'] = 0xc0000182 +status_codes['NT_STATUS_DRIVER_INTERNAL_ERROR'] = 0xc0000183 +status_codes['NT_STATUS_INVALID_DEVICE_STATE'] = 0xc0000184 +status_codes['NT_STATUS_IO_DEVICE_ERROR'] = 0xc0000185 +status_codes['NT_STATUS_DEVICE_PROTOCOL_ERROR'] = 0xc0000186 +status_codes['NT_STATUS_BACKUP_CONTROLLER'] = 0xc0000187 +status_codes['NT_STATUS_LOG_FILE_FULL'] = 0xc0000188 +status_codes['NT_STATUS_TOO_LATE'] = 0xc0000189 +status_codes['NT_STATUS_NO_TRUST_LSA_SECRET'] = 0xc000018a +status_codes['NT_STATUS_NO_TRUST_SAM_ACCOUNT'] = 0xc000018b +status_codes['NT_STATUS_TRUSTED_DOMAIN_FAILURE'] = 0xc000018c +status_codes['NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE'] = 0xc000018d +status_codes['NT_STATUS_EVENTLOG_FILE_CORRUPT'] = 0xc000018e +status_codes['NT_STATUS_EVENTLOG_CANT_START'] = 0xc000018f +status_codes['NT_STATUS_TRUST_FAILURE'] = 0xc0000190 +status_codes['NT_STATUS_MUTANT_LIMIT_EXCEEDED'] = 0xc0000191 +status_codes['NT_STATUS_NETLOGON_NOT_STARTED'] = 0xc0000192 +status_codes['NT_STATUS_ACCOUNT_EXPIRED'] = 0xc0000193 +status_codes['NT_STATUS_POSSIBLE_DEADLOCK'] = 0xc0000194 +status_codes['NT_STATUS_NETWORK_CREDENTIAL_CONFLICT'] = 0xc0000195 +status_codes['NT_STATUS_REMOTE_SESSION_LIMIT'] = 0xc0000196 +status_codes['NT_STATUS_EVENTLOG_FILE_CHANGED'] = 0xc0000197 +status_codes['NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT'] = 0xc0000198 +status_codes['NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT'] = 0xc0000199 +status_codes['NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT'] = 0xc000019a +status_codes['NT_STATUS_DOMAIN_TRUST_INCONSISTENT'] = 0xc000019b +status_codes['NT_STATUS_FS_DRIVER_REQUIRED'] = 0xc000019c +status_codes['NT_STATUS_NO_USER_SESSION_KEY'] = 0xc0000202 +status_codes['NT_STATUS_USER_SESSION_DELETED'] = 0xc0000203 +status_codes['NT_STATUS_RESOURCE_LANG_NOT_FOUND'] = 0xc0000204 +status_codes['NT_STATUS_INSUFF_SERVER_RESOURCES'] = 0xc0000205 +status_codes['NT_STATUS_INVALID_BUFFER_SIZE'] = 0xc0000206 +status_codes['NT_STATUS_INVALID_ADDRESS_COMPONENT'] = 0xc0000207 +status_codes['NT_STATUS_INVALID_ADDRESS_WILDCARD'] = 0xc0000208 +status_codes['NT_STATUS_TOO_MANY_ADDRESSES'] = 0xc0000209 +status_codes['NT_STATUS_ADDRESS_ALREADY_EXISTS'] = 0xc000020a +status_codes['NT_STATUS_ADDRESS_CLOSED'] = 0xc000020b +status_codes['NT_STATUS_CONNECTION_DISCONNECTED'] = 0xc000020c +status_codes['NT_STATUS_CONNECTION_RESET'] = 0xc000020d +status_codes['NT_STATUS_TOO_MANY_NODES'] = 0xc000020e +status_codes['NT_STATUS_TRANSACTION_ABORTED'] = 0xc000020f +status_codes['NT_STATUS_TRANSACTION_TIMED_OUT'] = 0xc0000210 +status_codes['NT_STATUS_TRANSACTION_NO_RELEASE'] = 0xc0000211 +status_codes['NT_STATUS_TRANSACTION_NO_MATCH'] = 0xc0000212 +status_codes['NT_STATUS_TRANSACTION_RESPONDED'] = 0xc0000213 +status_codes['NT_STATUS_TRANSACTION_INVALID_ID'] = 0xc0000214 +status_codes['NT_STATUS_TRANSACTION_INVALID_TYPE'] = 0xc0000215 +status_codes['NT_STATUS_NOT_SERVER_SESSION'] = 0xc0000216 +status_codes['NT_STATUS_NOT_CLIENT_SESSION'] = 0xc0000217 +status_codes['NT_STATUS_CANNOT_LOAD_REGISTRY_FILE'] = 0xc0000218 +status_codes['NT_STATUS_DEBUG_ATTACH_FAILED'] = 0xc0000219 +status_codes['NT_STATUS_SYSTEM_PROCESS_TERMINATED'] = 0xc000021a +status_codes['NT_STATUS_DATA_NOT_ACCEPTED'] = 0xc000021b +status_codes['NT_STATUS_NO_BROWSER_SERVERS_FOUND'] = 0xc000021c +status_codes['NT_STATUS_VDM_HARD_ERROR'] = 0xc000021d +status_codes['NT_STATUS_DRIVER_CANCEL_TIMEOUT'] = 0xc000021e +status_codes['NT_STATUS_REPLY_MESSAGE_MISMATCH'] = 0xc000021f +status_codes['NT_STATUS_MAPPED_ALIGNMENT'] = 0xc0000220 +status_codes['NT_STATUS_IMAGE_CHECKSUM_MISMATCH'] = 0xc0000221 +status_codes['NT_STATUS_LOST_WRITEBEHIND_DATA'] = 0xc0000222 +status_codes['NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID'] = 0xc0000223 +status_codes['NT_STATUS_PASSWORD_MUST_CHANGE'] = 0xc0000224 +status_codes['NT_STATUS_NOT_FOUND'] = 0xc0000225 +status_codes['NT_STATUS_NOT_TINY_STREAM'] = 0xc0000226 +status_codes['NT_STATUS_RECOVERY_FAILURE'] = 0xc0000227 +status_codes['NT_STATUS_STACK_OVERFLOW_READ'] = 0xc0000228 +status_codes['NT_STATUS_FAIL_CHECK'] = 0xc0000229 +status_codes['NT_STATUS_DUPLICATE_OBJECTID'] = 0xc000022a +status_codes['NT_STATUS_OBJECTID_EXISTS'] = 0xc000022b +status_codes['NT_STATUS_CONVERT_TO_LARGE'] = 0xc000022c +status_codes['NT_STATUS_RETRY'] = 0xc000022d +status_codes['NT_STATUS_FOUND_OUT_OF_SCOPE'] = 0xc000022e +status_codes['NT_STATUS_ALLOCATE_BUCKET'] = 0xc000022f +status_codes['NT_STATUS_PROPSET_NOT_FOUND'] = 0xc0000230 +status_codes['NT_STATUS_MARSHALL_OVERFLOW'] = 0xc0000231 +status_codes['NT_STATUS_INVALID_VARIANT'] = 0xc0000232 +status_codes['NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND'] = 0xc0000233 +status_codes['NT_STATUS_ACCOUNT_LOCKED_OUT'] = 0xc0000234 +status_codes['NT_STATUS_HANDLE_NOT_CLOSABLE'] = 0xc0000235 +status_codes['NT_STATUS_CONNECTION_REFUSED'] = 0xc0000236 +status_codes['NT_STATUS_GRACEFUL_DISCONNECT'] = 0xc0000237 +status_codes['NT_STATUS_ADDRESS_ALREADY_ASSOCIATED'] = 0xc0000238 +status_codes['NT_STATUS_ADDRESS_NOT_ASSOCIATED'] = 0xc0000239 +status_codes['NT_STATUS_CONNECTION_INVALID'] = 0xc000023a +status_codes['NT_STATUS_CONNECTION_ACTIVE'] = 0xc000023b +status_codes['NT_STATUS_NETWORK_UNREACHABLE'] = 0xc000023c +status_codes['NT_STATUS_HOST_UNREACHABLE'] = 0xc000023d +status_codes['NT_STATUS_PROTOCOL_UNREACHABLE'] = 0xc000023e +status_codes['NT_STATUS_PORT_UNREACHABLE'] = 0xc000023f +status_codes['NT_STATUS_REQUEST_ABORTED'] = 0xc0000240 +status_codes['NT_STATUS_CONNECTION_ABORTED'] = 0xc0000241 +status_codes['NT_STATUS_BAD_COMPRESSION_BUFFER'] = 0xc0000242 +status_codes['NT_STATUS_USER_MAPPED_FILE'] = 0xc0000243 +status_codes['NT_STATUS_AUDIT_FAILED'] = 0xc0000244 +status_codes['NT_STATUS_TIMER_RESOLUTION_NOT_SET'] = 0xc0000245 +status_codes['NT_STATUS_CONNECTION_COUNT_LIMIT'] = 0xc0000246 +status_codes['NT_STATUS_LOGIN_TIME_RESTRICTION'] = 0xc0000247 +status_codes['NT_STATUS_LOGIN_WKSTA_RESTRICTION'] = 0xc0000248 +status_codes['NT_STATUS_IMAGE_MP_UP_MISMATCH'] = 0xc0000249 +status_codes['NT_STATUS_INSUFFICIENT_LOGON_INFO'] = 0xc0000250 +status_codes['NT_STATUS_BAD_DLL_ENTRYPOINT'] = 0xc0000251 +status_codes['NT_STATUS_BAD_SERVICE_ENTRYPOINT'] = 0xc0000252 +status_codes['NT_STATUS_LPC_REPLY_LOST'] = 0xc0000253 +status_codes['NT_STATUS_IP_ADDRESS_CONFLICT1'] = 0xc0000254 +status_codes['NT_STATUS_IP_ADDRESS_CONFLICT2'] = 0xc0000255 +status_codes['NT_STATUS_REGISTRY_QUOTA_LIMIT'] = 0xc0000256 +status_codes['NT_STATUS_PATH_NOT_COVERED'] = 0xc0000257 +status_codes['NT_STATUS_NO_CALLBACK_ACTIVE'] = 0xc0000258 +status_codes['NT_STATUS_LICENSE_QUOTA_EXCEEDED'] = 0xc0000259 +status_codes['NT_STATUS_PWD_TOO_SHORT'] = 0xc000025a +status_codes['NT_STATUS_PWD_TOO_RECENT'] = 0xc000025b +status_codes['NT_STATUS_PWD_HISTORY_CONFLICT'] = 0xc000025c +status_codes['NT_STATUS_PLUGPLAY_NO_DEVICE'] = 0xc000025e +status_codes['NT_STATUS_UNSUPPORTED_COMPRESSION'] = 0xc000025f +status_codes['NT_STATUS_INVALID_HW_PROFILE'] = 0xc0000260 +status_codes['NT_STATUS_INVALID_PLUGPLAY_DEVICE_PATH'] = 0xc0000261 +status_codes['NT_STATUS_DRIVER_ORDINAL_NOT_FOUND'] = 0xc0000262 +status_codes['NT_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND'] = 0xc0000263 +status_codes['NT_STATUS_RESOURCE_NOT_OWNED'] = 0xc0000264 +status_codes['NT_STATUS_TOO_MANY_LINKS'] = 0xc0000265 +status_codes['NT_STATUS_QUOTA_LIST_INCONSISTENT'] = 0xc0000266 +status_codes['NT_STATUS_FILE_IS_OFFLINE'] = 0xc0000267 +status_codes['NT_STATUS_DS_NO_MORE_RIDS'] = 0xc00002a8 +status_codes['NT_STATUS_NOT_A_REPARSE_POINT'] = 0xc0000275 +status_codes['NT_STATUS_NO_SUCH_JOB'] = 0xc000EDE +for i, v in pairs(status_codes) do + status_names[v] = i +end +local function get_status_name(status) + if(status_names[status] == nil) then + return string.format("NT_STATUS_UNKNOWN (0x%08x)", status) + else + return status_names[status] + end +end + + --- Determines whether or not SMB checks are possible on this host, and, if they are, -- which port is best to use. This is how it decides:\n @@ -100,44 +714,75 @@ end -- otherwise, socket is the error message. function start(host) local port = get_port(host) - local mutex = nmap.mutex(mutex_id) + local status, result if(port == nil) then return false, "Couldn't find a valid port to check" end + stdnse.print_debug(3, "SMB: Attempting to lock SMB mutex") mutex "lock" + stdnse.print_debug(3, "SMB: Mutex lock obtained") if(port == 445) then - return start_raw(host, port) + status, result = start_raw(host, port) + if(status == false) then + stdnse.print_debug(3, "SMB: Attempting to release SMB mutex (1)") + mutex "done" + stdnse.print_debug(3, "SMB: SMB mutex released (1)") + end + + return status, result elseif(port == 139) then - return start_netbios(host, port) + status, result = start_netbios(host, port) + if(status == false) then + stdnse.print_debug(3, "SMB: Attempting to release SMB mutex (2)") + mutex "done" + stdnse.print_debug(3, "SMB: SMB mutex released (2)") + end + + return status, result end + stdnse.print_debug(3, "SMB: Attempting to release SMB mutex (3)") + mutex "done" + stdnse.print_debug(3, "SMB: SMB mutex released (3)") + return false, "Couldn't find a valid port to check" end --- Kills the SMB connection, closes the socket, and releases the mutex. Because of the mutex -- being released, a script HAS to call stop() before it exits, no matter why it's exiting! -- +-- In addition to killing the connection, this function can log off the user and disconnect +-- a tree. To do so, the appropriate parameters are passed. For a logoff, the uid is required. +-- For a tree disconnect, both tid and uid are required. +-- --@param socket The socket associated with the connection. +--@param uid [optional] If given, will do a logoff before disconnecting. +--@param tid [optional] If given, will do a tree disconnect before disconnecting. --@return (status, result) If status is false, result is an error message. Otherwise, result -- is undefined. -function stop(socket) - local mutex = nmap.mutex(mutex_id) +function stop(socket, uid, tid) - -- It's possible that the mutex wouldn't be created if there was an error condition. Therefore, - -- I'm calling 'trylock' first to ensure we have a lock on it. I'm not sure if that's the best - -- way to do this, though... - mutex "trylock" + if(tid ~= nil and uid ~= nil) then + tree_disconnect(socket, uid, tid) + end + + if(uid ~= nil) then + logoff(socket, uid) + end + + stdnse.print_debug(3, "SMB: Attempting to release SMB mutex (4)") mutex "done" + stdnse.print_debug(3, "SMB: SMB mutex released (4)") stdnse.print_debug(2, "Closing SMB socket") if(socket ~= nil) then local status, err = socket:close() if(status == false) then - return false, err + return false, "Failed to close socket: " .. err end end @@ -159,7 +804,7 @@ function start_raw(host, port) status, err = socket:connect(host.ip, port, "tcp") if(status == false) then - return false, err + return false, "Failed to connect to host: " .. err end return true, socket @@ -264,7 +909,7 @@ function start_netbios(host, port, name) status, err = socket:connect(host.ip, port, "tcp") if(status == false) then socket:close() - return false, err + return false, "Failed to connect: " .. err end -- Send the session request @@ -272,7 +917,7 @@ function start_netbios(host, port, name) status, err = socket:send(session_request) if(status == false) then socket:close() - return false, err + return false, "Failed to send: " .. err end socket:set_timeout(1000) @@ -281,7 +926,7 @@ function start_netbios(host, port, name) status, result = socket:receive_bytes(4); if(status == false) then socket:close() - return false, result + return false, "Failed to close socket: " .. result end pos, result, flags, length = bin.unpack(">CCS", result) @@ -437,7 +1082,7 @@ function smb_read(socket) -- Make sure the connection is still alive if(status ~= true) then - return false, result + return false, "Failed to receive bytes: " .. result end -- The length of the packet is 4 bytes of big endian (for our purposes). @@ -453,10 +1098,10 @@ function smb_read(socket) pos, data = bin.unpack(string.format("\n +--\n -- @output --- (no verbose) --- |_ NBSTAT: NetBIOS name: TEST1, NetBIOS user: RON, NetBIOS MAC: 00:0c:29:f9:d9:28 --- --- (verbose) --- | NBSTAT: NetBIOS name: TEST1, NetBIOS user: RON, NetBIOS MAC: 00:0c:29:f9:d9:28 --- | Name: TEST1<00> Flags: --- | Name: TEST1<20> Flags: --- | Name: WORKGROUP<00> Flags: --- | Name: TEST1<03> Flags: --- | Name: WORKGROUP<1e> Flags: --- | Name: RON<03> Flags: --- | Name: WORKGROUP<1d> Flags: --- |_ Name: \x01\x02__MSBROWSE__\x02<01> Flags: +-- (no verbose)\n +-- |_ NBSTAT: NetBIOS name: TEST1, NetBIOS user: RON, NetBIOS MAC: 00:0c:29:f9:d9:28\n +--\n +-- (verbose)\n +-- | NBSTAT: NetBIOS name: TEST1, NetBIOS user: RON, NetBIOS MAC: 00:0c:29:f9:d9:28\n +-- | Name: TEST1<00> Flags: \n +-- | Name: TEST1<20> Flags: \n +-- | Name: WORKGROUP<00> Flags: \n +-- | Name: TEST1<03> Flags: \n +-- | Name: WORKGROUP<1e> Flags: \n +-- | Name: RON<03> Flags: \n +-- | Name: WORKGROUP<1d> Flags: \n +-- |_ Name: \x01\x02__MSBROWSE__\x02<01> Flags: \n id = "NBSTAT" description = "Sends a NetBIOS query to target host to try to determine \ diff --git a/scripts/script.db b/scripts/script.db index 1670dcddc..3c8c69715 100644 --- a/scripts/script.db +++ b/scripts/script.db @@ -63,6 +63,8 @@ Entry{ category = "safe", filename = "SMTPcommands.nse" } Entry{ category = "default", filename = "anonFTP.nse" } Entry{ category = "auth", filename = "anonFTP.nse" } Entry{ category = "intrusive", filename = "anonFTP.nse" } +Entry{ category = "discovery", filename = "ASN.nse" } +Entry{ category = "external", filename = "ASN.nse" } Entry{ category = "default", filename = "robots.nse" } Entry{ category = "safe", filename = "robots.nse" } Entry{ category = "default", filename = "finger.nse" } @@ -77,8 +79,6 @@ Entry{ category = "discovery", filename = "ripeQuery.nse" } Entry{ category = "external", filename = "ripeQuery.nse" } Entry{ category = "demo", filename = "showHTTPVersion.nse" } Entry{ category = "version", filename = "PPTPversion.nse" } -Entry{ category = "discovery", filename = "ASN.nse" } -Entry{ category = "external", filename = "ASN.nse" } Entry{ category = "intrusive", filename = "brutePOP3.nse" } Entry{ category = "auth", filename = "brutePOP3.nse" } Entry{ category = "default", filename = "popcapa.nse" } @@ -89,12 +89,16 @@ Entry{ category = "external", filename = "whois.nse" } Entry{ category = "safe", filename = "whois.nse" } Entry{ category = "external", filename = "dns-safe-recursion-txid.nse" } Entry{ category = "intrusive", filename = "dns-safe-recursion-txid.nse" } -Entry{ category = "discovery", filename = "smb-enum.nse" } -Entry{ category = "intrusive", filename = "smb-enum.nse" } Entry{ category = "external", filename = "dns-safe-recursion-port.nse" } Entry{ category = "intrusive", filename = "dns-safe-recursion-port.nse" } +Entry{ category = "discovery", filename = "smb-security-mode.nse" } +Entry{ category = "safe", filename = "smb-security-mode.nse" } Entry{ category = "default", filename = "smb-os-discovery.nse" } Entry{ category = "discovery", filename = "smb-os-discovery.nse" } Entry{ category = "safe", filename = "smb-os-discovery.nse" } -Entry{ category = "discovery", filename = "smb-security-mode.nse" } -Entry{ category = "safe", filename = "smb-security-mode.nse" } +Entry{ category = "discovery", filename = "smb-enumusers.nse" } +Entry{ category = "intrusive", filename = "smb-enumusers.nse" } +Entry{ category = "discovery", filename = "smb-enumshares.nse" } +Entry{ category = "intrusive", filename = "smb-enumshares.nse" } +Entry{ category = "discovery", filename = "smb-enumdomains.nse" } +Entry{ category = "intrusive", filename = "smb-enumdomains.nse" } diff --git a/scripts/smb-enum.nse b/scripts/smb-enum.nse deleted file mode 100644 index 4acf51c2e..000000000 --- a/scripts/smb-enum.nse +++ /dev/null @@ -1,197 +0,0 @@ ---- Attempts to enumerate users and shares anonymously over SMB. --- --- First, it logs in as the anonymous user and tries to connect to IPC$. --- If it is successful, it knows that Null sessions are enabled. If it --- is unsuccessful, it can still check for shares (because Windows is --- cool like that). A list of common shares is checked (see the 'shares' --- variable) to see what anonymous can access. Either a successful result --- is returned (has access), STATUS_ACCESS_DENIED is returned (exists but --- anonymous can't access), or STATUS_BAD_NETWORK_NAME is returned (doesn't --- exist). --- --- Next, the Guest account is attempted with a blank password. If it's --- enabled, a message is displayed and shares that it has access to are --- checked the same as anonymous. --- --- Finally, the Administrator account is attempted with a blank password. --- Because Administrator can't typically be locked out, this should be --- safe. That being said, it is possible to configure Administrator to --- be lockoutable, so watch out for that caveat. If you do lock yourself --- out of Administrator, there's a bootdisk that can help. :) --- --- If Administrator has a blank password, it often doesn't allow remote --- logins, if this is the case, STATUS_ACCOUNT_RESTRICTION is returned --- instead of STATUS_ACCESS_DENIED, so we know the account has no password. --- ---@usage --- nmap --script smb-enum.nse -p445 127.0.0.1\n --- sudo nmap -sU -sS --script smb-enum.nse -p U:137,T:139 127.0.0.1\n --- ---@output --- Host script results: --- | SMB Enumeration: --- | Null sessions enabled --- | Anonymous shares found: IPC$ --- | Restricted shares found: C$ TEST --- | Guest account is enabled --- | Guest can access: IPC$ TEST --- | Administrator account has a blank password --- |_ Administrator can access: IPC$ C$ TEST ------------------------------------------------------------------------ - -id = "SMB Enumeration" -description = "Attempts to enumerate users and shares anonymously over SMB" -author = "Ron Bowes" -copyright = "Ron Bowes" -license = "Same as Nmap--See http://nmap.org/book/man-legal.html" -categories = {"discovery", "intrusive"} - -require 'smb' - --- Shares to try connecting to as Null session / GUEST -local shares = {"IPC", "C", "D", "TEST", "SHARE", "HOME", "DFS", "COMCFG" } - -hostrule = function(host) - - local port = smb.get_port(host) - - if(port == nil) then - return false - else - return true - end - -end ---- Attempts to connect to a list of shares as the given UID, returning the --- shares that it has and doesn't have access to. ---@param socket The socket to use ---@param ip The ip address of the host ---@param uid The UserID we're logged in as ---@return (allowed_shares, denied_shares) Lists of shares we can and can't access, --- but all of which exist. -function find_shares(socket, ip, uid) - local i - local allowed_shares = {} - local denied_shares = {} - - - for i = 1, #shares, 1 do - - local share = string.format("\\\\%s\\%s", ip, shares[i]) - - status, tree_result = smb.tree_connect(socket, share, uid) - if(status == false) then - if(tree_result == 0xc0000022) then -- STATUS_ACCESS_DENIED - denied_shares[#denied_shares + 1] = shares[i] - end - else - allowed_shares[#allowed_shares + 1] = shares[i] - end - - share = share .. "$" - status, tree_result = smb.tree_connect(socket, share, uid) - if(status == false) then - if(tree_result == 0xc0000022) then -- STATUS_ACCESS_DENIED - denied_shares[#denied_shares + 1] = shares[i] .. "$" - end - else - allowed_shares[#allowed_shares + 1] = shares[i] .. "$" - end - - end - - return allowed_shares, denied_shares -end - ---- Join strings together with a space. -function string_join(table) - local i - local response = " " - - for i = 1, #table, 1 do - response = response .. table[i] .. " " - end - - return response -end - -action = function(host) - local response = " \n" - local status, socket, negotiate_result, session_result - local allowed_shares, restricted_shares - - status, socket = smb.start(host) - if(status == false) then - return "ERROR: " .. socket - end - - status, negotiate_result = smb.negotiate_protocol(socket) - if(status == false) then - smb.stop(socket) - return "ERROR: " .. negotiate_result - end - - -- Start up a null session - status, session_result = smb.start_session(socket, "", negotiate_result['session_key'], negotiate_result['capabilities']) - if(status == false) then - smb.stop(socket) - return "ERROR: " .. session_result - end - - -- Check if null session has access to IPC$ - status, result = smb.tree_connect(socket, "IPC$", session_result['uid']) - if(status == true) then - response = response .. "Null sessions enabled\n" - end - - -- Find shares - allowed_shares, restricted_shares = find_shares(socket, host.ip, session_result['uid']) - - -- Display shares the Null user had access to - if(#allowed_shares > 0) then - response = response .. "Anonymous shares found: " .. string_join(allowed_shares) .. "\n" - end - - -- Display shares the Null user didn't have access to - if(#restricted_shares > 0) then - response = response .. "Restricted shares found: " .. string_join(restricted_shares) .. "\n" - end - - -- Check if Guest can log in - status, session_result = smb.start_session(socket, "GUEST", negotiate_result['session_key'], negotiate_result['capabilities']) - if(status == true) then - response = response .. "Guest account is enabled\n" - - -- Find shares for Guest - allowed_shares, restricted_shares = find_shares(socket, host.ip, session_result['uid']) - - -- Display shares Guest had access to - if(#allowed_shares > 0) then - response = response .. "Guest can access: " .. string_join(allowed_shares) .. "\n" - end - end - - -- Check if Administrator has a blank password - -- (we check Administrator and not other accounts because Administrator can't generally be locked out) - status, session_result = smb.start_session(socket, "ADMINISTRATOR", negotiate_result['session_key'], negotiate_result['capabilities']) - if(status == true) then - response = response .. "Administrator account has a blank password\n" - - -- Find shares for Administrator - allowed_shares, restricted_shares = find_shares(socket, host.ip, session_result['uid']) - - -- Display shares administrator had access to - if(#allowed_shares > 0) then - response = response .. "Administrator can access: " .. string_join(allowed_shares) .. "\n" - end - elseif(session_result == 0xc000006e) then -- STATUS_ACCOUNT_RESTRICTION - response = response .. "Administrator account has a blank password, but can't use SMB\n" - end - - - - smb.stop(socket) - return response -end - - diff --git a/scripts/smb-enumdomains.nse b/scripts/smb-enumdomains.nse new file mode 100644 index 000000000..6492fb6da --- /dev/null +++ b/scripts/smb-enumdomains.nse @@ -0,0 +1,182 @@ +--- Attempts to enumerate domains on a system, along with their policies. This will likely +-- only work without credentials against Windows 2000. \n +-- \n +-- After the initial bind() to SAMR, the sequence of calls is:\n +-- Connect4() -- get a connect_handle\n +-- EnumDomains() -- get a list of the domains (stop here if you just want the names)\n +-- QueryDomain() -- get the sid for the domain\n +-- OpenDomain() -- get a handle for each domain\n +-- QueryDomainInfo2() -- get the domain information\n +-- +--@usage +-- nmap --script smb-enumdomains.nse -p445 \n +-- sudo nmap -sU -sS --script smb-enumdomains.nse -p U:137,T:139 \n +-- +--@output +-- Host script results: +-- | MSRPC: List of domains:\n +-- | Domain: TEST1\n +-- | |_ SID: S-1-5-21-1060284298-842925246-839522115\n +-- | |_ Users: Administrator, ASPNET, Guest, Ron, test\n +-- | |_ Creation time: 2006-10-17 15:35:07\n +-- | |_ Min password length: 0 characters\n +-- | |_ Max password age: 10675199 days\n +-- | |_ Min password age: 0 days\n +-- | |_ Password history length: 0 passwords\n +-- | |_ Lockout threshold: 0 login attempts\n +-- | |_ Lockout duration: 60 minutes\n +-- | |_ Lockout window: 60 minutes\n +-- | |_ Password properties: \n +-- | |_ Password complexity requirements do not exist\n +-- |_ |_ Administrator account cannot be locked out\n + +----------------------------------------------------------------------- + +id = "MSRPC: List of domains" +description = "Tries calling the EnumDomains() and QueryDomainInfo2() RPC function to obtain a list of domains/policies." +author = "Ron Bowes" +copyright = "Ron Bowes" +license = "Same as Nmap--See http://nmap.org/book/man-legal.html" +categories = {"discovery","intrusive"} + +require 'msrpc' +require 'smb' +require 'stdnse' + +hostrule = function(host) + + local port = smb.get_port(host) + + if(port == nil) then + return false + else + return true + end + +end + +action = function(host) + local response = " \n" + local status, socket + local uid, tid, fid + + -- Create the SMB session + status, socket, uid, tid, fid = msrpc.start_smb(host, msrpc.SAMR_PATH) + if(status == false) then + return "ERROR: " .. socket + end + + -- Bind to SAMR service + status, bind_result = msrpc.bind(socket, msrpc.SAMR_UUID, msrpc.SAMR_VERSION, nil, uid, tid, fid) + if(status == false) then + msrpc.stop_smb(socket, uid, tid) + return "ERROR: " .. bind_result + end + + -- Call connect4() + status, connect4_result = msrpc.samr_connect4(socket, host.ip, uid, tid, fid) + if(status == false) then + msrpc.stop_smb(socket, uid, tid) + return "ERROR: " .. connect4_result + end + + -- Save the connect_handle + connect_handle = connect4_result['connect_handle'] + + -- Call EnumDomains() + status, enumdomains_result = msrpc.samr_enumdomains(socket, connect_handle, uid, tid, fid) + if(status == false) then + msrpc.stop_smb(socket, uid, tid) + return "ERROR: " .. enumdomains_result + end + + -- If no domanis were returned, print an error (I don't expect this will actually happen) + if(#enumdomains_result['domains'] == 0) then + return "ERROR: Couldn't find any domains to check" + end + + for i = 1, #enumdomains_result['domains'], 1 do + + local domain = enumdomains_result['domains'][i] + -- We don't care about the 'builtin' domain + if(domain ~= 'Builtin') then + local sid + local domain_handle + + -- Call LookupDomain() + status, lookupdomain_result = msrpc.samr_lookupdomain(socket, connect_handle, domain, uid, tid, fid) + if(status == false) then + msrpc.stop_smb(socket, uid, tid) + return "ERROR: " .. lookupdomain_result + end + + -- Save the sid + sid = lookupdomain_result['sid'] + + -- Call OpenDomain() + status, opendomain_result = msrpc.samr_opendomain(socket, connect_handle, sid, uid, tid, fid) + if(status == false) then + msrpc.stop_smb(socket, uid, tid) + return "ERROR: " .. opendomain_result + end + + -- Save the domain handle + domain_handle = opendomain_result['domain_handle'] + + -- Call QueryDomainInfo2() to get domain properties. We call these for three types == 1, 8, and 12, since those return + -- the most useful information. + status, querydomaininfo2_result = msrpc.samr_querydomaininfo2(socket, domain_handle, 1, uid, tid, fid) + if(status == false) then + msrpc.stop_smb(socket, uid, tid) + return "ERROR: " .. querydomaininfo2_result + end + status, querydomaininfo2_result = msrpc.samr_querydomaininfo2(socket, domain_handle, 8, uid, tid, fid, querydomaininfo2_result) + if(status == false) then + msrpc.stop_smb(socket, uid, tid) + return "ERROR: " .. querydomaininfo2_result + end + status, querydomaininfo2_result = msrpc.samr_querydomaininfo2(socket, domain_handle, 12, uid, tid, fid, querydomaininfo2_result) + if(status == false) then + msrpc.stop_smb(socket, uid, tid) + return "ERROR: " .. querydomaininfo2_result + end + + -- Call EnumDomainUsers() to get users + status, enumdomainusers_result = msrpc.samr_enumdomainusers(socket, domain_handle, uid, tid, fid) + if(status == false) then + msrpc.stop_smb(socket, uid, tid) + return "ERROR: " .. enumdomainusers_result + end + + -- Close the domain handle + msrpc.samr_close(socket, domain_handle, uid, tid, fid) + + -- Finally, fill in the response! + response = response .. string.format("Domain: %s\n", domain) + response = response .. string.format(" |_ SID: %s\n", msrpc.sid_to_string(lookupdomain_result['sid'])) + response = response .. string.format(" |_ Users: %s\n", stdnse.strjoin(", ", enumdomainusers_result['names'])) + response = response .. string.format(" |_ Creation time: %s\n", querydomaininfo2_result['create_date']) + response = response .. string.format(" |_ Min password length: %d characters\n", querydomaininfo2_result['min_password_length']) + response = response .. string.format(" |_ Max password age: %d days\n", querydomaininfo2_result['max_password_age']) + response = response .. string.format(" |_ Min password age: %d days\n", querydomaininfo2_result['min_password_age']) + response = response .. string.format(" |_ Password history length: %d passwords\n", querydomaininfo2_result['password_history_length']) + response = response .. string.format(" |_ Lockout threshold: %d login attempts\n", querydomaininfo2_result['lockout_threshold']) + response = response .. string.format(" |_ Lockout duration: %d minutes\n", querydomaininfo2_result['lockout_duration']) + response = response .. string.format(" |_ Lockout window: %d minutes\n", querydomaininfo2_result['lockout_window']) + if(#querydomaininfo2_result['password_properties_list'] > 0) then + response = response .. " |_ Password properties: \n |_ " .. stdnse.strjoin("\n |_ ", querydomaininfo2_result['password_properties_list']) .. "\n" + end + end + end + + -- Close the connect handle + msrpc.samr_close(socket, connect_handle, uid, tid, fid) + + -- Close the SMB session + msrpc.stop_smb(socket, uid, tid) + + return response + +end + + diff --git a/scripts/smb-enumshares.nse b/scripts/smb-enumshares.nse new file mode 100644 index 000000000..6cf1e3615 --- /dev/null +++ b/scripts/smb-enumshares.nse @@ -0,0 +1,179 @@ +--- Attempts to call the srvsvc.NetShareEnumAll() MSRPC function. This will +-- likely only work anonymously against Windows 2000. \n +--\n +-- There isn't a whole lot to say about this one. The sequence of calls after +-- the initial bind() is:\n +-- NetShareEnumAll()\n +--\n +-- Since NetShareEnumAll() only works anonymously, if it fails this will check +-- a handful of common shares. \n +--\n +-- Once it has a list of shares, whether it was pulled over MSRPC or guessed, +-- we attempt to connect to each of them with a standard smb tree_connect request +-- over a null session. We record which ones succeeded and failed (that is, which +-- shares allowed for anonymous access).\n +-- +--@usage +-- nmap --script smb-enumshares.nse -p445 \n +-- sudo nmap -sU -sS --script smb-enumshares.nse -p U:137,T:139 \n +-- +--@output +-- Host script results:\n +-- TODO +----------------------------------------------------------------------- + +id = "MSRPC: NetShareEnumAll()" +description = "Tries calling the NetShareEnumAll() RPC function, and guessing shares" +author = "Ron Bowes" +copyright = "Ron Bowes" +license = "Same as Nmap--See http://nmap.org/book/man-legal.html" +categories = {"discovery","intrusive"} + +require 'msrpc' +require 'smb' +require 'stdnse' + +hostrule = function(host) + + local port = smb.get_port(host) + + if(port == nil) then + return false + else + return true + end + +end + +---Attempts to enumerate the shares on a remote system using MSRPC calls. This will likely fail +-- against a modern system, but will succeed against Windows 2000. +-- +--@param host The host object. +--@return (status, result) If status is false, result is an error string. Otherwise, result is +-- a list of all shares on a system. +local function samr_enum_shares(host) + + local status, socket, uid, tid, fid + local bind_result, netshareenumall_result + + -- Create the SMB session + status, socket, uid, tid, fid = msrpc.start_smb(host, msrpc.SRVSVC_PATH) + if(status == false) then + return false, socket + end + + -- Bind to SRVSVC service + status, bind_result = msrpc.bind(socket, msrpc.SRVSVC_UUID, msrpc.SRVSVC_VERSION, nil, uid, tid, fid) + if(status == false) then + smb.stop(socket) + return false, bind_result + end + + -- Call netsharenumall + status, netshareenumall_result = msrpc.srvsvc_netshareenumall(socket, host.ip, uid, tid, fid) + if(status == false) then + smb.stop(socket) + return false, netshareenumall_result + end + + -- Stop the SMB session + smb.stop(socket, uid, tid) + + return true, netshareenumall_result['shares'] +end + +---Attempts to connect to a list of shares as the anonymous user, returning which ones +-- it has and doesn't have access to. +-- +--@param host The host object +--@param shares An array of shares to check +--@return (allowed_shares, denied_shares) Lists of shares we can and can't access, +-- but all of which exist. +function check_shares(host, shares) + local i + local allowed_shares = {} + local denied_shares = {} + + -- Begin the SMB session + status, socket = smb.start(host) + if(status == false) then + return false, socket + end + + -- Negotiate the protocol + status, negotiate_result = smb.negotiate_protocol(socket) + if(status == false) then + smb.stop(socket) + return false, negotiate_result + end + + -- Start up a null session + status, session_result = smb.start_session(socket, "", negotiate_result['session_key'], negotiate_result['capabilities']) + if(status == false) then + smb.stop(socket) + return false, session_result + end + + -- Connect to the shares + stdnse.print_debug(2, "EnumShares: Testing %d shares", #shares) + for i = 1, #shares, 1 do + + -- Change the share to the '\\ip\share' format + local share = string.format("\\\\%s\\%s", host.ip, shares[i]) + + -- Try connecting to the tree + stdnse.print_debug(3, "EnumShares: Testing share %s", share) + status, tree_result = smb.tree_connect(socket, share, session_result['uid']) + -- If it fails, checkwhy + if(status == false) then + -- If the result was ACCESS_DENIED, record it + if(tree_result == 0xc0000022 or tree_result == 'NT_STATUS_ACCESS_DENIED') then + stdnse.print_debug(3, "EnumShares: Access was denied") + denied_shares[#denied_shares + 1] = shares[i] + else + stdnse.print_debug(3, "EnumShares: Share didn't pan out: %s", tree_result) + end + else + -- Add it to allowed shares + stdnse.print_debug(3, "EnumShares: Access was granted") + allowed_shares[#allowed_shares + 1] = shares[i] + smb.tree_disconnect(socket, session_result['uid'], tree_result['tid']) + end + end + + -- Log off the user + smb.stop(socket, session_result['uid']) + + return allowed_shares, denied_shares +end + + +action = function(host) + local result, shared + local response = " \n" + local shares = {} + local allowed, denied + + -- Try and do this the good way, make a MSRPC call to get the shares + result, shares = samr_enum_shares(host) + + -- If that failed, try doing it with brute force. This almost certainly won't find everything, but it's the + -- best we can do. + if(result == false) then + response = response .. string.format("Couldn't enum all shares, checking for common ones (%s)\n", shares) + -- Take some common share names I've seen + shares = {"IPC$", "ADMIN$", "TEST", "TEST$", "HOME", "HOME$"} + -- Try every alphabetic share, with and without a trailing '$' + for i = string.byte("A", 1), string.byte("Z", 1), 1 do + shares[#shares + 1] = string.char(i) + shares[#shares + 1] = string.char(i) .. "$" + end + end + + -- Break them into anonymous/authenticated shares + allowed, denied = check_shares(host, shares) + + return response .. string.format("Anonymous shares: %s\nRestricted shares: %s\n", stdnse.strjoin(", ", allowed), stdnse.strjoin(", ", denied)) +end + + diff --git a/scripts/smb-enumusers.nse b/scripts/smb-enumusers.nse new file mode 100644 index 000000000..c3a38e71f --- /dev/null +++ b/scripts/smb-enumusers.nse @@ -0,0 +1,384 @@ +--- Attempts to enumerate the users on a remote Windows system, with as much information as possible, +-- through a variety of techniques (over SMB + MSRPC, which uses port 445 or 139). \n +--\n +-- Will first attempt to call the QueryDisplayInfo() MSRPC function. If NULL sessions are enabled, +-- this will succeed and pull back a detailed list of users. Unfortunately, this likely won't succeed +-- unless we're scanning Windows 2000. When this test is performed, the following MSRPC functions +-- are called:\n +--\n +-- Bind() -- bind to the SAMR service\n +-- Connect4() -- get a connect_handle\n +-- EnumDomains() -- get a list of the domains\n +-- QueryDomain() -- get the sid for the domain\n +-- OpenDomain() -- get a handle for each domain\n +-- QueryDisplayInfo() -- get the list of users in the domain\n +-- Close() -- Close the domain handle\n +-- Close() -- Close the connect handle\n +--\n +-- Credit goes out to the enum.exe program, the code I wrote for this is largely due to packetlogs +-- I took of its operations. \n +--\n +-- Regardless of whether or not this succeeds, a second technique is used to pull user accounts. +-- This one is apparently successful against more machines, although I haven't found a machine +-- that this only works against. However, I did find that this will turn up more users for certain +-- systems (although I haven't figured out why). \n +-- \n +-- Each user on a Windows system has an RID. The RID of 500 is the Administrator account (even if +-- it's renamed), 501 is the Guest account, and 1000+ are the user accounts. This technique, which +-- was originally used in the sid2user/user2sid programs, will attempt to convert common RID numbers +-- to names to discover users. \n +-- \n +-- First, the SID of the server has to be determined. This is done by looking up any name present on +-- the server using a technique like user2sid. For this code, we try and convert as many names as we +-- can find -- all we need is one valid name for this to succeed. In this code, I use:\n +-- - The computer name / domain name, returned in SMB_COM_NEGOTIATE\n +-- - An nbstat query to get the server name and the currently loggeed in user\n +-- - Some common names ("administrator", "guest", and "test")\n +--\n +-- In theory, the computer name should be sufficient for this to always work, and the rest of the \n +-- names are in there for good measure. \n +--\n +-- Once that's completed, the RIDs 500 - 505 are requested, and any responses are displayed. Then, +-- starting at 1000, we take small groups of RIDs which are requestd. I break them into +-- smaller groups because if too many are requested at once, we get a STATUS_BUFFER_OVERFLOW +-- error. We try every RID up to 1100, then, as soon as we get an empty group (5 RIDs in a row +-- without a result), we stop. \n +--\n +-- It might be a good idea to modify this, in the future, with some more intelligence. For example, +-- have it run until it get 5 groups in a row with no results instead of going up to 1100. I +-- performed a test on an old server we have here with a lot of accounts, and I got these results: +-- 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.\n +--\n +-- The disadvantage of using the user2sid/sid2user technique is that less information is returned +-- about the user. \n +--\n +-- The names and details from both of these techniques are merged and displayed. If the output is +-- verbose, then as many details as possible are displayed, otherwise only the list of usernames +-- are displayed. The names are ordered alphabetically.\n +-- +--@usage +-- nmap --script smb-enumusers.nse -p445 \n +-- sudo nmap -sU -sS --script smb-enumusers.nse -p U:137,T:139 \n +-- +--@output +-- TODO +----------------------------------------------------------------------- + +id = "MSRPC: List of user accounts" +description = "Tries calling SAMR and LSA functions to get a list of user accounts." +author = "Ron Bowes" +copyright = "Ron Bowes" +license = "Same as Nmap--See http://nmap.org/book/man-legal.html" +categories = {"discovery","intrusive"} + +require 'msrpc' +require 'smb' +require 'stdnse' + +hostrule = function(host) + + local port = smb.get_port(host) + + if(port == nil) then + return false + else + return true + end + +end + +---Attempt to enumerate users through SAMR methods. See the file description for more information. +-- +--@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 table contains a 'name', 'domain', 'fullname', 'rid', and 'description'. +local function enum_samr(host) + + local bind_result, connect4_result, enumdomains_result + local connect_handle + local status, socket + local uid, tid, fid + local response = {} + + -- Create the SMB session + status, socket, uid, tid, fid = msrpc.start_smb(host, msrpc.SAMR_PATH) + if(status == false) then + return false, socket + end + + -- Bind to SAMR service + status, bind_result = msrpc.bind(socket, msrpc.SAMR_UUID, msrpc.SAMR_VERSION, nil, uid, tid, fid) + if(status == false) then + msrpc.stop_smb(socket, uid, tid) + return false, bind_result + end + + -- Call connect4() + status, connect4_result = msrpc.samr_connect4(socket, host.ip, uid, tid, fid) + if(status == false) then + msrpc.stop_smb(socket, uid, tid) + return false, connect4_result + end + + -- Save the connect_handle + connect_handle = connect4_result['connect_handle'] + + -- Call EnumDomains() + status, enumdomains_result = msrpc.samr_enumdomains(socket, connect_handle, uid, tid, fid) + if(status == false) then + msrpc.stop_smb(socket, uid, tid) + return false, enumdomains_result + end + + -- If no domains were returned, go back with an error + if(#enumdomains_result['domains'] == 0) then + msrpc.stop_smb(socket, uid, tid) + return false, "Couldn't find any domains" + end + + for i = 1, #enumdomains_result['domains'], 1 do + + local domain = enumdomains_result['domains'][i] + -- We don't care about the 'builtin' domain + if(domain ~= 'Builtin') then + local sid + local domain_handle + local opendomain_result, querydisplayinfo_result + + -- Call LookupDomain() + status, lookupdomain_result = msrpc.samr_lookupdomain(socket, connect_handle, domain, uid, tid, fid) + if(status == false) then + msrpc.stop_smb(socket, uid, tid) + return false, lookupdomain_result + end + + -- Save the sid + sid = lookupdomain_result['sid'] + + -- Call OpenDomain() + status, opendomain_result = msrpc.samr_opendomain(socket, connect_handle, sid, uid, tid, fid) + if(status == false) then + msrpc.stop_smb(socket, uid, tid) + return false, opendomain_result + end + + -- Save the domain handle + domain_handle = opendomain_result['domain_handle'] + + -- Call QueryDisplayInfo() + status, querydisplayinfo_result = msrpc.samr_querydisplayinfo(socket, domain_handle, uid, tid, fid) + if(status == false) then + msrpc.stop_smb(socket, uid, tid) + return false, querydisplayinfo_result + end + + -- Close the domain handle + msrpc.samr_close(socket, domain_handle, uid, tid, fid) + + -- Finally, fill in the response! + for i = 1, #querydisplayinfo_result['details'], 1 do + querydisplayinfo_result['details'][i]['domain'] = domain + response[#response + 1] = querydisplayinfo_result['details'][i] + end + end -- Checking for 'builtin' + end -- Domain loop + + -- Close the connect handle + msrpc.samr_close(socket, connect_handle, uid, tid, fid) + + -- Stop the SAMR SMB + msrpc.stop_smb(socket, uid, tid) + + 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, result) If status is false, result is an error message. Otherwise, result is an +-- array of tables. Each table contains a 'name', 'domain', and 'rid'. +local function enum_lsa(host) + + local status, socket + local uid, tid, fid + local response = {} + + -- Create the SMB session + status, socket, uid, tid, fid, negotiate_result = msrpc.start_smb(host, msrpc.LSA_PATH) + if(status == false) then + return false, socket + end + + -- Bind to LSA service + status, bind_result = msrpc.bind(socket, msrpc.LSA_UUID, msrpc.LSA_VERSION, nil, uid, tid, fid) + if(status == false) then + msrpc.stop_smb(socket, uid, tid) + return false, bind_result + end + + -- Open the LSA policy + status, openpolicy2_result = msrpc.lsa_openpolicy2(socket, host.ip, uid, tid, fid) + if(status == false) then + msrpc.stop_smb(socket, uid, tid) + return false, openpolicy2_result + end + + -- Start with some common names, as well as the name returned by the negotiate call + names = {"administrator", "guest", "test", negotiate_result['domain'], negotiate_result['server'] } + + -- 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(socket, openpolicy2_result['policy_handle'], names, uid, tid, fid) + if(status == false) then + msrpc.stop_smb(socket, uid, tid) + return false, lookupnames2_result + end + + -- Loop through the domains returned and find teh users in each + for i = 1, #lookupnames2_result['domains'], 1 do + local domain = lookupnames2_result['domains'][i]['name'] + local sid = lookupnames2_result['domains'][i]['sid'] + local rids = { } + local start = 1000 + + -- Start by looking up 500 - 505 (will likely be Administrator + guest) + for j = 500, 505, 1 do + rids[#rids + 1] = j + end + + status, lookupsids2_result = msrpc.lsa_lookupsids2(socket, openpolicy2_result['policy_handle'], sid, rids, uid, tid, fid) + if(status == false) then + msrpc.stop_smb(socket, uid, tid) + return false, lookupsids2_result + end + + -- Put the details for each name into an array + for j = 1, #lookupsids2_result['details'], 1 do + if(lookupsids2_result['details'][j]['name'] ~= nil) then + local result = {} + result['name'] = lookupsids2_result['details'][j]['name'] + result['rid'] = 500 + j - 1 + result['domain'] = domain + response[#response + 1] = result + end + end + + -- Now do groups of 5 users, until we get past 1100 and have an empty group + repeat + rids = {} + for j = start, start + 4, 1 do + rids[#rids + 1] = j + end + + -- Try converting this group of RIDs into names + status, lookupsids2_result = msrpc.lsa_lookupsids2(socket, openpolicy2_result['policy_handle'], sid, rids, uid, tid, fid) + if(status == false) then + msrpc.stop_smb(socket, uid, tid) + return false, lookupsids2_result + end + + -- Put the details for each name into an array + for j = 1, #lookupsids2_result['details'], 1 do + if(lookupsids2_result['details'][j]['name'] ~= nil) then + local result = {} + result['name'] = lookupsids2_result['details'][j]['name'] + result['rid'] = start + j - 1 + result['domain'] = domain + response[#response + 1] = result + end + end + + -- Go to the next set of RIDs + start = start + 5 + until #lookupsids2_result['names'] == 0 and start > 1100 + + end + + -- Close the handle + msrpc.lsa_close(socket, openpolicy2_result['policy_handle'], uid, tid, fid) + + msrpc.stop_smb(socket, uid, tid) + + return true, response +end + + + +action = function(host) + local i, j + local status + local samr_result, lsa_result + local names = {} + local name_strings = {} + local response = " \n" + + -- Try enumerating through SAMR + status, samr_result = enum_samr(host) + if(status == false) then + response = response .. "Enum via SAMR error: " .. samr_result .. "\n" + else + -- Copy the returned array into the names[] table, using the name as the key + stdnse.print_debug("EnumUsers: Received %d names from SAMR", #samr_result) + for i = 1, #samr_result, 1 do + names[string.upper(samr_result[i]['name'])] = samr_result[i] + end + end + + -- Try enumerating through LSA + status, lsa_result = enum_lsa(host) + if(status == false) then + response = response .. "Enum via LSA error: " .. lsa_result .. "\n" + else + -- Copy the returned array into the names[] table, using the name as the key + stdnse.print_debug("EnumUsers: Received %d names from LSA", #samr_result) + for i = 1, #lsa_result, 1 do + if(names[lsa_result[i]['name']] == nil) then + names[string.upper(lsa_result[i]['name'])] = lsa_result[i] + end + end + end + + -- Put the names into an array of strings, so we can sort them + for name, details in pairs(names) do + name_strings[#name_strings + 1] = name + end + -- Sort them + table.sort(name_strings, function (a, b) return string.lower(a) < string.lower(b) end) + + -- Check if we actually got any names back + if(#name_strings == 0) then + response = response .. "Sorry, couldn't find any account names anonymously!" + else + -- If we're not verbose, just print out the names. Otherwise, print out everything we can + if(nmap.verbosity() < 1) then + response = response .. stdnse.strjoin(", ", name_strings) + else + for i = 1, #name_strings, 1 do + local name = name_strings[i] + response = response .. string.format("%s\n", names[name]['name']) + if(names[name]['domain'] ~= nil) then response = response .. string.format(" |_ Domain: %s\n", names[name]['domain']) end + if(names[name]['rid'] ~= nil) then response = response .. string.format(" |_ RID: %s\n", names[name]['rid']) 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]['flags'] ~= nil) then response = response .. string.format(" |_ Flags: %s\n", stdnse.strjoin(", ", names[name]['flags_list'])) end + end + end + end + + return response +end + + diff --git a/scripts/smb-os-discovery.nse b/scripts/smb-os-discovery.nse index 4a5d7970f..8d8989d95 100644 --- a/scripts/smb-os-discovery.nse +++ b/scripts/smb-os-discovery.nse @@ -6,10 +6,10 @@ -- sudo nmap -sU -sS --script smb-os-discovery.nse -p U:137,T:139 127.0.0.1\n -- --@output --- | OS from SMB: Windows 2000 --- | LAN Manager: Windows 2000 LAN Manager --- | Name: WORKGROUP\TEST1 --- |_ System time: 2008-09-09 20:55:55 UTC-5 +-- | OS from SMB: Windows 2000\n +-- | LAN Manager: Windows 2000 LAN Manager\n +-- | Name: WORKGROUP\TEST1\n +-- |_ System time: 2008-09-09 20:55:55 UTC-5\n -- ----------------------------------------------------------------------- @@ -52,28 +52,30 @@ end action = function(host) + -- Start up SMB status, socket = smb.start(host) - if(status == false) then return "Error: " .. socket end + -- Negotiate protocol status, negotiate_result = smb.negotiate_protocol(socket) - if(status == false) then stdnse.print_debug(2, "Negotiate session failed") smb.stop(socket) return "Error: " .. negotiate_result end + -- Start a session status, session_result = smb.start_session(socket, "", negotiate_result['session_key'], negotiate_result['capabilities']) - if(status == false) then smb.stop(socket) return "Error: " .. session_result end - smb.stop(socket) + -- Kill SMB + smb.stop(socket, session_result['uid']) + return string.format("%s\nLAN Manager: %s\nName: %s\\%s\nSystem time: %s %s\n", get_windows_version(session_result['os']), session_result['lanmanager'], negotiate_result['domain'], negotiate_result['server'], negotiate_result['date'], negotiate_result['timezone_str']) end diff --git a/scripts/smb-security-mode.nse b/scripts/smb-security-mode.nse index 6fdbd43d8..3c5ce4fa3 100644 --- a/scripts/smb-security-mode.nse +++ b/scripts/smb-security-mode.nse @@ -1,25 +1,25 @@ ---- Returns information about the SMB security level determined by SMB. --- --- Here is how to interpret the output: --- +--- Returns information about the SMB security level determined by SMB. \n +--\n +-- Here is how to interpret the output:\n +--\n -- User-level security: Each user has a separate username/password that is used -- to log into the system. This is the default setup of pretty much everything --- these days. +-- these days. \n -- Share-level security: The anonymous account should be used to log in, then -- the password is given (in plaintext) when a share is accessed. All users who -- have access to the share use this password. This was the original way of doing -- things, but isn't commonly seen, now. If a server uses share-level security, --- it is vulnerable to sniffing. --- +-- it is vulnerable to sniffing. \n +--\n -- Challenge/response passwords: If enabled, the server can accept any type of --- password: --- * Plaintext --- * LM and NTLM --- * LMv2 and NTLMv2 +-- password:\n +-- * Plaintext\n +-- * LM and NTLM\n +-- * LMv2 and NTLMv2\n -- If it isn't set, the server can only accept plaintext passwords. Most servers -- are configured to use challenge/response these days. If a server is configured --- to accept plaintext passwords, it is vulnerable to sniffing. --- +-- to accept plaintext passwords, it is vulnerable to sniffing. \n +--\n -- Message signing: If required, all messages between the client and server must -- sign be signed by a shared key, derived from the password and the server -- challenge. If supported and not required, message signing is negotiated between @@ -27,18 +27,18 @@ -- don't sign messages, so if message signing isn't required by the server, messages -- probably won't be signed; additionally, if performing a man-in-the-middle attack, -- an attacker can negotiate no message signing. If message signing isn't required, the --- server is vulnerable to man-in-the-middle attacks. --- --- See nselib/smb.lua for more information on the protocol itself. --- +-- server is vulnerable to man-in-the-middle attacks. \n +-- \n +-- See nselib/smb.lua for more information on the protocol itself. \n +--\n --@usage -- nmap --script smb-security-mode.nse -p445 127.0.0.1\n -- sudo nmap -sU -sS --script smb-security-mode.nse -p U:137,T:139 127.0.0.1\n -- --@output --- | SMB Security: User-level authentication --- | SMB Security: Challenge/response passwords supported --- |_ SMB Security: Message signing supported +-- | SMB Security: User-level authentication\n +-- | SMB Security: Challenge/response passwords supported\n +-- |_ SMB Security: Message signing supported\n -- -----------------------------------------------------------------------