diff --git a/nselib/shortport.lua b/nselib/shortport.lua
index 455a69b36..5da0ca101 100644
--- a/nselib/shortport.lua
+++ b/nselib/shortport.lua
@@ -25,6 +25,29 @@ local function includes(t, value)
return false
end
+-- Just like includes, but can match simple port ranges
+local function port_includes(t, value)
+ for _, elem in ipairs(t) do
+ if elem == value then
+ return true
+ elseif type(elem) == "string" then
+ local pstart, pend = elem:match("^(%d+)%-(%d+)$")
+ if not pstart then
+ pstart = elem:match("^(%d+)$")
+ pend = pstart
+ end
+ pstart, pend = tonumber(pstart), tonumber(pend)
+ assert(pstart,"Incorrect port range specification.")
+ assert(pstart<=pend,"Incorrect port range specification, the starting port should have a smaller value than the ending port.")
+ assert(pstart>-1 and pend<65536, "Port range number out of range (0-65535).")
+ if value >= pstart and value <= pend then
+ return true
+ end
+ end
+ end
+ return false
+end
+
--- Check if the port and its protocol are in the exclude directive.
--
-- @param port A port number.
@@ -60,7 +83,7 @@ portnumber = function(ports, protos, states)
end
return function(host, port)
- return includes(ports, port.number)
+ return port_includes(ports, port.number)
and includes(protos, port.protocol)
and includes(states, port.state)
end
@@ -281,4 +304,54 @@ function ssl(host, port)
return false
end
+
+--- Return a portrule that returns true when given an open port matching a port range
+--
+--@param range A port range string in Nmap standard format (ex. "T:80,1-30,U:31337,21-25")
+--@return Function for the portrule.
+function port_range(range)
+ assert(type(range)=="string" and range~="","Incorrect port range specification.")
+
+ local ports = {
+ tcp = {},
+ udp = {},
+ }
+ local proto = "both"
+ local pos = 1
+ repeat
+ local i, j, protspec = range:find("^%s*([TU:]+)", pos)
+ if i then
+ pos = j + 1
+ if protspec == "U:" then
+ proto = "udp"
+ elseif protspec == "T:" then
+ proto = "tcp"
+ else
+ assert(protspec == "", "Incorrect port range specification.")
+ end
+ end
+ repeat
+ local i, j, portspec = range:find("^%s*([%d%-]+),?", pos)
+ if not i then break end
+ pos = j + 1
+ portspec = tonumber(portspec) or portspec
+ if proto == "both" then
+ local ttab = ports.tcp
+ ttab[#ttab+1] = portspec
+ local utab = ports.udp
+ utab[#utab+1] = portspec
+ else
+ local ptab = ports[proto]
+ ptab[#ptab+1] = portspec
+ end
+ until pos >= #range
+ until pos >= #range
+
+ local tcp_rule = portnumber(ports.tcp, "tcp")
+ local udp_rule = portnumber(ports.udp, "udp")
+ return function(host, port)
+ return tcp_rule(host, port) or udp_rule(host, port)
+ end
+end
+
return _ENV;
diff --git a/nselib/stdnse.lua b/nselib/stdnse.lua
index 3e6d171d2..e8f5fc2d1 100644
--- a/nselib/stdnse.lua
+++ b/nselib/stdnse.lua
@@ -944,77 +944,6 @@ do end -- no function here, see nse_main.lua
do end -- no function here, see nse_main.lua
-
----Checks if the port is in the port range
---
--- For example, calling:
--- in_port_range({number=31337,protocol="udp"},"T:15,50-75,U:31334-31339")
--- would result in a true value
---@param port a port structure containing keys port number(number) and protocol(string)
---@param port_range a port range string in Nmap standard format (ex. "T:80,1-30,U:31337,21-25")
---@returns boolean indicating whether the port is in the port range
-function in_port_range(port,port_range)
- assert(port and type(port.number)=="number" and type(port.protocol)=="string" and
- (port.protocol=="udp" or port.protocol=="tcp"),"Port structure missing or invalid: port={ number=, protocol= }")
- assert((type(port_range)=="string" or type(port_range)=="number") and port_range~="","Incorrect port range specification.")
-
- -- Proto - true for TCP, false for UDP
- local proto
- if(port.protocol=="tcp") then proto = true else proto = false end
-
- --TCP flag for iteration - true for TCP, false for UDP, if not specified we presume TCP
- local tcp_flag = true
-
- -- in case the port_range is a single number
- if type(port_range)=="number" then
- if proto and port_range==port.number then return true
- else return false
- end
- end
-
- --clean the string a bit
- port_range=port_range:gsub("%s+","")
-
- -- single_pr - single port range
- for i, single_pr in ipairs(strsplit(",",port_range)) do
- if single_pr:match("T:") then
- tcp_flag = true
- single_pr = single_pr:gsub("T:","")
- else
- if single_pr:match("U:") then
- tcp_flag = false
- single_pr = single_pr:gsub("U:","")
- end
- end
-
- -- compare ports only when the port's protocol is the same as
- -- the current single port range
- if tcp_flag == proto then
- local pone = single_pr:match("^(%d+)$")
- if pone then
- pone = tonumber(pone)
- assert(pone>-1 and pone<65536, "Port range number out of range (0-65535).")
-
- if pone == port.number then
- return true
- end
- else
- local pstart, pend = single_pr:match("^(%d+)%-(%d+)$")
- pstart, pend = tonumber(pstart), tonumber(pend)
- assert(pstart,"Incorrect port range specification.")
- assert(pstart<=pend,"Incorrect port range specification, the starting port should have a smaller value than the ending port.")
- assert(pstart>-1 and pstart<65536 and pend>-1 and pend<65536, "Port range number out of range (0-65535).")
-
- if port.number >=pstart and port.number <= pend then
- return true
- end
- end
- end
- end
- -- if no match is found then the port doesn't belong to the port_range
- return false
-end
-
--- Module function that mimics some behavior of Lua 5.1 module function.
--
-- This convenience function returns a module environment to set the _ENV
diff --git a/scripts/backorifice-brute.nse b/scripts/backorifice-brute.nse
index 8b27174d2..9f8372493 100644
--- a/scripts/backorifice-brute.nse
+++ b/scripts/backorifice-brute.nse
@@ -49,20 +49,16 @@ categories = {"intrusive", "brute"}
-- This portrule succeeds only when the open|filtered port is in the port range
-- which is specified by the ports script argument
portrule = function(host, port)
- if not stdnse.get_script_args(SCRIPT_NAME .. ".ports") then
- stdnse.debug3("Skipping '%s' %s, 'ports' argument is missing.",SCRIPT_NAME, SCRIPT_TYPE)
- return false
- end
local ports = stdnse.get_script_args(SCRIPT_NAME .. ".ports")
-
- --print out a debug message if port 31337/udp is open
- if port.number==31337 and port.protocol == "udp" and not(ports) then
- stdnse.debug1("Port 31337/udp is open. Possibility of version detection and password bruteforcing using the backorifice-brute script")
+ if not ports then
+ stdnse.verbose1("Skipping '%s' %s, 'ports' argument is missing.",SCRIPT_NAME, SCRIPT_TYPE)
return false
end
- return port.protocol == "udp" and stdnse.in_port_range(port, ports:gsub(",",",") ) and
+ -- ensure UDP
+ portarg = portarg:gsub("^[U:]*", "U:")
+ return port.protocol == "udp" and shortport.port_range(ports)(host, port) and
not(shortport.port_is_excluded(port.number,port.protocol))
end
diff --git a/scripts/banner.nse b/scripts/banner.nse
index e8ec7f162..1482d16df 100644
--- a/scripts/banner.nse
+++ b/scripts/banner.nse
@@ -1,6 +1,7 @@
local comm = require "comm"
local nmap = require "nmap"
local stdnse = require "stdnse"
+local shortport = require "shortport"
local table = require "table"
local U = require "lpeg-utility"
@@ -28,20 +29,15 @@ categories = {"discovery", "safe"}
local portarg = stdnse.get_script_args(SCRIPT_NAME .. ".ports")
-if portarg == "common" then
- portarg = "13,17,21-23,25,129,194,587,990,992,994,6667,6697"
-end
-
----
--- Script is executed for any TCP port.
-portrule = function( host, port )
- if port.protocol == "tcp" then
- if portarg then
- return stdnse.in_port_range(port, portarg)
- end
- return true
+if portarg then
+ if portarg == "common" then
+ portarg = "13,17,21-23,25,129,194,587,990,992,994,6667,6697"
end
- return false
+ -- ensure TCP
+ portarg = portarg:gsub("^[T:]*", "T:")
+ portrule = shortport.port_range(portarg)
+else
+ portrule = function(host, port) return port.protocol == "tcp" end
end