diff --git a/nselib/mssql.lua b/nselib/mssql.lua index 1553806fe..466bd77fd 100644 --- a/nselib/mssql.lua +++ b/nselib/mssql.lua @@ -74,12 +74,19 @@ -- argument is not given but mssql.username, a blank password -- is used. -- --- @args mssql.instance-name The name of the instance to connect to. +-- @args mssql.instance-name In addition to instances discovered via port +-- scanning and version detection, run scripts on +-- these named instances (string or list of strings) -- --- @args mssql.instance-port The port of the instance to connect to. +-- @args mssql.instance-port In addition to instances discovered via port +-- scanning and version detection, run scripts on +-- the instances running on these ports (number or list of numbers) -- --- @args mssql.instance-all Targets all SQL server instances discovered --- through the browser service. +-- @args mssql.instance-all In addition to instances discovered via port +-- scanning and version detection, run scripts on all +-- discovered instances. These include named-pipe +-- instances via SMB and those discovered via the +-- browser service. -- -- @args mssql.domain The domain against which to perform integrated -- authentication. When set, the scripts assume integrated authentication @@ -107,13 +114,14 @@ local math = require "math" local match = require "match" local nmap = require "nmap" local datetime = require "datetime" -local shortport = require "shortport" +local outlib = require "outlib" local smb = require "smb" local smbauth = require "smbauth" local stdnse = require "stdnse" local strbuf = require "strbuf" local string = require "string" local table = require "table" +local tableaux = require "tableaux" local unicode = require "unicode" _ENV = stdnse.module("mssql", stdnse.seeall) @@ -150,12 +158,25 @@ do end MSSQL_TIMEOUT = timeout - SCANNED_PORTS_ONLY = false - if ( stdnse.get_script_args( "mssql.scanned-ports-only" ) ) then - SCANNED_PORTS_ONLY = true - end end +-- Make args either a list or nil +local function list_of (input, transform) + if not input then return nil end + if type(input) ~= "table" then + return {transform(input)} + end + for i, v in ipairs(input) do + input[i] = transform(v) + end + return input +end + +local SCANNED_PORTS_ONLY = not not stdnse.get_script_args("mssql.scanned-ports-only") +local targetInstanceNames = list_of(stdnse.get_script_args("mssql.instance-name"), string.upper) +local targetInstancePorts = list_of(stdnse.get_script_args("mssql.instance-port"), tonumber) +local targetAllInstances = not not stdnse.get_script_args("mssql.instance-all") + -- This constant is number of seconds from 1900-01-01 to 1970-01-01 local tds_offset_seconds = -2208988800 - datetime.utc_offset() @@ -2561,6 +2582,9 @@ Helper = --- Gets a table containing SqlServerInstanceInfo objects discovered on -- the specified host (and port, if specified). -- + -- This table is the NSE registry table itself, not a copy, so do not alter + -- it unintentionally. + -- -- @param host A host table for the target host -- @param port (Optional) If omitted, all of the instances for the host -- will be returned. @@ -2570,12 +2594,12 @@ Helper = nmap.registry.mssql.instances = nmap.registry.mssql.instances or {} nmap.registry.mssql.instances[ host.ip ] = nmap.registry.mssql.instances[ host.ip ] or {} + local instances = nmap.registry.mssql.instances[ host.ip ] if ( not port ) then - local instances = nmap.registry.mssql.instances[ host.ip ] if ( instances and #instances == 0 ) then instances = nil end return instances else - for _, instance in ipairs( nmap.registry.mssql.instances[ host.ip ] ) do + for _, instance in ipairs(instances) do if ( instance.port and instance.port.number == port.number and instance.port.protocol == port.protocol ) then return { instance } @@ -2660,29 +2684,32 @@ Helper = DiscoverByTcp = function( host, port ) local version, instance, status -- Check to see if we've already discovered an instance on this port - instance = Helper.GetDiscoveredInstances( host, port ) - if ( not instance ) then - instance = SqlServerInstanceInfo:new() - instance.host = host - instance.port = port + local instance = Helper.GetDiscoveredInstances(host, port) + if instance then + return true, {instance} + end + instance = SqlServerInstanceInfo:new() + instance.host = host + instance.port = port - -- -sV may have gotten a version, but for now, it doesn't extract subBuild. - status, version = Helper.GetInstanceVersion( instance ) - if ( status ) then - Helper.AddOrMergeInstance( instance ) - -- The point of this wasn't to get the version, just to use the - -- pre-login packet to determine whether there was a SQL Server on - -- the port. However, since we have the version now, we'll store it. - instance.version = version - -- Give some version info back to Nmap - if ( instance.port and instance.version ) then - instance.version:PopulateNmapPortVersion( instance.port ) - nmap.set_port_version( host, instance.port) - end - end + -- -sV may have gotten a version, but for now, it doesn't extract subBuild. + status, version = Helper.GetInstanceVersion( instance ) + if not status then + return false, version end - return (instance ~= nil), { instance } + Helper.AddOrMergeInstance( instance ) + -- The point of this wasn't to get the version, just to use the + -- pre-login packet to determine whether there was a SQL Server on + -- the port. However, since we have the version now, we'll store it. + instance.version = version + -- Give some version info back to Nmap + if ( instance.port and instance.version ) then + instance.version:PopulateNmapPortVersion( instance.port ) + nmap.set_port_version( host, instance.port) + end + + return true, { instance } end, --- Attempts to discover SQL Server instances listening on default named @@ -2743,48 +2770,37 @@ Helper = end nmap.registry.mssql.discovery_performed[ host.ip ] = false - -- Check all ports that -sV discovered - -- First SSRP browser ports, then TCP instances - for _, c in ipairs({ - {proto="udp", name="ms-sql-m", method="DiscoverBySsrp"}, - {proto="tcp", name="ms-sql-s", method="DiscoverByTcp"}, - }) do - -- (no need to check open|filtered because -sV marks it as open if it gets a response) - local port = nmap.get_ports(host, nil, c.proto, "open") - while port do - if port.version and port.version.name == c.name then - Helper[c.method]( host, port ) - end - port = nmap.get_ports(host, port, c.proto, "open") + -- First, do SSRP discovery. Check any open (got response) ports first: + local port = nmap.get_ports(host, nil, "udp", "open") + while port do + if port.version and port.version.name == "ms-sql-m" then + Helper.DiscoverBySsrp(host, port) end + port = nmap.get_ports(host, port, "udp", "open") + end + -- Then check if default SSRP port hasn't been done yet. + port = nmap.get_port_state(host, SSRP.PORT) + if not port or port.state == "open|filtered" then + -- Either it wasn't scanned or it wasn't strictly "open" so we missed it above + Helper.DiscoverBySsrp(host, port) + end + + -- Next, do TCP discovery. Check any ports with an appropriate service name + port = nmap.get_ports(host, nil, "tcp", "open") + while port do + if port.version and port.version.name == "ms-sql-s" then + Helper.DiscoverByTcp(host, port) + end + port = nmap.get_ports(host, port, "tcp", "open") end - local sqlDefaultPort = nmap.get_port_state( host, {number = 1433, protocol = "tcp"} ) or {number = 1433, protocol = "tcp"} - local sqlBrowserPort = nmap.get_port_state( host, {number = 1434, protocol = "udp"} ) or {number = 1434, protocol = "udp"} - local smbPort -- smb.get_port() will return nil if no SMB port was scanned OR if SMB ports were scanned but none was open - local smbPortNumber = smb.get_port( host ) - if ( smbPortNumber ) then - smbPort = nmap.get_port_state( host, {number = smbPortNumber, protocol = "tcp"} ) - -- There's no use in manually setting an SMB port; if no SMB port was - -- scanned and found open, the SMB library won't work + if smb.get_port(host) then + Helper.DiscoverBySmb( host ) end - -- if the user has specified ports, we'll check those too - local targetInstancePorts = stdnse.get_script_args( "mssql.instance-port" ) - if ( sqlBrowserPort and sqlBrowserPort.state ~= "closed" ) then - Helper.DiscoverBySsrp( host, sqlBrowserPort ) - end - if ( sqlDefaultPort and sqlDefaultPort.state ~= "closed" ) then - Helper.DiscoverByTcp( host, sqlDefaultPort ) - end - if ( smbPort ) then - Helper.DiscoverBySmb( host, smbPort ) - end + -- if the user has specified ports, we'll check those too if ( targetInstancePorts ) then - if ( type( targetInstancePorts ) == "string" ) then - targetInstancePorts = { targetInstancePorts } - end for _, portNumber in ipairs( targetInstancePorts ) do portNumber = tonumber( portNumber ) Helper.DiscoverByTcp( host, {number = portNumber, protocol = "tcp"} ) @@ -3142,27 +3158,18 @@ Helper = -- more SqlServerInstanceInfo objects. If status is false, this will be -- an error message. GetTargetInstances = function( host, port ) - if ( port ) then - local status = true - local instance = Helper.GetDiscoveredInstances( host, port ) + -- Perform discovery. This won't do anything if it's already been done. + -- It's important because otherwise we might miss some ports when not using -sV + Helper.Discover( host ) - if ( not instance ) then - status, instance = Helper.DiscoverByTcp( host, port ) - end - if ( instance ) then - return true, instance + if ( port ) then + local status, instances = Helper.GetDiscoveredInstances(host, port) + if status then + return true, instances else return false, "No SQL Server instance detected on this port" end else - local targetInstanceNames = stdnse.get_script_args( "mssql.instance-name" ) - local targetInstancePorts = stdnse.get_script_args( "mssql.instance-port" ) - local targetAllInstances = stdnse.get_script_args( "mssql.instance-all" ) - - if ( targetInstanceNames and targetInstancePorts ) then - return false, "Connections can be made either by instance name or port." - end - if ( targetAllInstances and ( targetInstanceNames or targetInstancePorts ) ) then return false, "All instances cannot be specified together with an instance name or port." end @@ -3171,51 +3178,43 @@ Helper = return false, "No instance(s) specified." end - -- Perform discovery. This won't do anything if it's already been done. - Helper.Discover( host ) - local instanceList = Helper.GetDiscoveredInstances( host ) if ( not instanceList ) then return false, "No instances found on target host" end local targetInstances = {} - if ( targetAllInstances ) then - targetInstances = instanceList - else - -- We want an easy way to look up whether an instance's name was - -- in our target list. So, we'll make a table of { instanceName = true, ... } - local temp = {} - if ( targetInstanceNames ) then - if ( type( targetInstanceNames ) == "string" ) then - targetInstanceNames = { targetInstanceNames } - end - for _, instanceName in ipairs( targetInstanceNames ) do - temp[ string.upper( instanceName ) ] = true - end - end - targetInstanceNames = temp - -- Do the same for the target ports - temp = {} - if ( targetInstancePorts ) then - if ( type( targetInstancePorts ) == "string" ) then - targetInstancePorts = { targetInstancePorts } + for _, instance in ipairs( instanceList ) do + repeat -- just so we can use break + if instance.port then + local scanport = nmap.get_port_state(host, instance.port) + -- If scanned-ports-only and it's on a non-scanned port + if (SCANNED_PORTS_ONLY and not scanport) + -- or if a portrule script will run on it + or (scanport and scanport.state == "open") then + break -- not interested + end + -- If they want everything + if targetAllInstances or + -- or if it's in the instance-port arg + (targetInstancePorts and + tableaux.contains(targetInstancePorts, instance.port.number)) then + -- keep it and move on + targetInstances[#targetInstances+1] = instance + break + end end - for _, portNumber in ipairs( targetInstancePorts ) do - portNumber = tonumber( portNumber ) - temp[portNumber] = true + -- If they want everything + if targetAllInstances or + -- or if it's in the instance-name arg + (instance.instanceName and targetInstanceNames and + tableaux.contains(targetInstanceNames, string.upper(instance.instanceName))) then + --keep it and move on + targetInstances[#targetInstances+1] = instance + break end - end - targetInstancePorts = temp - - for _, instance in ipairs( instanceList ) do - if ( instance.instanceName and targetInstanceNames[ string.upper( instance.instanceName ) ] ) then - table.insert( targetInstances, instance ) - elseif ( instance.port and targetInstancePorts[ tonumber( instance.port.number ) ] ) then - table.insert( targetInstances, instance ) - end - end + until false end if ( #targetInstances > 0 ) then @@ -3232,14 +3231,17 @@ Helper = -- the database when normal connection attempts fail, for example, when -- the server is hanging, out of memory or other bad states. -- - -- @param host Host table as received by the script action function - -- @param instanceName the instance name to probe for a DAC port + -- @param instance the SqlServerInstanceInfo object to probe for a DAC port -- @return number containing the DAC port on success or nil on failure - DiscoverDACPort = function(host, instanceName) - local socket = nmap.new_socket() + DiscoverDACPort = function(instance) + local instanceName = instance.instanceName or instance.pipeName + if not instanceName then + return nil + end + local socket = nmap.new_socket("udp") socket:set_timeout(5000) - if ( not(socket:connect(host, 1434, "udp")) ) then + if ( not(socket:connect(instance.host, 1434, "udp")) ) then return false, "Failed to connect to sqlbrowser service" end @@ -3249,11 +3251,10 @@ Helper = end local status, data = socket:receive_buf(match.numbytes(6), true) + socket:close() if ( not(status) ) then - socket:close() return nil end - socket:close() if ( #data < 6 ) then return nil @@ -3261,42 +3262,47 @@ Helper = return string.unpack("mssql.instance - -- script argument. + --- Returns an action, portrule, and hostrule for standard SQL Server scripts -- - -- However, if a previous script has failed to find any - -- SQL Server instances on the host, the hostrule function will return - -- false to keep further scripts from running unnecessarily on that host. + -- The action function performs discovery if necessary and dispatches the + -- process_instance function on all discovered instances. -- - -- @return A hostrule function (use as hostrule = mssql.GetHostrule_Standard()) - GetHostrule_Standard = function() - return function( host ) - if ( stdnse.get_script_args( {"mssql.instance-all", "mssql.instance-name", "mssql.instance-port"} ) ~= nil ) then - if ( Helper.WasDiscoveryPerformed( host ) ) then - return Helper.GetDiscoveredInstances( host ) ~= nil - else - return true - end - else - return false + -- The portrule returns true if the port has been identified as "ms-sql-s" or + -- discovery has found an instance on that port. + -- + -- The hostrule returns true if any of the mssql.instance-* + -- script-args has been set and either a matching instance exists or + -- discovery has not yet been done. + -- @usage action, portrule, hostrule = mssql.Helper.InitScript(do_something) + -- + -- @param process_instance A function that takes a single parameter, a + -- SqlServerInstanceInfo object, and + -- returns output suitable for an action function to + -- return. + -- + -- @return An action function + -- @return A portrule function + -- @return A hostrule function + InitScript = function(process_instance) + local action = function(host, port) + local status, instances = Helper.GetTargetInstances(host, port) + if not status then + stdnse.debug1("GetTargetInstances: %s", instances) + return nil end + local output = {} + for _, instance in ipairs(instances) do + output[instance:GetName()] = process_instance(instance) + end + if #output > 0 then + return outlib.sorted_by_key(output) + end + return nil end - end, - - --- Returns a portrule for standard SQL Server scripts - -- - -- The portrule return true if BOTH of the following conditions are met: - -- * The port has been identified as "ms-sql-s" - -- * The mssql.instance script argument has NOT been used - -- - -- @return A portrule function (use as portrule = mssql.GetPortrule_Standard()) - GetPortrule_Standard = function() - return function( host, port ) - return ( shortport.service( "ms-sql-s" )(host, port) and - stdnse.get_script_args( {"mssql.instance-all", "mssql.instance-name", "mssql.instance-port"} ) == nil) - end + -- GetTargetInstances does the right thing depending on whether port is + -- provided, which corresponds to portrule vs hostrule. + return action, Helper.GetTargetInstances, Helper.GetTargetInstances end, } @@ -3371,11 +3377,11 @@ Auth = { Util = { --- Takes a table as returned by Query and does some fancy formatting - -- better suitable for stdnse.output_result + -- better suitable for stdnse.format_output -- -- @param tbl as received by Helper.Query -- @param with_headers boolean true if output should contain column headers - -- @return table suitable for stdnse.output_result + -- @return table suitable for stdnse.format_output FormatOutputTable = function ( tbl, with_headers ) local new_tbl = {} local col_names = {} diff --git a/scripts/ms-sql-brute.nse b/scripts/ms-sql-brute.nse index 86f2a5304..71d7f0d44 100644 --- a/scripts/ms-sql-brute.nse +++ b/scripts/ms-sql-brute.nse @@ -77,13 +77,7 @@ author = "Patrik Karlsson" license = "Same as Nmap--See https://nmap.org/book/man-legal.html" categories = {"brute", "intrusive"} -dependencies = {"ms-sql-empty-password"} - - - -hostrule = mssql.Helper.GetHostrule_Standard() -portrule = mssql.Helper.GetPortrule_Standard() - +dependencies = {"broadcast-ms-sql-discover", "ms-sql-empty-password"} --- Returns formatted output for the given instance local function create_instance_output_table( instance ) @@ -132,7 +126,7 @@ local function create_instance_output_table( instance ) end end - return instanceOutput + return stdnse.format_output(true, instanceOutput) end @@ -199,7 +193,7 @@ local function test_credentials( instance, helper, username, password ) end --- Processes a single instance, attempting to detect an empty password for "sa" -local function process_instance( instance ) +process_instance = function ( instance ) -- One of this script's features is that it will report an instance's -- in both the port-script results and the host-script results. In order to @@ -275,10 +269,10 @@ local function process_instance( instance ) end +local do_action +do_action, portrule, hostrule = mssql.Helper.InitScript(process_instance) -action = function( host, port ) - local scriptOutput = {} - local status, instanceList = mssql.Helper.GetTargetInstances( host, port ) +action = function(...) local domain, bruteWindows = stdnse.get_script_args("mssql.domain", "ms-sql-brute.brute-windows-accounts") @@ -292,16 +286,5 @@ action = function( host, port ) return ret end - if ( not status ) then - return stdnse.format_output( false, instanceList ) - else - for _, instance in pairs( instanceList ) do - local instanceOutput = process_instance( instance ) - if instanceOutput then - table.insert( scriptOutput, instanceOutput ) - end - end - end - - return stdnse.format_output( true, scriptOutput ) + return do_action(...) end diff --git a/scripts/ms-sql-config.nse b/scripts/ms-sql-config.nse index dad49cf91..83db0306f 100644 --- a/scripts/ms-sql-config.nse +++ b/scripts/ms-sql-config.nse @@ -70,12 +70,7 @@ license = "Same as Nmap--See https://nmap.org/book/man-legal.html" categories = {"discovery", "safe"} -dependencies = {"ms-sql-brute", "ms-sql-empty-password"} - - -hostrule = mssql.Helper.GetHostrule_Standard() -portrule = mssql.Helper.GetPortrule_Standard() - +dependencies = {"broadcast-ms-sql-discover", "ms-sql-brute", "ms-sql-empty-password"} --- Processes a set of instances local function process_instance( instance ) @@ -130,28 +125,8 @@ local function process_instance( instance ) helper:Disconnect() - local instanceOutput = {} - instanceOutput["name"] = string.format( "[%s]", instance:GetName() ) - table.insert( instanceOutput, result ) - - return instanceOutput + -- TODO: structured output instead of format_output + return stdnse.format_output(true, result) end - -action = function( host, port ) - local scriptOutput = {} - local status, instanceList = mssql.Helper.GetTargetInstances( host, port ) - - if ( not status ) then - return stdnse.format_output( false, instanceList ) - else - for _, instance in pairs( instanceList ) do - local instanceOutput = process_instance( instance ) - if instanceOutput then - table.insert( scriptOutput, instanceOutput ) - end - end - end - - return stdnse.format_output( true, scriptOutput ) -end +action, portrule, hostrule = mssql.Helper.InitScript(process_instance) diff --git a/scripts/ms-sql-dac.nse b/scripts/ms-sql-dac.nse index 121e7242b..ab8ee20a6 100644 --- a/scripts/ms-sql-dac.nse +++ b/scripts/ms-sql-dac.nse @@ -1,8 +1,6 @@ -local coroutine = require "coroutine" local mssql = require "mssql" local nmap = require "nmap" local stdnse = require "stdnse" -local table = require "table" description = [[ Queries the Microsoft SQL Browser service for the DAC (Dedicated Admin @@ -28,75 +26,60 @@ accessible or not. -- -- @output -- | ms-sql-dac: --- |_ Instance: SQLSERVER; DAC port: 1533 +-- | SQLSERVER: +-- | port: 1533 +-- |_ state: open -- author = "Patrik Karlsson" license = "Same as Nmap--See https://nmap.org/book/man-legal.html" categories = {"discovery", "safe"} -hostrule = function(host) - if ( mssql.Helper.WasDiscoveryPerformed( host ) ) then - return mssql.Helper.GetDiscoveredInstances( host ) ~= nil - else - local sqlBrowserPort = nmap.get_port_state( host, {number = 1434, protocol = "udp"} ) - if ( (stdnse.get_script_args( {"mssql.instance-all", "mssql.instance-name", "mssql.instance-port"} ) ~= nil) or - (sqlBrowserPort and (sqlBrowserPort.state == "open" or sqlBrowserPort.state == "open|filtered")) ) then - return true - end - end -end +dependencies = {"broadcast-ms-sql-discover"} local function checkPort(host, port) + local scanport = nmap.get_port_state(host, {number=port, protocol="tcp"}) + if scanport then + return scanport.state + end local s = nmap.new_socket() s:set_timeout(5000) - local status = s:connect(host, port, "tcp") + local status, err = s:connect(host, port, "tcp") s:close() - return status + return (status and "open" or "closed"), err end -local function discoverDAC(host, name, result) - local condvar = nmap.condvar(result) - stdnse.debug2("Discovering DAC port on instance: %s", name) - local port = mssql.Helper.DiscoverDACPort( host, name ) - if ( port ) then - if ( checkPort(host, port) ) then - table.insert(result, ("Instance: %s; DAC port: %s"):format(name, port)) - else - table.insert(result, ("Instance: %s; DAC port: %s (connection failed)"):format(name, port)) - end +local function discoverDAC(instance) + stdnse.debug2("Discovering DAC port on instance: %s", instance:GetName()) + local port = mssql.Helper.DiscoverDACPort(instance) + if not port then + return nil end - condvar "signal" + + local result = stdnse.output_table() + result.port = port + local state, err = checkPort(instance.host, port) + result.state = state + result.error = err + return result end -action = function( host ) - local result, threads = {}, {} - local condvar = nmap.condvar(result) +local lib_portrule, lib_hostrule +action, lib_portrule, lib_hostrule = mssql.Helper.InitScript(discoverDAC) - local status, instanceList = mssql.Helper.GetTargetInstances( host ) - -- if no instances were targeted, then display info on all - if ( not status ) then - mssql.Helper.Discover( host ) - instanceList = mssql.Helper.GetDiscoveredInstances( host ) - end - - for _, instance in ipairs(instanceList or {}) do - local name = instance:GetName():match("^[^\\]*\\(.*)$") - if ( name ) then - local co = stdnse.new_thread(discoverDAC, host, name, result) - threads[co] = true +local function rule_if_browser_open(lib_rule) + return function (host, ...) + if not lib_rule(host, ...) then + return false end + local bport = nmap.get_port_state(host, {number=1434, protocol="udp"}) + -- If port is nil, we don't know the state + return bport == nil or ( + -- we know the state, so it has to be a good one + bport.state == "open" or bport.state == "open|filtered" + ) end - - while(next(threads)) do - for t in pairs(threads) do - threads[t] = ( coroutine.status(t) ~= "dead" ) and true or nil - end - if ( next(threads) ) then - condvar "wait" - end - end - - return stdnse.format_output( true, result ) end +portrule = rule_if_browser_open(lib_portrule) +hostrule = rule_if_browser_open(lib_hostrule) diff --git a/scripts/ms-sql-dump-hashes.nse b/scripts/ms-sql-dump-hashes.nse index 7993dc037..c895b02ee 100644 --- a/scripts/ms-sql-dump-hashes.nse +++ b/scripts/ms-sql-dump-hashes.nse @@ -40,29 +40,19 @@ author = "Patrik Karlsson" license = "Same as Nmap--See https://nmap.org/book/man-legal.html" categories = {"auth", "discovery", "safe"} - -dependencies = {"ms-sql-brute", "ms-sql-empty-password"} - -hostrule = mssql.Helper.GetHostrule_Standard() -portrule = mssql.Helper.GetPortrule_Standard() +dependencies = {"broadcast-ms-sql-discover", "ms-sql-brute", "ms-sql-empty-password"} local function process_instance(instance) local helper = mssql.Helper:new() local status, errorMessage = helper:ConnectEx( instance ) if ( not(status) ) then - return false, { - ['name'] = string.format( "[%s]", instance:GetName() ), - "ERROR: " .. errorMessage - } + return "ERROR: " .. errorMessage end status, errorMessage = helper:LoginEx( instance ) if ( not(status) ) then - return false, { - ['name'] = string.format( "[%s]", instance:GetName() ), - "ERROR: " .. errorMessage - } + return "ERROR: " .. errorMessage end local result @@ -83,12 +73,7 @@ local function process_instance(instance) end helper:Disconnect() - local instanceOutput = {} - instanceOutput["name"] = string.format( "[%s]", instance:GetName() ) - table.insert( instanceOutput, output ) - - return true, instanceOutput - + return output end -- Saves the hashes to file @@ -110,31 +95,19 @@ local function saveToFile(filename, response) return true end -action = function( host, port ) - local dir = stdnse.get_script_args("ms-sql-dump-hashes.dir") - local scriptOutput = {} - local status, instanceList = mssql.Helper.GetTargetInstances( host, port ) +local dir = stdnse.get_script_args("ms-sql-dump-hashes.dir") - if ( not status ) then - return stdnse.format_output( false, instanceList ) - else - for _, instance in pairs( instanceList ) do - local status, instanceOutput = process_instance( instance ) - if ( status ) then - local filename - if ( dir ) then - local instance = instance:GetName():match("%\\+(.+)$") or instance:GetName() - filename = dir .. "/" .. stringaux.filename_escape(("%s_%s_ms-sql_hashes.txt"):format(host.ip, instance)) - saveToFile(filename, instanceOutput[1]) - end - end - table.insert( scriptOutput, instanceOutput ) - end +local function process_and_save (instance) + local instanceOutput = process_instance( instance ) + if type(instanceOutput) == "table" then + local inst = instance:GetName():gsub(".*\\", "") + local filename = dir .. "/" .. stringaux.filename_escape( + ("%s_%s_ms-sql_hashes.txt"):format(instance.host.ip, inst)) + saveToFile(filename, instanceOutput) end - - if ( #scriptOutput == 0 ) then return end - - local output = ( #scriptOutput > 1 and scriptOutput or scriptOutput[1] ) - - return stdnse.format_output( true, output ) + return instanceOutput end + +local do_instance = dir and process_and_save or process_instance + +action, portrule, hostrule = mssql.Helper.InitScript(do_instance) diff --git a/scripts/ms-sql-empty-password.nse b/scripts/ms-sql-empty-password.nse index d35e3f7c8..0b52db510 100644 --- a/scripts/ms-sql-empty-password.nse +++ b/scripts/ms-sql-empty-password.nse @@ -59,9 +59,7 @@ author = "Patrik Karlsson" license = "Same as Nmap--See https://nmap.org/book/man-legal.html" categories = {"auth","intrusive"} - -hostrule = mssql.Helper.GetHostrule_Standard() -portrule = mssql.Helper.GetPortrule_Standard() +dependencies = {"broadcast-ms-sql-discover"} local function test_credentials( instance, helper, username, password ) local database = "tempdb" @@ -150,7 +148,6 @@ local function process_instance( instance ) local instanceOutput if ( instance.ms_sql_empty ) then instanceOutput = {} - instanceOutput["name"] = string.format( "[%s]", instance:GetName() ) for _, message in ipairs( instance.ms_sql_empty ) do table.insert( instanceOutput, message ) end @@ -163,21 +160,4 @@ local function process_instance( instance ) end - -action = function( host, port ) - local scriptOutput = {} - local status, instanceList = mssql.Helper.GetTargetInstances( host, port ) - - if ( not status ) then - return stdnse.format_output( false, instanceList ) - else - for _, instance in pairs( instanceList ) do - local instanceOutput = process_instance( instance ) - if instanceOutput then - table.insert( scriptOutput, instanceOutput ) - end - end - end - - return stdnse.format_output( true, scriptOutput ) -end +action, portrule, hostrule = mssql.Helper.InitScript(process_instance) diff --git a/scripts/ms-sql-hasdbaccess.nse b/scripts/ms-sql-hasdbaccess.nse index 7e362c205..3d28f3771 100644 --- a/scripts/ms-sql-hasdbaccess.nse +++ b/scripts/ms-sql-hasdbaccess.nse @@ -73,11 +73,7 @@ license = "Same as Nmap--See https://nmap.org/book/man-legal.html" categories = {"auth", "discovery","safe"} -dependencies = {"ms-sql-brute", "ms-sql-empty-password"} - - -hostrule = mssql.Helper.GetHostrule_Standard() -portrule = mssql.Helper.GetPortrule_Standard() +dependencies = {"broadcast-ms-sql-discover", "ms-sql-brute", "ms-sql-empty-password"} local function process_instance( instance ) @@ -146,30 +142,9 @@ local function process_instance( instance ) end end - - local instanceOutput = {} - instanceOutput["name"] = string.format( "[%s]", instance:GetName() ) - table.insert( instanceOutput, output ) - - return instanceOutput - + -- TODO: structured output, not format_output + return stdnse.format_output(true, output) end -action = function( host, port ) - local scriptOutput = {} - local status, instanceList = mssql.Helper.GetTargetInstances( host, port ) - - if ( not status ) then - return stdnse.format_output( false, instanceList ) - else - for _, instance in pairs( instanceList ) do - local instanceOutput = process_instance( instance ) - if instanceOutput then - table.insert( scriptOutput, instanceOutput ) - end - end - end - - return stdnse.format_output( true, scriptOutput ) -end +action, portrule, hostrule = mssql.Helper.InitScript(process_instance) diff --git a/scripts/ms-sql-info.nse b/scripts/ms-sql-info.nse index df76b6737..445e6941d 100644 --- a/scripts/ms-sql-info.nse +++ b/scripts/ms-sql-info.nse @@ -150,25 +150,7 @@ license = "Same as Nmap--See https://nmap.org/book/man-legal.html" categories = {"default", "discovery", "safe"} - -hostrule = function(host) - if ( mssql.Helper.WasDiscoveryPerformed( host ) ) then - return mssql.Helper.GetDiscoveredInstances( host ) ~= nil - else - local sqlDefaultPort = nmap.get_port_state( host, {number = 1433, protocol = "tcp"} ) - local sqlBrowserPort = nmap.get_port_state( host, {number = 1434, protocol = "udp"} ) - -- smb.get_port() will return nil if no SMB port was scanned OR if SMB ports were scanned but none was open - local smbPortNumber = smb.get_port( host ) - - if ( (stdnse.get_script_args( {"mssql.instance-all", "mssql.instance-name", "mssql.instance-port"} ) ~= nil) or - (sqlBrowserPort and (sqlBrowserPort.state == "open" or sqlBrowserPort.state == "open|filtered")) or - (sqlDefaultPort and (sqlDefaultPort.state == "open" or sqlDefaultPort.state == "open|filtered")) or - (smbPortNumber ~= nil) ) then - return true - end - end -end - +dependencies = {"broadcast-ms-sql-discover"} --- Returns formatted output for the given version data local function create_version_output_table( versionInfo ) @@ -247,33 +229,9 @@ local function process_instance( instance ) end - -action = function( host ) - local scriptOutput = stdnse.output_table() - - local status, instanceList = mssql.Helper.GetTargetInstances( host ) - -- if no instances were targeted, then display info on all - if ( not status ) then - mssql.Helper.Discover( host ) - instanceList = mssql.Helper.GetDiscoveredInstances( host ) - end - - - if ( not instanceList ) then - return stdnse.format_output( false, instanceList or "" ) - else - for _, instance in ipairs( instanceList ) do - if instance.serverName then - scriptOutput["Windows server name"] = instance.serverName - break - end - end - for _, instance in pairs( instanceList ) do - process_instance( instance ) - scriptOutput[instance:GetName()] = create_instance_output_table( instance ) - end - end - - return scriptOutput +local function do_instance (instance) + process_instance( instance ) + return create_instance_output_table( instance ) end +action, portrule, hostrule = mssql.Helper.InitScript(do_instance) diff --git a/scripts/ms-sql-ntlm-info.nse b/scripts/ms-sql-ntlm-info.nse index 4dd987d3f..6ff3db5a6 100644 --- a/scripts/ms-sql-ntlm-info.nse +++ b/scripts/ms-sql-ntlm-info.nse @@ -1,7 +1,6 @@ local os = require "os" local datetime = require "datetime" local mssql = require "mssql" -local shortport = require "shortport" local stdnse = require "stdnse" local smbauth = require "smbauth" local string = require "string" @@ -46,9 +45,9 @@ author = "Justin Cacak" license = "Same as Nmap--See https://nmap.org/book/man-legal.html" categories = {"default", "discovery", "safe"} -portrule = shortport.port_or_service(1433, "ms-sql-s") +dependencies = {"broadcast-ms-sql-discover"} -action = function(host, port) +local do_action = function(host, port) local output = stdnse.output_table() @@ -127,3 +126,9 @@ action = function(host, port) return output end + +local function process_instance(instance) + return do_action(instance.host, instance.port) +end + +action, portrule = mssql.Helper.InitScript(process_instance) diff --git a/scripts/ms-sql-query.nse b/scripts/ms-sql-query.nse index aa8904111..8096da1d3 100644 --- a/scripts/ms-sql-query.nse +++ b/scripts/ms-sql-query.nse @@ -60,10 +60,7 @@ license = "Same as Nmap--See https://nmap.org/book/man-legal.html" categories = {"discovery", "safe"} -dependencies = {"ms-sql-brute", "ms-sql-empty-password"} - -hostrule = mssql.Helper.GetHostrule_Standard() -portrule = mssql.Helper.GetPortrule_Standard() +dependencies = {"broadcast-ms-sql-discover", "ms-sql-brute", "ms-sql-empty-password"} --- local function process_instance( instance ) @@ -92,30 +89,18 @@ local function process_instance( instance ) result = mssql.Util.FormatOutputTable( result, true ) result["name"] = string.format( "Query: %s", query ) end - local instanceOutput = {} - instanceOutput["name"] = string.format( "[%s]", instance:GetName() ) - table.insert( instanceOutput, result ) - return instanceOutput + return result end +local do_action +do_action, portrule, hostrule = mssql.Helper.InitScript(process_instance) -action = function( host, port ) - local scriptOutput = {} - local status, instanceList = mssql.Helper.GetTargetInstances( host, port ) +action = function(...) + local scriptOutput = do_action(...) - if ( not status ) then - return stdnse.format_output( false, instanceList ) - else - for _, instance in pairs( instanceList ) do - local instanceOutput = process_instance( instance ) - if instanceOutput then - table.insert( scriptOutput, instanceOutput ) - end - end - if ( not( stdnse.get_script_args( {'ms-sql-query.query', 'mssql-query.query' } ) ) ) then - table.insert(scriptOutput, 1, "(Use --script-args=ms-sql-query.query='' to change query.)") - end + if ( not( stdnse.get_script_args( {'ms-sql-query.query', 'mssql-query.query' } ) ) ) then + table.insert(scriptOutput, 1, "(Use --script-args=ms-sql-query.query='' to change query.)") end return stdnse.format_output( true, scriptOutput ) diff --git a/scripts/ms-sql-tables.nse b/scripts/ms-sql-tables.nse index dbeb3e538..caf0a822a 100644 --- a/scripts/ms-sql-tables.nse +++ b/scripts/ms-sql-tables.nse @@ -98,10 +98,7 @@ license = "Same as Nmap--See https://nmap.org/book/man-legal.html" categories = {"discovery", "safe"} -dependencies = {"ms-sql-brute", "ms-sql-empty-password"} - -hostrule = mssql.Helper.GetHostrule_Standard() -portrule = mssql.Helper.GetPortrule_Standard() +dependencies = {"broadcast-ms-sql-discover", "ms-sql-brute", "ms-sql-empty-password"} local function process_instance( instance ) @@ -248,25 +245,9 @@ local function process_instance( instance ) instanceOutput["name"] = string.format( "[%s]", instance:GetName() ) table.insert( instanceOutput, output ) - return instanceOutput + return stdnse.format_ouptut(true, instanceOutput) end -action = function( host, port ) - local scriptOutput = {} - local status, instanceList = mssql.Helper.GetTargetInstances( host, port ) - - if ( not status ) then - return stdnse.format_output( false, instanceList ) - else - for _, instance in pairs( instanceList ) do - local instanceOutput = process_instance( instance ) - if instanceOutput then - table.insert( scriptOutput, instanceOutput ) - end - end - end - - return stdnse.format_output( true, scriptOutput ) -end +action, portrule, hostrule = mssql.Helper.InitScript(process_instance) diff --git a/scripts/ms-sql-xp-cmdshell.nse b/scripts/ms-sql-xp-cmdshell.nse index ab1632b4c..b5dc4a3f3 100644 --- a/scripts/ms-sql-xp-cmdshell.nse +++ b/scripts/ms-sql-xp-cmdshell.nse @@ -86,10 +86,7 @@ license = "Same as Nmap--See https://nmap.org/book/man-legal.html" categories = {"intrusive"} -dependencies = {"ms-sql-brute", "ms-sql-empty-password"} - -hostrule = mssql.Helper.GetHostrule_Standard() -portrule = mssql.Helper.GetPortrule_Standard() +dependencies = {"broadcast-ms-sql-discover", "ms-sql-brute", "ms-sql-empty-password"} local function process_instance( instance ) @@ -143,23 +140,13 @@ local function process_instance( instance ) end -action = function( host, port ) - local scriptOutput = {} - local status, instanceList = mssql.Helper.GetTargetInstances( host, port ) +local do_action +do_action, portrule, hostrule = mssql.Helper.InitScript(process_instance) - if ( not status ) then - return stdnse.format_output( false, instanceList ) - else - for _, instance in pairs( instanceList ) do - local instanceOutput = process_instance( instance ) - if instanceOutput then - table.insert( scriptOutput, instanceOutput ) - end - end - - if ( not(stdnse.get_script_args( {'ms-sql-xp-cmdshell.cmd', 'mssql-xp-cmdshell.cmd'} ) ) ) then - table.insert(scriptOutput, 1, "(Use --script-args=ms-sql-xp-cmdshell.cmd='' to change command.)") - end +action = function(...) + local scriptOutput = do_action(...) + if ( not(stdnse.get_script_args( {'ms-sql-xp-cmdshell.cmd', 'mssql-xp-cmdshell.cmd'} ) ) ) then + table.insert(scriptOutput, 1, "(Use --script-args=ms-sql-xp-cmdshell.cmd='' to change command.)") end return stdnse.format_output( true, scriptOutput )