This repository has been archived on 2025-02-13. You can view files and clone it, but cannot push or open issues or pull requests.
lanScan3/nmap/scripts/smb-shares-size.nse
2024-10-07 01:16:44 +02:00

206 lines
7.7 KiB
Lua

local stdnse = require "stdnse"
local smb = require "smb"
local smb2 = require "smb2"
local msrpc = require "msrpc"
local bin = require "bin"
local shortport = require "shortport"
description = [[
Return free and total size in octets of each SMB shares
]]
---
-- @args See the documentation for the smbauth library.
--
-- @usage nmap -p445 --script smb-shares-size.nse <host>
--
-- @output
-- Host script results:
-- | smb-shares-size:
-- | data:
-- | FreeSize: 38495883264
-- | TotalSize: 500961574912
-- |_ IPC$: NT_STATUS_ACCESS_DENIED
---
categories = {"discovery", "intrusive"}
author = "Adrien Malingrey"
license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
portrule = shortport.service({"microsoft-ds", "netbios-ssn"})
action = function(host)
local status, shares, extra
local response = stdnse.output_table()
-- Try and do this the good way, make a MSRPC call to get the shares
stdnse.debug1("SMB: Attempting to log into the system to enumerate shares")
status, shares = msrpc.enum_shares(host)
if(status == false) then
return stdnse.format_output(false, string.format("Couldn't enumerate shares: %s", shares))
end
-- Get more information on each share
for i = 1, #shares, 1 do
local share = shares[i]
if (share ~= nil) then
local status, result = get_share_info(host, share)
if (status) then
response[share] = result
end
end
end
return response
end
TRANS2_QUERY_FS_INFORMATION = 0x0003
SMB_QUERY_FS_SIZE_INFO = 0x0103
---Attempts to retrieve additional information about a share. Will fail unless we have
-- administrative access.
--
--@param host The host object.
--@return Status (true or false).
--@return A table of information about the share (if status is true) or an an error string (if
-- status is false).
function get_share_info(host, share)
local status, smbstate, err
local hostaddress = (host.name ~= '' and host.name) or host.ip
local path = "\\\\" .. hostaddress .. "\\" .. share
status, smbstate = smb.start(host)
status, err = smb.negotiate_protocol(smbstate, {})
status, err = smb.start_session(smbstate, {})
status, err = smb.tree_connect(smbstate, path, {})
stdnse.debug1("SMB: Getting information for share: %s", path)
local status, err = send_transaction2(smbstate, TRANS2_QUERY_FS_INFORMATION, bin.pack("<S", SMB_QUERY_FS_SIZE_INFO))
if ( not(status) ) then
status, err = smb.stop(smbstate)
return false, "Failed to send data to server: send_transaction2"
end
local status, response = receive_transaction2(smbstate)
if ( not(status) ) then
status, err = smb.stop(smbstate)
return false, response
end
local pos, totalAllocationUnits, totalFreeAllocationUnits, sectorsPerAllocationUnit, bytesPerSector = bin.unpack("<LLII", response.data)
status, err = smb.stop(smbstate)
return true, {
TotalSize = totalAllocationUnits * sectorsPerAllocationUnit * bytesPerSector,
FreeSize = totalFreeAllocationUnits * sectorsPerAllocationUnit * bytesPerSector
}
end
-- Taken from smb lib
function send_transaction2(smbstate, sub_command, function_parameters, function_data, overrides)
overrides = overrides or {}
local header1, header2, header3, header4, command, status, flags, flags2, pid_high, signature, unused, pid, mid
local header, parameters, data
local parameter_offset = 0
local parameter_size = 0
local data_offset = 0
local data_size = 0
local total_word_count, total_data_count, reserved1, parameter_count, parameter_displacement, data_count, data_displacement, setup_count, reserved2
local response = {}
-- Header is 0x20 bytes long (not counting NetBIOS header).
header = smb.smb_encode_header(smbstate, smb.command_codes['SMB_COM_TRANSACTION2'], overrides) -- 0x32 = SMB_COM_TRANSACTION2
if(function_parameters) then
parameter_offset = 0x44
parameter_size = #function_parameters
data_offset = #function_parameters + 33 + 32
end
-- Parameters are 0x20 bytes long.
parameters = bin.pack("<SSSSCCSISSSSSCCS",
parameter_size, -- Total parameter count.
data_size, -- Total data count.
0x000a, -- Max parameter count.
0x3984, -- Max data count.
0x00, -- Max setup count.
0x00, -- Reserved.
0x0000, -- Flags (0x0000 = 2-way transaction, don't disconnect TIDs).
0x00001388, -- Timeout (0x00000000 = return immediately).
0x0000, -- Reserved.
parameter_size, -- Parameter bytes.
parameter_offset, -- Parameter offset.
data_size, -- Data bytes.
data_offset, -- Data offset.
0x01, -- Setup Count
0x00, -- Reserved
sub_command -- Sub command
)
local data = "\0\0\0" .. (function_parameters or '')
.. (function_data or '')
-- Send the transaction request
stdnse.debug2("SMB: Sending SMB_COM_TRANSACTION2")
local result, err = smb.smb_send(smbstate, header, parameters, data, overrides)
if(result == false) then
stdnse.debug1("SMB: Try SMBv2 connexion")
local result, err = smb2.smb2_send(smbstate, header, parameters, data, overrides)
if(result == false) then
return false, err
end
end
return true
end
function receive_transaction2(smbstate)
-- Read the result
local status, header, parameters, data = smb.smb_read(smbstate)
if(status ~= true) then
stdnse.debug1("SMB: Try SMBv2 connexion")
local status, header, parameters, data = smb2.smb2_read(smbstate)
if(status ~= true) then
return false, header
end
end
-- Check if it worked
local pos, header1, header2, header3, header4, command, status, flags, flags2, pid_high, signature, unused, tid, pid, uid, mid = bin.unpack("<CCCCCICSSlSSSSS", header)
if(header1 == nil or mid == nil) then
return false, "SMB: ERROR: Server returned less data than it was supposed to (one or more fields are missing); aborting [29]"
end
if(status ~= 0) then
if(smb.status_names[status] == nil) then
return false, string.format("Unknown SMB error: 0x%08x\n", status)
else
return false, smb.status_names[status]
end
end
-- Parse the parameters
local pos, total_word_count, total_data_count, reserved1, parameter_count, parameter_offset, parameter_displacement, data_count, data_offset, data_displacement, setup_count, reserved2 = bin.unpack("<SSSSSSSSSCC", parameters)
if(total_word_count == nil or reserved2 == nil) then
return false, "SMB: ERROR: Server returned less data than it was supposed to (one or more fields are missing); aborting [30]"
end
-- Convert the parameter/data offsets into something more useful (the offset into the data section)
-- - 0x20 for the header, - 0x01 for the length.
parameter_offset = parameter_offset - 0x20 - 0x01 - #parameters - 0x02;
-- - 0x20 for the header, - 0x01 for parameter length, the parameter length, and - 0x02 for the data length.
data_offset = data_offset - 0x20 - 0x01 - #parameters - 0x02;
-- I'm not sure I entirely understand why the '+1' is here, but I think it has to do with the string starting at '1' and not '0'.
local function_parameters = string.sub(data, parameter_offset + 1, parameter_offset + parameter_count)
local function_data = string.sub(data, data_offset + 1, data_offset + data_count)
local response = {}
response['parameters'] = function_parameters
response['data'] = function_data
return true, response
end