diff --git a/NmapOps.cc b/NmapOps.cc index 24fc1d0fb..68aad37e4 100644 --- a/NmapOps.cc +++ b/NmapOps.cc @@ -167,6 +167,10 @@ NmapOps::~NmapOps() { free(portlist); portlist = NULL; } + if (exclude_portlist) { + free(exclude_portlist); + exclude_portlist = NULL; + } if (proxy_chain) { nsock_proxychain_delete(proxy_chain); proxy_chain = NULL; @@ -382,6 +386,7 @@ void NmapOps::Initialize() { inputfd = NULL; idleProxy = NULL; portlist = NULL; + exclude_portlist = NULL; proxy_chain = NULL; } diff --git a/NmapOps.h b/NmapOps.h index 89eb41d34..577efbf64 100644 --- a/NmapOps.h +++ b/NmapOps.h @@ -372,6 +372,7 @@ class NmapOps { char *exclude_spec; FILE *inputfd; char *portlist; /* Ports list specified by user */ + char *exclude_portlist; /* exclude-ports list specified by user */ nsock_proxychain proxy_chain; diff --git a/nmap.cc b/nmap.cc index ab7529d60..149359160 100644 --- a/nmap.cc +++ b/nmap.cc @@ -255,6 +255,7 @@ static void printusage(int rc) { "PORT SPECIFICATION AND SCAN ORDER:\n" " -p : Only scan specified ports\n" " Ex: -p22; -p1-65535; -p U:53,111,137,T:21-25,80,139,8080,S:9\n" + " --exclude-ports : Exclude the specified ports from scanning\n" " -F: Fast mode - Scan fewer ports than the default scan\n" " -r: Scan ports consecutively - don't randomize\n" " --top-ports : Scan most common ports\n" @@ -638,6 +639,8 @@ void parse_options(int argc, char **argv) { {"dns-servers", required_argument, 0, 0}, {"port-ratio", required_argument, 0, 0}, {"port_ratio", required_argument, 0, 0}, + {"exclude-ports", required_argument, 0, 0}, + {"exclude_ports", required_argument, 0, 0}, {"top-ports", required_argument, 0, 0}, {"top_ports", required_argument, 0, 0}, #ifndef NOLUA @@ -941,6 +944,10 @@ void parse_options(int argc, char **argv) { o.topportlevel = strtod(optarg, &ptr); if (!ptr || o.topportlevel < 0 || o.topportlevel >= 1) fatal("--port-ratio should be between [0 and 1)"); + } else if (optcmp(long_options[option_index].name, "exclude-ports") == 0) { + if (o.exclude_portlist) + fatal("Only 1 --exclude-ports option allowed, separate multiple ranges with commas."); + o.exclude_portlist = strdup(optarg); } else if (optcmp(long_options[option_index].name, "top-ports") == 0) { char *ptr; o.topportlevel = strtod(optarg, &ptr); @@ -1495,7 +1502,7 @@ void apply_delayed_options() { else getpts((char *) (o.fastscan ? "[P:0-]" : "0-"), &ports); // Default protocols to scan } else if (!o.noportscan) { - gettoppts(o.topportlevel, o.portlist, &ports); + gettoppts(o.topportlevel, o.portlist, &ports, o.exclude_portlist); } // Uncomment the following line to use the common lisp port spec test suite @@ -1560,6 +1567,9 @@ void apply_delayed_options() { /* Warn if setuid/setgid. */ check_setugid(); + /* Remove any ports that are in the exclusion list */ + removepts(o.exclude_portlist, &ports); + /* By now, we've got our port lists. Give the user a warning if no * ports are specified for the type of scan being requested. Other things * (such as OS ident scan) might break cause no ports were specified, but @@ -1574,6 +1584,16 @@ void apply_delayed_options() { if (o.ipprotscan && ports.prot_count == 0) error("WARNING: protocol scan was requested, but no protocols were specified to be scanned. Skipping this scan type."); + if (o.pingtype & PINGTYPE_TCP && ports.syn_ping_count+ports.ack_ping_count == 0) + error("WARNING: a TCP ping scan was requested, but after excluding requested TCP ports, none remain. Skipping this scan type."); + if (o.pingtype & PINGTYPE_UDP && ports.udp_ping_count == 0) + error("WARNING: a UDP ping scan was requested, but after excluding requested UDP ports, none remain. Skipping this scan type."); + if (o.pingtype & PINGTYPE_SCTP_INIT && ports.sctp_ping_count == 0) + error("WARNING: a SCTP ping scan was requested, but after excluding requested SCTP ports, none remain. Skipping this scan type."); + if (o.pingtype & PINGTYPE_PROTO && ports.proto_ping_count == 0) + error("WARNING: a IP Protocol ping scan was requested, but after excluding requested protocols, none remain. Skipping this scan type."); + + /* Set up our array of decoys! */ if (o.decoyturn == -1) { o.decoyturn = (o.numdecoys == 0) ? 0 : get_random_uint() % o.numdecoys; @@ -2379,6 +2399,11 @@ void getpts(const char *origexpr, struct scan_lists *ports) { range_type |= SCAN_SCTP_PORT; if (o.ipprotscan) range_type |= SCAN_PROTOCOLS; + if (o.noportscan && o.exclude_portlist) { // We want to exclude from ping scans in this case but we take port list normally and then removepts() handles it + range_type |= SCAN_TCP_PORT; + range_type |= SCAN_UDP_PORT; + range_type |= SCAN_SCTP_PORT; + } porttbl = (u8 *) safe_zalloc(65536); @@ -2475,6 +2500,80 @@ void getpts_simple(const char *origexpr, int range_type, free(porttbl); } +/* removepts() takes a port specification and removes any matching ports + from the given scan_lists struct. */ + +static int remaining_ports(unsigned short int *ports, int count, unsigned short int *exclude_ports, int exclude_count, const char *type = ""); + +void removepts(const char *expr, struct scan_lists * ports) { + static struct scan_lists exclude_ports; + + if (!expr) + return; + + getpts(expr, &exclude_ports); + + #define SUBTRACT_PORTS(type,excludetype) \ + ports->type##_count = remaining_ports(ports->type##_ports, \ + ports->type##_count, \ + exclude_ports.excludetype##_ports, \ + exclude_ports.excludetype##_count, \ + #type) + + SUBTRACT_PORTS(tcp, tcp); + SUBTRACT_PORTS(udp, udp); + SUBTRACT_PORTS(sctp, sctp); + SUBTRACT_PORTS(syn_ping, tcp); + SUBTRACT_PORTS(ack_ping, tcp); + SUBTRACT_PORTS(udp_ping, udp); + SUBTRACT_PORTS(sctp_ping, sctp); + + #define prot_ports prots + SUBTRACT_PORTS(prot, prot); + SUBTRACT_PORTS(proto_ping, prot); + #undef prot_ports + + #undef SUBTRACT_PORTS + + free_scan_lists(&exclude_ports); +} + +/* This function returns the number of ports that remain after the excluded ports + are removed from the ports. It places these ports at the start of the ports array. */ +static int remaining_ports(unsigned short int *ports, int count, unsigned short int *exclude_ports, int exclude_count, const char *type) { + static bool has_been_excluded[65536]; + int i, j; + + if (count == 0 || exclude_count == 0) + return count; + + if (o.debugging > 1) + log_write(LOG_STDOUT, "Removed %s ports: ", type); + + for (i = 0; i < 65536; i++) + has_been_excluded[i] = false; + for (i = 0; i < exclude_count; i++) + has_been_excluded[exclude_ports[i]] = true; + for (i = 0, j = 0; i < count; i++) + if (!has_been_excluded[ports[i]]) + ports[j++] = ports[i]; + else if (o.debugging > 1) + log_write(LOG_STDOUT, "%d ", ports[i]); + + if (o.debugging > 1) { + if (count-j) { + log_write(LOG_STDOUT, "\n"); + } else { + log_write(LOG_STDOUT, "None\n"); + } + } + if (o.debugging && count-j) { + log_write(LOG_STDOUT, "Removed %d %s ports that would have been considered for scanning otherwise.\n", count-j, type); + } + + return j; +} + /* getpts() and getpts_simple() (see above) are wrappers for this function */ static void getpts_aux(const char *origexpr, int nested, u8 *porttbl, int range_type, int *portwarning, bool change_range_type) { @@ -2548,10 +2647,10 @@ static void getpts_aux(const char *origexpr, int nested, u8 *porttbl, int range_ rangestart = strtol(current_range, &endptr, 10); if (range_type & SCAN_PROTOCOLS) { if (rangestart < 0 || rangestart > 255) - fatal("Protocols to be scanned must be between 0 and 255 inclusive"); + fatal("Protocols specified must be between 0 and 255 inclusive"); } else { if (rangestart < 0 || rangestart > 65535) - fatal("Ports to be scanned must be between 0 and 65535 inclusive"); + fatal("Ports specified must be between 0 and 65535 inclusive"); } current_range = endptr; while (isspace((int) (unsigned char) *current_range)) current_range++; @@ -2595,10 +2694,10 @@ static void getpts_aux(const char *origexpr, int nested, u8 *porttbl, int range_ rangeend = strtol(current_range, &endptr, 10); if (range_type & SCAN_PROTOCOLS) { if (rangeend < 0 || rangeend > 255) - fatal("Protocols to be scanned must be between 0 and 255 inclusive"); + fatal("Protocols specified must be between 0 and 255 inclusive"); } else { if (rangeend < 0 || rangeend > 65535) - fatal("Ports to be scanned must be between 0 and 65535 inclusive"); + fatal("Ports specified must be between 0 and 65535 inclusive"); } current_range = endptr; } else { diff --git a/nmap.h b/nmap.h index cd2d4947a..985d0da7a 100644 --- a/nmap.h +++ b/nmap.h @@ -436,6 +436,7 @@ void printinteractiveusage(); void getpts(const char *expr, struct scan_lists * ports); /* someone stole the name getports()! */ void getpts_simple(const char *origexpr, int range_type, unsigned short **list, int *count); +void removepts(const char *expr, struct scan_lists * ports); void free_scan_lists(struct scan_lists *ports); /* Renamed main so that interactive mode could preprocess when necessary */ diff --git a/services.cc b/services.cc index 46882cb3b..a72136dc2 100644 --- a/services.cc +++ b/services.cc @@ -424,10 +424,13 @@ static bool is_port_member(const struct scan_lists *ptsdata, const struct servic // If level is 1 or above, we treat it as a "top ports" directive // and return the N highest ratio ports (where N==level). // +// If the fourth parameter is not NULL, then the specified ports +// are excluded first and only then are the top N ports taken +// // This function doesn't support IP protocol scan so only call this // function if o.TCPScan() || o.UDPScan() || o.SCTPScan() -void gettoppts(double level, char *portlist, struct scan_lists * ports) { +void gettoppts(double level, char *portlist, struct scan_lists * ports, char *exclude_ports) { int ti=0, ui=0, si=0; struct scan_lists ptsdata = { 0 }; bool ptsdata_initialized = false; @@ -466,7 +469,14 @@ void gettoppts(double level, char *portlist, struct scan_lists * ports) { if (portlist){ getpts(portlist, &ptsdata); ptsdata_initialized = true; + } else if (exclude_ports) { + getpts("-", &ptsdata); + ptsdata_initialized = true; } + + if (ptsdata_initialized && exclude_ports) + removepts(exclude_ports, &ptsdata); + if (level < 1) { for (i = services_by_ratio.begin(); i != services_by_ratio.end(); i++) { current = &(*i); diff --git a/services.h b/services.h index 3649e9be6..1904a8a2a 100644 --- a/services.h +++ b/services.h @@ -150,7 +150,7 @@ int addportsfromservmask(char *mask, u8 *porttbl, int range_type); struct servent *nmap_getservbyport(int port, const char *proto); -void gettoppts(double level, char *portlist, struct scan_lists * ports); +void gettoppts(double level, char *portlist, struct scan_lists * ports, char *exclude_list = NULL); void free_services(); diff --git a/todo/done.txt b/todo/done.txt index 2ef833ee1..71cb598ae 100644 --- a/todo/done.txt +++ b/todo/done.txt @@ -1,5 +1,8 @@ DONE: +o Implement an --exclude-ports option. See + http://seclists.org/nmap-dev/2012/q1/275 + o In an ideal world, Zenmap would not run out of memory and crash. And we already have an entry for improving Zenmap's memory consumption. But in the meantime, we should catch the error and diff --git a/todo/nmap.txt b/todo/nmap.txt index d8d995d49..045c31933 100644 --- a/todo/nmap.txt +++ b/todo/nmap.txt @@ -58,8 +58,6 @@ o GSOC 2014 student Jay will be looking at these items: o Consider using a binary decision diagram for --exclude list to make it more efficient for large exclude lists. See http://seclists.org/nmap-dev/2012/q4/420. - o Implement an --exclude-ports option. See - http://seclists.org/nmap-dev/2012/q1/275 o [Zenmap] Combine parallel timed-out hops into one node in the topology view. http://seclists.org/nmap-dev/2012/q1/82 has a patch, however it doesn't handle the case of two or more consecutive