mirror of
https://github.com/nmap/nmap.git
synced 2025-12-06 04:31:29 +00:00
Change how ms-sql NSE scripts run
MS SQL NSE scripts run on database instances, which can be TCP or named pipes. With this change, all TCP instances on scanned ports will have script output attached under the port as a portrule script. Named pipe instances and TCP instances on unscanned ports will be displayed in the hostrule script output at the end of the host's output. Utility function mssql.Helper.InitScript makes it easy to write scripts that just work on a per-instance basis, without bothering where to put the output. Discovery will be done once per host, regardless of how many scripts are run, and can be guaranteed to be done before the script's action takes place.
This commit is contained in:
284
nselib/mssql.lua
284
nselib/mssql.lua
@@ -74,12 +74,19 @@
|
||||
-- argument is not given but <code>mssql.username</code>, 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,11 +158,24 @@ do
|
||||
end
|
||||
MSSQL_TIMEOUT = timeout
|
||||
|
||||
SCANNED_PORTS_ONLY = false
|
||||
if ( stdnse.get_script_args( "mssql.scanned-ports-only" ) ) then
|
||||
SCANNED_PORTS_ONLY = true
|
||||
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 {}
|
||||
|
||||
if ( not port ) then
|
||||
local instances = nmap.registry.mssql.instances[ host.ip ]
|
||||
if ( not port ) then
|
||||
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,15 +2684,20 @@ 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
|
||||
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
|
||||
if not status then
|
||||
return false, version
|
||||
end
|
||||
|
||||
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
|
||||
@@ -2679,10 +2708,8 @@ Helper =
|
||||
instance.version:PopulateNmapPortVersion( instance.port )
|
||||
nmap.set_port_version( host, instance.port)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return (instance ~= nil), { instance }
|
||||
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")
|
||||
-- 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 == c.name then
|
||||
Helper[c.method]( host, port )
|
||||
if port.version and port.version.name == "ms-sql-m" then
|
||||
Helper.DiscoverBySsrp(host, port)
|
||||
end
|
||||
port = nmap.get_ports(host, port, c.proto, "open")
|
||||
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 }
|
||||
end
|
||||
for _, portNumber in ipairs( targetInstancePorts ) do
|
||||
portNumber = tonumber( portNumber )
|
||||
temp[portNumber] = true
|
||||
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 )
|
||||
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
|
||||
-- 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
|
||||
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 <code>SqlServerInstanceInfo</code> 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)
|
||||
if ( not(status) ) then
|
||||
socket:close()
|
||||
if ( not(status) ) then
|
||||
return nil
|
||||
end
|
||||
socket:close()
|
||||
|
||||
if ( #data < 6 ) then
|
||||
return nil
|
||||
@@ -3261,42 +3262,47 @@ Helper =
|
||||
return string.unpack("<I2", data, 5)
|
||||
end,
|
||||
|
||||
--- Returns a hostrule for standard SQL Server scripts, which will return
|
||||
-- true if one or more instances have been targeted with the <code>mssql.instance</code>
|
||||
-- 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 <code>hostrule = mssql.GetHostrule_Standard()</code>)
|
||||
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
|
||||
-- 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 <code>mssql.instance-*</code>
|
||||
-- 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
|
||||
-- <code>SqlServerInstanceInfo</code> 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
|
||||
else
|
||||
return false
|
||||
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 <code>mssql.instance</code> script argument has NOT been used
|
||||
--
|
||||
-- @return A portrule function (use as <code>portrule = mssql.GetPortrule_Standard()</code>)
|
||||
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 <code>stdnse.output_result</code>
|
||||
-- better suitable for <code>stdnse.format_output</code>
|
||||
--
|
||||
-- @param tbl as received by <code>Helper.Query</code>
|
||||
-- @param with_headers boolean true if output should contain column headers
|
||||
-- @return table suitable for <code>stdnse.output_result</code>
|
||||
-- @return table suitable for <code>stdnse.format_output</code>
|
||||
FormatOutputTable = function ( tbl, with_headers )
|
||||
local new_tbl = {}
|
||||
local col_names = {}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
end
|
||||
condvar "signal"
|
||||
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
|
||||
|
||||
action = function( host )
|
||||
local result, threads = {}, {}
|
||||
local condvar = nmap.condvar(result)
|
||||
|
||||
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 )
|
||||
local result = stdnse.output_table()
|
||||
result.port = port
|
||||
local state, err = checkPort(instance.host, port)
|
||||
result.state = state
|
||||
result.error = err
|
||||
return result
|
||||
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 lib_portrule, lib_hostrule
|
||||
action, lib_portrule, lib_hostrule = mssql.Helper.InitScript(discoverDAC)
|
||||
|
||||
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
|
||||
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)
|
||||
|
||||
@@ -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 )
|
||||
|
||||
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 )
|
||||
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
|
||||
return instanceOutput
|
||||
end
|
||||
|
||||
if ( #scriptOutput == 0 ) then return end
|
||||
local do_instance = dir and process_and_save or process_instance
|
||||
|
||||
local output = ( #scriptOutput > 1 and scriptOutput or scriptOutput[1] )
|
||||
|
||||
return stdnse.format_output( true, output )
|
||||
end
|
||||
action, portrule, hostrule = mssql.Helper.InitScript(do_instance)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
local function do_instance (instance)
|
||||
process_instance( instance )
|
||||
scriptOutput[instance:GetName()] = create_instance_output_table( instance )
|
||||
end
|
||||
end
|
||||
|
||||
return scriptOutput
|
||||
return create_instance_output_table( instance )
|
||||
end
|
||||
|
||||
action, portrule, hostrule = mssql.Helper.InitScript(do_instance)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,31 +89,19 @@ 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='<QUERY>' to change query.)")
|
||||
end
|
||||
end
|
||||
|
||||
return stdnse.format_output( true, scriptOutput )
|
||||
end
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,24 +140,14 @@ 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
|
||||
local do_action
|
||||
do_action, portrule, hostrule = mssql.Helper.InitScript(process_instance)
|
||||
|
||||
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='<CMD>' to change command.)")
|
||||
end
|
||||
end
|
||||
|
||||
return stdnse.format_output( true, scriptOutput )
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user