From e80976a13aedbba8cb058c834fada9f961d6d93f Mon Sep 17 00:00:00 2001 From: nnposter Date: Wed, 19 Apr 2017 18:00:36 +0000 Subject: [PATCH] Provides a common function, url.get_default_port(), for obtaining the default port number for a given scheme. Fixes #781 --- CHANGELOG | 6 ++- .../http-default-accounts-fingerprints.lua | 6 +-- nselib/http.lua | 37 ++++++------------- nselib/httpspider.lua | 13 +------ nselib/url.lua | 12 ++++++ scripts/http-backup-finder.nse | 9 +---- scripts/http-favicon.nse | 10 ++--- scripts/http-open-redirect.nse | 8 +--- scripts/http-phpself-xss.nse | 8 +--- scripts/http-unsafe-output-escaping.nse | 9 +---- 10 files changed, 41 insertions(+), 77 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index b2fe3fe93c..04e5e1be44 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,8 +1,10 @@ # Nmap Changelog ($Id$); -*-text-*- +o [NSE][GH#781] There is a new common function, url.get_default_port(), + to obtain the default port number for a given scheme. [nnposter] + o [NSE][GH#833] Function url.parse() now returns the port part as a number, - not a string, which eliminates various inconsistencies in scripts that - consume the function. [nnposter] + not a string. [nnposter] o [NSE][GH#854] New script smb-double-pulsar-backdoor detects the Shadow Brokers-leaked Double Pulsar backdoor in Windows SMB servers. [Andrew Orr] diff --git a/nselib/data/http-default-accounts-fingerprints.lua b/nselib/data/http-default-accounts-fingerprints.lua index 8ee6a0a81d..123fba6301 100644 --- a/nselib/data/http-default-accounts-fingerprints.lua +++ b/nselib/data/http-default-accounts-fingerprints.lua @@ -175,10 +175,8 @@ local function url_build_defaults (host, port, parsed) local parts = tcopy(parsed or {}) parts.host = parts.host or stdnse.get_hostname(host, port) parts.scheme = parts.scheme or shortport.ssl(host, port) and "https" or "http" - local pn = parts.port or port.number - if not (parts.scheme == "http" and pn == 80 - or parts.scheme == "https" and pn == 443) then - parts.port = pn + if not parts.port and port.number ~= url.get_default_port(parts.scheme) then + parts.port = port.number end return parts end diff --git a/nselib/http.lua b/nselib/http.lua index e625ea5bf8..be7ecc2c3f 100644 --- a/nselib/http.lua +++ b/nselib/http.lua @@ -157,6 +157,11 @@ local function table_augment(to, from) end end +--- Provide the default port for a given scheme. +-- The localization is necessary because functions in http.lua like to use +-- "url" as a local parameter +local get_default_port = url.get_default_port + --- Get a value suitable for the Host header field. -- See RFC 2616 sections 14.23 and 5.2. local function get_host_field(host, port) @@ -164,12 +169,11 @@ local function get_host_field(host, port) if type(port) == "number" then port = {number=port, protocol="tcp", state="open", version={}} end - local ssl = shortport.ssl(host, port) - local pn = port.number - if not ssl and pn == 80 or ssl and pn == 443 then + local scheme = shortport.ssl(host, port) and "https" or "http" + if port.number == get_default_port(scheme) then return stdnse.get_hostname(host) else - return stdnse.get_hostname(host) .. ":" .. pn + return stdnse.get_hostname(host) .. ":" .. port.number end end @@ -1496,14 +1500,7 @@ local redirect_ok_rules = { function (url, host, port) -- port fixup, adds default ports 80 and 443 in case no url.port was -- defined, we do this based on the url scheme - local url_port = url.port - if ( not(url_port) ) then - if ( url.scheme == "http" ) then - url_port = 80 - elseif( url.scheme == "https" ) then - url_port = 443 - end - end + local url_port = url.port or get_default_port(url.scheme) if not url_port or url_port == port.number then return true end @@ -1581,11 +1578,7 @@ function parse_redirect(host, port, path, response) u.path = ((u.path:sub(1,1) == "/" and "" ) or "/" ) .. u.path -- ensuring leading slash end -- do port fixup - if ( not(u.port) ) then - if ( u.scheme == "http" ) then u.port = 80 - elseif ( u.scheme == "https") then u.port = 443 - else u.port = port.number end - end + u.port = u.port or get_default_port(u.scheme) or port.number if ( not(u.path) ) then u.path = "/" end @@ -1679,15 +1672,7 @@ function get_url( u, options ) local port = {} port.service = parsed.scheme - port.number = parsed.port - - if not port.number then - if parsed.scheme == 'https' then - port.number = 443 - else - port.number = 80 - end - end + port.number = parsed.port or get_default_port(parsed.scheme) or 80 local path = parsed.path or "/" if parsed.query then diff --git a/nselib/httpspider.lua b/nselib/httpspider.lua index c153d9cec6..6b9d2991d0 100644 --- a/nselib/httpspider.lua +++ b/nselib/httpspider.lua @@ -233,9 +233,7 @@ LinkExtractor = { base_href = base_href .. '/' end - if ( ( base_url:getProto() == 'https' and base_url:getPort() == 443 ) or - ( base_url:getProto() == 'http' and base_url:getPort() == 80 ) ) then - + if base_url:getPort() == url.get_default_port(base_url:getProto()) then if ( leading_slash ) then return ("%s://%s/%s"):format(base_url:getProto(), base_url:getHost(), rel_url) else @@ -427,14 +425,7 @@ URL = { self.proto, self.host, self.port, self.file = self.raw:match("^(http[s]?)://([^:/]*)[:]?(%d*)") if ( self.proto and self.host ) then self.file = self.raw:match("^http[s]?://[^:/]*[:]?%d*(/[^#]*)") or '/' - self.port = tonumber(self.port) - if ( not(self.port) ) then - if ( self.proto:match("https") ) then - self.port = 443 - elseif ( self.proto:match("http")) then - self.port = 80 - end - end + self.port = tonumber(self.port) or url.get_default_port(self.proto) self.path = self.file:match("^([^?]*)[%?]?") self.dir = self.path:match("^(.+%/)") or "/" diff --git a/nselib/url.lua b/nselib/url.lua index 9a64f3d910..1618f7da15 100644 --- a/nselib/url.lua +++ b/nselib/url.lua @@ -373,4 +373,16 @@ function build_query(query) return table.concat(qstr, '&') end +--- +-- Provides the default port for a given URI scheme. +-- +-- @param scheme for determining the port, such as "http" or "https". +-- @return A port number as an integer, such as 443 for scheme "https", +-- or nil in case of an undefined scheme +----------------------------------------------------------------------------- +function get_default_port (scheme) + local ports = {http=80, https=443} + return ports[(scheme or ""):lower()] +end + return _ENV; diff --git a/scripts/http-backup-finder.nse b/scripts/http-backup-finder.nse index 6d798a11b1..894c738317 100644 --- a/scripts/http-backup-finder.nse +++ b/scripts/http-backup-finder.nse @@ -122,13 +122,8 @@ action = function(host, port) if ( parsed.path:match(".*%.*.$") ) then -- iterate over possible backup files for link in backupNames(parsed.path) do - local host, port = parsed.host, parsed.port - - -- if no port was found, try to deduce it from the scheme - if ( not(port) ) then - port = (parsed.scheme == 'https') and 443 - port = port or ((parsed.scheme == 'http') and 80) - end + local host = parsed.host + local port = parsed.port or url.get_default_port(parsed.scheme) -- the url.escape doesn't work here as it encodes / to %2F -- which results in 400 bad request, so we simple do a space diff --git a/scripts/http-favicon.nse b/scripts/http-favicon.nse index bca4b9a264..91984d3635 100644 --- a/scripts/http-favicon.nse +++ b/scripts/http-favicon.nse @@ -128,21 +128,17 @@ end -- host, port, and path if the URL is relative. Return nil if the scheme is not -- "http" or "https". function parse_url_relative(u, host, port, path) - local defaultport, scheme, abspath + local scheme, abspath u = url.parse(u) scheme = u.scheme or "http" - if scheme == "http" then - defaultport = 80 - elseif scheme == "https" then - defaultport = 443 - else + if not (scheme == "http" or scheme == "https") then return nil end abspath = u.path or "" if not string.find(abspath, "^/") then abspath = dirname(path) .. "/" .. abspath end - return u.host or host, u.port or defaultport, abspath + return u.host or host, u.port or url.get_default_port(scheme), abspath end function parseIcon( body ) diff --git a/scripts/http-open-redirect.nse b/scripts/http-open-redirect.nse index 2ceb152509..91bec505f3 100644 --- a/scripts/http-open-redirect.nse +++ b/scripts/http-open-redirect.nse @@ -59,13 +59,7 @@ local function dbgt(tbl) end local function getHostPort(parsed) - local host, port = parsed.host, parsed.port - -- if no port was found, try to deduce it from the scheme - if ( not(port) ) then - port = (parsed.scheme == 'https') and 443 - port = port or ((parsed.scheme == 'http') and 80) - end - return host, port + return parsed.host, parsed.port or url.get_default_port(parsed.scheme) end local function isRedirect(status) diff --git a/scripts/http-phpself-xss.nse b/scripts/http-phpself-xss.nse index 9be1845a63..b59d892f34 100644 --- a/scripts/http-phpself-xss.nse +++ b/scripts/http-phpself-xss.nse @@ -147,12 +147,8 @@ PHP files are not handling safely the variable $_SERVER["PHP_SELF"] causing Refl --Only work with .php files if ( parsed.path and parsed.path:match(".*.php") ) then - --The following port/scheme code was seen in http-backup-finder and its neat =) - local host, port = parsed.host, parsed.port - if ( not(port) ) then - port = (parsed.scheme == 'https') and 443 - port = port or ((parsed.scheme == 'http') and 80) - end + local host = parsed.host + local port = parsed.port or url.get_default_port(parsed.scheme) local escaped_link = parsed.path:gsub(" ", "%%20") if launch_probe(host,port,escaped_link) then table.insert(vulnpages, parsed.scheme..'://'..host..escaped_link..PHP_SELF_PROBE) diff --git a/scripts/http-unsafe-output-escaping.nse b/scripts/http-unsafe-output-escaping.nse index 64c2a2ba76..2ef97c1c5d 100644 --- a/scripts/http-unsafe-output-escaping.nse +++ b/scripts/http-unsafe-output-escaping.nse @@ -49,14 +49,9 @@ portrule = shortport.http local dbg = stdnse.debug2 local function getHostPort(parsed) - local host, port = parsed.host, parsed.port - -- if no port was found, try to deduce it from the scheme - if ( not(port) ) then - port = (parsed.scheme == 'https') and 443 - port = port or ((parsed.scheme == 'http') and 80) - end - return host, port + return parsed.host, parsed.port or url.get_default_port(parsed.scheme) end + local function getReflected(parsed, r) local reflected_values,not_reflected_values = {},{} local count = 0