From 38671f22259f87f564403dc6e91c1e4216fdb978 Mon Sep 17 00:00:00 2001 From: dmiller Date: Thu, 17 Dec 2020 22:12:04 +0000 Subject: [PATCH] Enhance output to print proto, reason, and port list for ignored ports Normal output will report count, protocol, and reason for each group of ports in an ignored state (usually closed or filtered when there are more than 25 ports in one of those states). XML output will contain a consolidated list of port numbers as well. --- output.cc | 140 ++++++++++++++++++++++++++----------------------- portreasons.cc | 93 ++++---------------------------- portreasons.h | 15 +++--- 3 files changed, 90 insertions(+), 158 deletions(-) diff --git a/output.cc b/output.cc index f116f6537..4303606ce 100644 --- a/output.cc +++ b/output.cc @@ -494,6 +494,9 @@ static char *formatScriptOutput(const ScriptResult &sr) { } #endif /* NOLUA */ +/* Output a list of ports, compressing ranges like 80-85 */ +static void output_rangelist_given_ports(int logt, unsigned short *ports, int numports); + /* Prints the familiar Nmap tabular output showing the "interesting" ports found on the machine. It also handles the Machine/Grepable output and the XML output. It is pretty ugly -- in particular I @@ -523,6 +526,7 @@ void printportoutput(Target *currenths, PortList *plist) { int numrows; int numignoredports = plist->numIgnoredPorts(); int numports = plist->numPorts(); + state_reason_summary_t *reasons, *currentr; std::vector saved_servicefps; @@ -530,53 +534,8 @@ void printportoutput(Target *currenths, PortList *plist) { return; xml_start_tag("ports"); - int prevstate = PORT_UNKNOWN; - int istate; - - while ((istate = plist->nextIgnoredState(prevstate)) != PORT_UNKNOWN) { - xml_open_start_tag("extraports"); - xml_attribute("state", "%s", statenum2str(istate)); - xml_attribute("count", "%d", plist->getStateCounts(istate)); - xml_close_start_tag(); - xml_newline(); - print_xml_state_summary(plist, istate); - xml_end_tag(); - xml_newline(); - prevstate = istate; - } - - if (numignoredports == numports) { - log_write(LOG_PLAIN, "All %d scanned ports on %s are ", - numignoredports, - currenths->NameIP(hostname, sizeof(hostname))); - log_write(LOG_MACHINE, "Host: %s (%s)\t%s: ", - currenths->targetipstr(), currenths->HostName(), - (o.ipprotscan) ? "Protocols" : "Ports"); - - if (plist->numIgnoredStates() == 1) { - istate = plist->nextIgnoredState(PORT_UNKNOWN); - log_write(LOG_PLAIN, "%s", statenum2str(istate)); - /* Grepable output supports only one ignored state. */ - log_write(LOG_MACHINE, "\tIgnored State: %s (%d)", - statenum2str(istate), plist->getStateCounts(istate)); - } else { - prevstate = PORT_UNKNOWN; - while ((istate = plist->nextIgnoredState(prevstate)) != PORT_UNKNOWN) { - if (prevstate != PORT_UNKNOWN) - log_write(LOG_PLAIN, " or "); - log_write(LOG_PLAIN, "%s (%d)", statenum2str(istate), - plist->getStateCounts(istate)); - prevstate = istate; - } - } - if (o.reason) - print_state_summary(plist, STATE_REASON_EMPTY); - log_write(LOG_PLAIN, "\n"); - - xml_end_tag(); /* ports */ - xml_newline(); - return; - } + log_write(LOG_MACHINE, "Host: %s (%s)", currenths->targetipstr(), + currenths->HostName()); if ((o.verbose > 1 || o.debugging) && currenths->StartTime()) { time_t tm_secs, tm_sece; @@ -603,35 +562,82 @@ void printportoutput(Target *currenths, PortList *plist) { } } } - log_write(LOG_MACHINE, "Host: %s (%s)", currenths->targetipstr(), - currenths->HostName()); - /* Show line like: - Not shown: 3995 closed ports, 514 filtered ports - if appropriate (note that states are reverse-sorted by # of ports) */ - prevstate = PORT_UNKNOWN; + int prevstate = PORT_UNKNOWN; + int istate; + while ((istate = plist->nextIgnoredState(prevstate)) != PORT_UNKNOWN) { - if (prevstate == PORT_UNKNOWN) + i = plist->getStateCounts(istate); + xml_open_start_tag("extraports"); + xml_attribute("state", "%s", statenum2str(istate)); + xml_attribute("count", "%d", i); + xml_close_start_tag(); + xml_newline(); + + /* Show line like: + Not shown: 98 open|filtered udp ports (no-response), 59 closed tcp ports (reset) + if appropriate (note that states are reverse-sorted by # of ports) */ + if (prevstate == PORT_UNKNOWN) { + // First time through, check special case + if (numignoredports == numports) { + log_write(LOG_PLAIN, "All %d scanned ports on %s are in ignored states.\n", + numignoredports, currenths->NameIP(hostname, sizeof(hostname))); + log_write(LOG_MACHINE, "\t%s: ", (o.ipprotscan) ? "Protocols" : "Ports"); + /* Grepable output supports only one ignored state. */ + if (plist->numIgnoredStates() == 1) { + log_write(LOG_MACHINE, "\tIgnored State: %s (%d)", statenum2str(istate), i); + } + } log_write(LOG_PLAIN, "Not shown: "); - else + } else { log_write(LOG_PLAIN, ", "); - char desc[32]; - if (o.ipprotscan) - Snprintf(desc, sizeof(desc), - (plist->getStateCounts(istate) == - 1) ? "protocol" : "protocols"); - else - Snprintf(desc, sizeof(desc), - (plist->getStateCounts(istate) == 1) ? "port" : "ports"); - log_write(LOG_PLAIN, "%d %s %s", plist->getStateCounts(istate), - statenum2str(istate), desc); + } + + if((currentr = reasons = get_state_reason_summary(plist, istate)) == NULL) { + log_write(LOG_PLAIN, "%d %s %s%s", i, statenum2str(istate), + o.ipprotscan ? "protocol" : "port", + plist->getStateCounts(istate) == 1 ? "" : "s"); + prevstate = istate; + continue; + } + + while(currentr != NULL) { + if(currentr->count > 0) { + xml_open_start_tag("extrareasons"); + xml_attribute("reason", "%s", reason_str(currentr->reason_id, SINGULAR)); + xml_attribute("count", "%d", currentr->count); + xml_attribute("proto", "%s", IPPROTO2STR(currentr->proto)); + xml_write_raw(" ports=\""); + output_rangelist_given_ports(LOG_XML, currentr->ports, currentr->count); + xml_write_raw("\""); + xml_close_empty_tag(); + xml_newline(); + + if (currentr != reasons) + log_write(LOG_PLAIN, ", "); + log_write(LOG_PLAIN, "%d %s %s %s%s (%s)", + currentr->count, statenum2str(istate), IPPROTO2STR(currentr->proto), + o.ipprotscan ? "protocol" : "port", + plist->getStateCounts(istate) == 1 ? "" : "s", + reason_str(currentr->reason_id, SINGULAR)); + } + currentr = currentr->next; + } + state_reason_summary_dinit(reasons); + xml_end_tag(); + xml_newline(); prevstate = istate; } log_write(LOG_PLAIN, "\n"); - if (o.reason) - print_state_summary(plist, STATE_REASON_FULL); + if (numignoredports == numports) { + // Nothing left to show. + xml_end_tag(); /* ports */ + xml_newline(); + log_flush_all(); + return; + } /* OK, now it is time to deal with the service table ... */ colno = 0; diff --git a/portreasons.cc b/portreasons.cc index 5a58dbdbe..bf177f26f 100644 --- a/portreasons.cc +++ b/portreasons.cc @@ -67,11 +67,9 @@ #include "winfix.h" #endif #include "portlist.h" -#include "output.h" #include "NmapOps.h" #include "portreasons.h" #include "Target.h" -#include "xml.h" extern NmapOps o; @@ -252,7 +250,7 @@ static void state_reason_summary_init(state_reason_summary_t *r) { r->next = NULL; } -static void state_reason_summary_dinit(state_reason_summary_t *r) { +void state_reason_summary_dinit(state_reason_summary_t *r) { state_reason_summary_t *tmp; while(r != NULL) { @@ -262,19 +260,6 @@ static void state_reason_summary_dinit(state_reason_summary_t *r) { } } -/* Counts how different valid state reasons exist */ -static int state_summary_size(state_reason_summary_t *head) { - state_reason_summary_t *current = head; - int size = 0; - - while(current) { - if(current->count > 0) - size++; - current = current->next; - } - return size; -} - /* Simon Tatham's linked list merge sort * * Merge sort works really well on linked lists @@ -333,28 +318,29 @@ static state_reason_summary_t *reason_sort(state_reason_summary_t *list) { } /* Builds and aggregates reason state summary messages */ -static int update_state_summary(state_reason_summary_t *head, reason_t reason_id) { +static int update_state_summary(state_reason_summary_t *head, Port *port) { state_reason_summary_t *tmp = head; if(tmp == NULL) return -1; while(1) { - if(tmp->reason_id == reason_id) { - tmp->count++; - return 0; + if(tmp->reason_id == port->reason.reason_id && tmp->proto == port->proto) { + break; } if(tmp->next == NULL) { tmp->next = (state_reason_summary_t *)safe_malloc(sizeof(state_reason_summary_t)); tmp = tmp->next; + state_reason_summary_init(tmp); + tmp->reason_id = port->reason.reason_id; + tmp->proto = port->proto; break; } tmp = tmp->next; } - state_reason_summary_init(tmp); - tmp->reason_id = reason_id; - tmp->count = 1; + tmp->ports[tmp->count] = port->portno; + tmp->count++; return 0; } @@ -374,14 +360,14 @@ static unsigned int get_state_summary(state_reason_summary_t *head, PortList *Po while((current = Ports->nextPort(current, &port, proto, state)) != NULL) { if(Ports->isIgnoredState(current->state, NULL)) { total++; - update_state_summary(reason, current->reason.reason_id); + update_state_summary(reason, current); } } return total; } /* parse and sort reason summary for main print_* functions */ -static state_reason_summary_t *print_state_summary_internal(PortList *Ports, int state) { +state_reason_summary_t *get_state_reason_summary(PortList *Ports, int state) { state_reason_summary_t *reason_head; reason_head = (state_reason_summary_t *)safe_malloc(sizeof(state_reason_summary_t)); @@ -416,63 +402,6 @@ void state_reason_init(state_reason_t *reason) { reason->ttl = 0; } -/* Main external interface to converting, building, sorting and - * printing plain-text state reason summaries */ -void print_state_summary(PortList *Ports, unsigned short type) { - state_reason_summary_t *reason_head, *currentr; - bool first_time = true; - const char *separator = ", "; - int states; - - if((reason_head = print_state_summary_internal(Ports, 0)) == NULL) - return; - - if(type == STATE_REASON_EMPTY) - log_write(LOG_PLAIN, " because of"); - else if(type == STATE_REASON_FULL) - log_write(LOG_PLAIN, "Reason:"); - else - assert(0); - - states = state_summary_size(reason_head); - currentr = reason_head; - - while(currentr != NULL) { - if(states == 1 && (!first_time)) - separator = " and "; - if(currentr->count > 0) { - log_write(LOG_PLAIN, "%s%d %s", (first_time) ? " " : separator, - currentr->count, reason_str(currentr->reason_id, currentr->count)); - first_time = false; - - } - states--; - currentr = currentr->next; - } - if(type == STATE_REASON_FULL) - log_write(LOG_PLAIN, "\n"); - state_reason_summary_dinit(reason_head); -} - -void print_xml_state_summary(PortList *Ports, int state) { - state_reason_summary_t *reason_head, *currentr; - - if((currentr = reason_head = print_state_summary_internal(Ports, state)) == NULL) - return; - - while(currentr != NULL) { - if(currentr->count > 0) { - xml_open_start_tag("extrareasons"); - xml_attribute("reason", "%s", reason_str(currentr->reason_id, currentr->count)); - xml_attribute("count", "%d", currentr->count); - xml_close_empty_tag(); - xml_newline(); - } - currentr = currentr->next; - } - state_reason_summary_dinit(reason_head); -} - /* converts target into reason message for ping scans. Uses a static * buffer so new values overwrite old values */ char *target_reason_str(Target *t) { diff --git a/portreasons.h b/portreasons.h index 76cfb0c6e..3bb1529fd 100644 --- a/portreasons.h +++ b/portreasons.h @@ -107,6 +107,8 @@ typedef struct port_reason_summary { reason_t reason_id; unsigned int count; struct port_reason_summary *next; + unsigned short proto; + unsigned short ports[0xffff+1]; } state_reason_summary_t; @@ -146,12 +148,6 @@ public: /* Function to translate ICMP code and typ to reason code */ reason_codes icmp_to_reason(u8 proto, int icmp_type, int icmp_code); -/* passed to the print_state_summary. - * STATE_REASON_EMPTY will append to the current line, prefixed with " because of" - * STATE_REASON_FULL will start a new line, prefixed with "Reason:" */ -#define STATE_REASON_EMPTY 0 -#define STATE_REASON_FULL 1 - /* Passed to reason_str to determine if string should be in * plural of singular form */ #define SINGULAR 1 @@ -164,9 +160,10 @@ void state_reason_init(state_reason_t *reason); * port the plural is used, otherwise the singular is used. */ const char *reason_str(reason_t reason_id, unsigned int number); -/* Displays reason summary messages */ -void print_state_summary(PortList *Ports, unsigned short type); -void print_xml_state_summary(PortList *Ports, int state); +/* Returns a linked list of reasons why ports are in a given state */ +state_reason_summary_t *get_state_reason_summary(PortList *Ports, int state); +/* Frees the linked list from get_state_reason_summary */ +void state_reason_summary_dinit(state_reason_summary_t *r); /* Build an output string based on reason and source ip address. * Uses static return value so previous values will be over