diff --git a/nse_nmaplib.cc b/nse_nmaplib.cc index 3bb83c946..818197ad9 100644 --- a/nse_nmaplib.cc +++ b/nse_nmaplib.cc @@ -11,6 +11,7 @@ extern "C" { #include "NmapOps.h" #include "Target.h" #include "portlist.h" +#include "service_scan.h" #include "nmap_rpc.h" #include "nmap_dns.h" #include "osscan.h" @@ -502,6 +503,29 @@ static int l_get_port_state (lua_State *L) return 1; } +/* this function must be used by version category scripts or any other + * lua code to check if a given port with it's protocol are in the + * exclude directive found in the nmap-service-probes file. + * */ +static int l_port_is_excluded (lua_State *L) +{ + unsigned short portno; + int protocol; + + if (!lua_isnumber(L, 1)) + luaL_error(L, "port 'number' field must be a number"); + if (!lua_isstring(L, 2)) + luaL_error(L, "port 'protocol' field must be a string"); + + portno = (unsigned short) lua_tointeger(L, 1); + protocol = strcmp(lua_tostring(L, 2), "tcp") == 0 ? IPPROTO_TCP : + strcmp(lua_tostring(L, 2), "udp") == 0 ? IPPROTO_UDP : + luaL_error(L, "port 'protocol' field must be \"tcp\" or \"udp\""); + + lua_pushboolean(L,AllProbes::check_excluded_port(portno, protocol)); + return 1; +} + /* unlike set_portinfo() this function sets the port state in nmap. * if for example a udp port was seen by the script as open instead of * filtered, the script is free to say so. @@ -699,6 +723,7 @@ int luaopen_nmap (lua_State *L) {"get_ports", l_get_ports}, {"set_port_state", l_set_port_state}, {"set_port_version", l_set_port_version}, + {"port_is_excluded", l_port_is_excluded}, {"new_socket", l_nsock_new}, {"new_dnet", l_dnet_new}, {"get_interface_link", l_dnet_get_interface_link}, diff --git a/nselib/shortport.lua b/nselib/shortport.lua index 93ebc4b9f..00bcb9af2 100644 --- a/nselib/shortport.lua +++ b/nselib/shortport.lua @@ -6,6 +6,8 @@ module(... or "shortport", package.seeall) +local nmap = require "nmap" + --- -- See if a table contains a value. -- @param t A table repesenting a set. @@ -20,6 +22,17 @@ local function includes(t, value) return false end +--- Check if the port and it's protocol are in the exclude directive. +-- +-- @param port A port number. +-- @param proto The protocol to match against, default "tcp". +-- @return True if the port and protocol are +-- in the exclude directive. +port_is_excluded = function(port, proto) + proto = proto or "tcp" + return nmap.port_is_excluded(port, proto) +end + --- Return a portrule that returns true when given an open port matching a -- single port number or a list of port numbers. -- @param ports A single port number or a list of port numbers. @@ -110,3 +123,28 @@ port_or_service = function(ports, services, protos, states) return port_checker(host, port) or service_checker(host, port) end end + +--- Return a portrule that returns true when given an open port matching +-- either a port number or service name and has not been listed in the +-- exclude port directive of the nmap-service-probes file. +-- +-- This function is a combination of the port_is_excluded +-- and port_or_service functions. The port, service, proto may +-- be single values or a list of values as in those functions. +-- This function can be used by version category scripts to check if a +-- given port and it's protocol are in the exclude directive. +-- @usage portrule = shortport.version_port_or_service(22) +-- @usage portrule = shortport.version_port_or_service(nil, "ssh", "tcp") +-- @param services Service name or a list of names to run against. +-- @param protos The protocol or list of protocols to match against, default +-- "tcp". +-- @param states A state or list of states to match against, default +-- {"open", "open|filtered"}. +-- @return Function for the portrule. +version_port_or_service = function(ports, services, protos, states) + return function(host, port) + local p_s_check = port_or_service(ports, services, protos, states) + return p_s_check(host, port) + and not(port_is_excluded(port.number, port.protocol)) + end +end diff --git a/scripts/db2-das-info.nse b/scripts/db2-das-info.nse index efced4763..8108679bd 100644 --- a/scripts/db2-das-info.nse +++ b/scripts/db2-das-info.nse @@ -85,7 +85,9 @@ require "shortport" -- -- Details on how to reproduce these steps with the CLI are welcome. -portrule = shortport.portnumber({523}, {"tcp","udp"}, {"open", "open|filtered"}) +portrule = shortport.version_port_or_service({523}, nil, + {"tcp","udp"}, + {"open", "open|filtered"}) --- Extracts the server profile from an already parsed db2 packet -- diff --git a/scripts/db2-info.nse b/scripts/db2-info.nse index b3d9ff4fd..a7e41f87f 100644 --- a/scripts/db2-info.nse +++ b/scripts/db2-info.nse @@ -29,7 +29,9 @@ require "db2" -- parseVersion was ripped from the old db2-info.nse written by Tom Sellers -- -portrule = shortport.port_or_service({50000,60000},"ibm-db2", "tcp", {"open", "open|filtered"}) +portrule = shortport.version_port_or_service({50000,60000}, + "ibm-db2", "tcp", + {"open", "open|filtered"}) --- Converts the prodrel server string to a version string -- @@ -86,4 +88,4 @@ action = function( host, port ) results = results .. "External Name: " .. response.extname return results -end \ No newline at end of file +end diff --git a/scripts/iax2-version.nse b/scripts/iax2-version.nse index 50acc9244..dd7302429 100644 --- a/scripts/iax2-version.nse +++ b/scripts/iax2-version.nse @@ -18,7 +18,7 @@ categories = {"version"} require "comm" require "shortport" -portrule = shortport.portnumber(4569, "udp") +portrule = shortport.version_port_or_service(4569, nil, "udp") action = function(host, port) -- see http://www.cornfed.com/iax.pdf for all options. diff --git a/scripts/jdwp-version.nse b/scripts/jdwp-version.nse index 431a4d3fd..c05f24e92 100644 --- a/scripts/jdwp-version.nse +++ b/scripts/jdwp-version.nse @@ -17,12 +17,14 @@ categories = {"version"} -- 9999/tcp open jdwp Java Debug Wire Protocol (Reference Implementation) version 1.6 1.6.0_17 require "comm" +require "shortport" portrule = function(host, port) -- JDWP will close the port if there is no valid handshake within 2 -- seconds, Service detection's NULL probe detects it as tcpwrapped. return port.service == "tcpwrapped" and port.protocol == "tcp" and port.state == "open" + and not(shortport.port_is_excluded(port.number,port.protocol)) end action = function(host, port) diff --git a/scripts/pptp-version.nse b/scripts/pptp-version.nse index 03f8f94c7..46f5c7c87 100644 --- a/scripts/pptp-version.nse +++ b/scripts/pptp-version.nse @@ -18,7 +18,7 @@ categories = {"version"} require "comm" require "shortport" -portrule = shortport.portnumber(1723) +portrule = shortport.version_port_or_service(1723) action = function(host, port) -- build a PPTP Start-Control-Connection-Request packet diff --git a/scripts/skypev2-version.nse b/scripts/skypev2-version.nse index a08530f48..1b85dbd6e 100644 --- a/scripts/skypev2-version.nse +++ b/scripts/skypev2-version.nse @@ -12,6 +12,7 @@ license = "Same as Nmap--See http://nmap.org/book/man-legal.html" categories = {"version"} require "comm" +require "shortport" portrule = function(host, port) return (port.number == 80 or port.number == 443 or @@ -19,6 +20,7 @@ portrule = function(host, port) port.service == "unknown") and port.protocol == "tcp" and port.state == "open" and port.service ~= "http" and port.service ~= "ssl/http" + and not(shortport.port_is_excluded(port.number,port.protocol)) end action = function(host, port) diff --git a/service_scan.cc b/service_scan.cc index b7e1df8c9..842e370e3 100644 --- a/service_scan.cc +++ b/service_scan.cc @@ -1172,6 +1172,28 @@ void AllProbes::service_scan_free(void) } } +// Function that calls isExcluded() function to check if the port +// is in the excludedports list. +int AllProbes::check_excluded_port(unsigned short portno, int proto) +{ + int excluded; + + // Check if the -sV version scan option was specified + // or if the --allports option was used + if (!o.servicescan || o.override_excludeports) + return 0; + + if (global_AP == NULL) + fatal("Failed to check the list of excluded ports: %s", __func__); + + if (excluded = global_AP->isExcluded(portno, proto)) { + if (o.debugging) + log_write(LOG_PLAIN, "EXCLUDING %d/%s\n", + portno, IPPROTO2STR(proto)); + } + + return excluded; +} // If the buf (of length buflen) matches one of the regexes in this // ServiceProbe, returns the details of nth match (service name, diff --git a/service_scan.h b/service_scan.h index f93dfb191..02b8388a0 100644 --- a/service_scan.h +++ b/service_scan.h @@ -332,6 +332,7 @@ public: static AllProbes *service_scan_init(void); static void service_scan_free(void); + static int check_excluded_port(unsigned short port, int proto); protected: static AllProbes *global_AP; };