From 8161f16c0ed2447fba2324fee5d037db08ecd643 Mon Sep 17 00:00:00 2001 From: david Date: Sat, 12 Jul 2008 02:18:18 +0000 Subject: [PATCH] This commit changes two separate but related things, which I found it inconvenient to change separately. The first change fixes a logical error in the storage of timing ping probes. Each target contains a description of a timing ping probe, which is stored in the two members probespec pingprobe; int pingprobe_state; pingprobe is the probe itself, and pingprobe_state is the state of the port that the probe was sent to (PORT_OPEN, PORT_CLOSED, etc.). A change in the state of the port was a criterion used in deciding whether to replace the current ping probe. The problem with this was that pingprobe_state was used to hold a host state, not a port state, during host discovery. Therefore it held a value like HOST_DOWN or HOST_UP. This was fine as long as host discovery and port scanning were separate, but now that timing pings are shared between those phases the states were in confict: HOST_UP = 1 = PORT_CLOSED. THis was fixed by using a value of PORT_UNKNOWN during host discovery. The second change redoes how timing ping probes are replaced. There is now an order of preference for timing ping probe types, defined by the function pingprobe_score (and pingprobe_is_better, which calls it). The order I have defined, from highest preference to lowest, is ARP Raw TCP (not SYN to an open port) UDP, IP protocol, or ICMP Raw TCP (SYN to an open port) TCP connect Anything else The port state is considered only in raw TCP SYN to an open port, which is given a lower preference because of the possibility of SYN flooding. Better ping probes supersede worse ping probes. So in nmap -PS -sA scanme.nmap.org the ping probe will be SYN to port 80 after host discovery, but then will change to ACK to an unfiltered port during port scanning. In nmap -PA -sS scanme.nmap.org the ping probe will be ACK to port 80 after host discovery and will remain that way during port scanning because SYN to an open port is a worse ping probe. Run with -d2 to see when timing pings change. --- scan_engine.cc | 123 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 80 insertions(+), 43 deletions(-) diff --git a/scan_engine.cc b/scan_engine.cc index ccc31dcf3..c09b1ec1f 100644 --- a/scan_engine.cc +++ b/scan_engine.cc @@ -649,7 +649,7 @@ static void init_ultra_timing_vals(ultra_timing_vals *timing, /* Take a buffer, buf, of size bufsz (32 bytes is sufficient) and writes a short description of the probe (arg1) into buf. It also returns buf. */ -static char *probespec2ascii(probespec *pspec, char *buf, unsigned int bufsz) { +static char *probespec2ascii(const probespec *pspec, char *buf, unsigned int bufsz) { char flagbuf[32]; char *f; switch(pspec->type) { @@ -2192,6 +2192,56 @@ void HostScanStats::getTiming(struct ultra_timing_vals *tmng) { return; } +/* Define a score for a ping probe, for the purposes of deciding whether one + probe should be preferred to another. The order, from most preferred to least + preferred, is + ARP + Raw TCP (not SYN to an open port) + UDP, IP protocol, or ICMP + Raw TCP (SYN to an open port) + TCP connect + Anything else + Raw TCP SYN to an open port is given a low preference because of the risk of + SYN flooding (this is the only case where the port state is considered). The + probe passed to this function is assumed to have received a positive + response, that is, it should not have set a port state just by timing out. */ +static unsigned int pingprobe_score(const probespec *pspec, int state) { + unsigned int score; + + switch (pspec->type) { + case PS_ARP: + score = 5; + break; + case PS_TCP: + if (pspec->pd.tcp.flags == TH_SYN && (state == PORT_OPEN || state == PORT_UNKNOWN)) + score = 2; + else + score = 4; + break; + case PS_UDP: + case PS_PROTO: + case PS_ICMP: + score = 3; + break; + case PS_CONNECTTCP: + score = 1; + break; + case PS_NONE: + default: + score = 0; + break; + } + + return score; +} + +/* Return true if new_probe and new_state define a better ping probe, as defined + by pingprobe_score, than do old_probe and old_state. */ +static bool pingprobe_is_better(const probespec *new_probe, int new_state, + const probespec *old_probe, int old_state) { + return pingprobe_score(new_probe, new_state) > pingprobe_score(old_probe, old_state); +} + static void ultrascan_host_pspec_update(UltraScanInfo *USI, HostScanStats *hss, const probespec *pspec, int newstate); @@ -2206,7 +2256,6 @@ static bool ultrascan_port_pspec_update(UltraScanInfo *USI, u8 proto = 0; int oldstate = PORT_TESTING; Port *currentp; - bool swappingport = false; /* Whether no response means a port is open */ bool noresp_open_scan = USI->noresp_open_scan; @@ -2275,28 +2324,6 @@ static bool ultrascan_port_pspec_update(UltraScanInfo *USI, break; } - - /* Consider changing the ping port */ - if (hss->target->pingprobe_state != newstate) { - /* TODO: UDP scan and such will have different preferences -- add them */ - if (noresp_open_scan) { - if (newstate == PORT_CLOSED || (hss->target->pingprobe_state == PORT_UNKNOWN && newstate == PORT_FILTERED)) - swappingport = true; - } else { - if (hss->target->pingprobe_state == PORT_UNKNOWN && - (newstate == PORT_OPEN || newstate == PORT_CLOSED || newstate == PORT_UNFILTERED)) - swappingport = true; - else if (hss->target->pingprobe_state == PORT_OPEN && (newstate == PORT_CLOSED || newstate == PORT_UNFILTERED)) - swappingport = true; - } - - if (swappingport) { - if (o.debugging > 1) - log_write(LOG_PLAIN, "Changing ping technique for %s to %s\n", hss->target->targetipstr(), pspectype2ascii(pspec->type)); - hss->target->pingprobe = *pspec; - hss->target->pingprobe_state = newstate; - } - } return oldstate != newstate; } @@ -2419,24 +2446,6 @@ static void ultrascan_host_pspec_update(UltraScanInfo *USI, HostScanStats *hss, hss->target->flags |= HOST_DOWN; } else assert(0); } - - /* Consider changing the ping port */ - if (hss->target->pingprobe_state != newstate) { - if (hss->target->pingprobe_state == PORT_UNKNOWN && newstate == HOST_UP) { - if (o.debugging > 1) - log_write(LOG_PLAIN, "Changing ping technique for %s to %s\n", hss->target->targetipstr(), pspectype2ascii(pspec->type)); - hss->target->pingprobe = *pspec; - hss->target->pingprobe_state = newstate; - /* Make this the new global ping host, but only if it's not waiting for - any probes. */ - if (USI->gstats->pinghost == NULL - || USI->gstats->pinghost->probes_outstanding_empty()) { - if (o.debugging > 1) - log_write(LOG_PLAIN, "Changing global ping host to %s.\n", hss->target->targetipstr()); - USI->gstats->pinghost = hss; - } - } - } } /* Called when a new status is determined for host in hss (eg. it is @@ -2463,6 +2472,20 @@ static void ultrascan_host_probe_update(UltraScanInfo *USI, HostScanStats *hss, ultrascan_host_pspec_update(USI, hss, probe->pspec(), newstate); + if (rcvdtime != NULL) { + /* This probe received a positive response. Consider making it the new + timing ping probe. */ + if (pingprobe_is_better(probe->pspec(), PORT_UNKNOWN, &hss->target->pingprobe, hss->target->pingprobe_state)) { + if (o.debugging > 1) { + char buf[32]; + probespec2ascii(probe->pspec(), buf, sizeof(buf)); + log_write(LOG_PLAIN, "Changing ping technique for %s to %s\n", hss->target->targetipstr(), buf); + } + hss->target->pingprobe = *probe->pspec(); + hss->target->pingprobe_state = PORT_UNKNOWN; + } + } + hss->destroyOutstandingProbe(probeI); } @@ -2484,6 +2507,20 @@ static void ultrascan_port_probe_update(UltraScanInfo *USI, HostScanStats *hss, changed = ultrascan_port_pspec_update(USI, hss, pspec, newstate); + if (rcvdtime != NULL) { + /* This probe received a positive response. Consider making it the new + timing ping probe. */ + if (pingprobe_is_better(probe->pspec(), newstate, &hss->target->pingprobe, hss->target->pingprobe_state)) { + if (o.debugging > 1) { + char buf[32]; + probespec2ascii(probe->pspec(), buf, sizeof(buf)); + log_write(LOG_PLAIN, "Changing ping technique for %s to %s\n", hss->target->targetipstr(), buf); + } + hss->target->pingprobe = *probe->pspec(); + hss->target->pingprobe_state = newstate; + } + } + ultrascan_adjust_timeouts(USI, hss, probe, rcvdtime); if (adjust_timing && @@ -2988,7 +3025,7 @@ static void doAnyPings(UltraScanInfo *USI) { for(hostI = USI->incompleteHosts.begin(); hostI != USI->incompleteHosts.end(); hostI++) { hss = *hostI; - if (hss->target->pingprobe_state != PORT_UNKNOWN && + if (hss->target->pingprobe.type != PS_NONE && hss->rld.rld_waiting == false && hss->numprobes_sent >= hss->lastping_sent_numprobes + 10 && TIMEVAL_SUBTRACT(USI->now, hss->lastrcvd) > USI->perf.pingtime &&