Skip to content

Commit

Permalink
Adjust how timeouts are handled with labeling services tcpwrapped. Fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
bonsaiviking committed Jun 1, 2015
1 parent 61ebb11 commit 90a9f8b
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 9 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG
Expand Up @@ -29,6 +29,11 @@ o Solve a crash on Windows (reported on Windows 8.1 on Surface Pro 3) caused by
passing a NULL pointer to a WinPcap function that then tries to write an
error message to it. [Peter Malecka]

o Enhance Nmap's tcpwrapped service detection by using a shorter timeout for
the tcpwrapped designation. This prevents falsely labeling services as
tcpwrapped which merely have a read timeout shorter than 6 seconds. Full
discussion: http://issues.nmap.org/39 [nnposter, Daniel Miller]

o Integrated all of your IPv6 OS fingerprint submissions from June 2013 to
April 2015 (only 97 of them!). We are steadily improving the IPv6 database,
but we need your submissions. The classifier added 9 new groups, bringing the
Expand Down
3 changes: 3 additions & 0 deletions nmap-service-probes
Expand Up @@ -35,6 +35,9 @@ Probe TCP NULL q||
# smtp services have lately been instituting an artificial pause (see
# FEATURE('greet_pause') in Sendmail, for example)
totalwaitms 6000
# If the service closes the connection before 3 seconds, it's probably
# tcpwrapped. Adjust up or down depending on your false-positive rate.
tcpwrappedms 3000

match 1c-server m|^S\xf5\xc6\x1a{| p/1C:Enterprise business management server/

Expand Down
45 changes: 36 additions & 9 deletions service_scan.cc
Expand Up @@ -218,6 +218,9 @@ class ServiceNFO {
// when SSL is detected -- we redo all probes through SSL. If freeFP, any
// service fingerprint is freed too.
void resetProbes(bool freefp);
// Number of milliseconds used so far to complete the present probe. Timeval
// can omitted, it is just there as an optimization in case you have it handy.
int probe_timemsused(const ServiceProbe *probe, const struct timeval *now = NULL);
// Number of milliseconds left to complete the present probe, or 0 if
// the probe is already expired. Timeval can omitted, it is just there
// as an optimization in case you have it handy.
Expand All @@ -235,6 +238,9 @@ class ServiceNFO {
// INVALIDATED if you call appendtocurrentproberesponse() or nextProbe()
u8 *getcurrentproberesponse(int *respstrlen);
AllProbes *AP;
// Is it possible this service is tcpwrapped? Not if a probe times out or
// gets a real response.
bool tcpwrap_possible;

private:
// Adds a character to servicefp. Takes care of word wrapping if
Expand Down Expand Up @@ -1044,6 +1050,7 @@ ServiceProbe::ServiceProbe() {
probename = NULL;
probestring = NULL;
totalwaitms = DEFAULT_SERVICEWAITMS;
tcpwrappedms = DEFAULT_TCPWRAPPEDMS;
probestringlen = 0; probeprotocol = -1;
// The default rarity level for a probe without a rarity
// directive - should almost never have to be relied upon.
Expand Down Expand Up @@ -1314,6 +1321,11 @@ void parse_nmap_service_probe_file(AllProbes *AP, char *filename) {
if (waitms < 100 || waitms > 300000)
fatal("Error on line %d of nmap-service-probes file (%s): bad totalwaitms value. Must be between 100 and 300000 milliseconds", lineno, filename);
newProbe->totalwaitms = waitms;
} else if (strncmp(line, "tcpwrappedms ", 12) == 0) {
long waitms = strtol(line + 12, NULL, 10);
if (waitms < 100 || waitms > 300000)
fatal("Error on line %d of nmap-service-probes file (%s): bad tcpwrappedms value. Must be between 100 and 300000 milliseconds", lineno, filename);
newProbe->tcpwrappedms = waitms;
} else if (strncmp(line, "match ", 6) == 0 || strncmp(line, "softmatch ", 10) == 0) {
newProbe->addMatch(line, lineno);
} else if (strncmp(line, "Exclude ", 8) == 0) {
Expand Down Expand Up @@ -1564,6 +1576,7 @@ ServiceNFO::ServiceNFO(AllProbes *newAP) {
softMatchFound = false;
servicefplen = servicefpalloc = 0;
servicefp = NULL;
tcpwrap_possible = true;
memset(&currentprobe_exec_time, 0, sizeof(currentprobe_exec_time));
}

Expand Down Expand Up @@ -1816,9 +1829,8 @@ void ServiceNFO::resetProbes(bool freefp) {
probe_state = PROBESTATE_INITIAL;
}


int ServiceNFO::probe_timemsleft(const ServiceProbe *probe, const struct timeval *now) {
int timeused, timeleft;
int ServiceNFO::probe_timemsused(const ServiceProbe *probe, const struct timeval *now) {
int timeused;

if (now)
timeused = TIMEVAL_MSEC_SUBTRACT(*now, currentprobe_exec_time);
Expand All @@ -1832,7 +1844,16 @@ int ServiceNFO::probe_timemsleft(const ServiceProbe *probe, const struct timeval
// probe == currentProbe(). Check that this remains the case.
assert(probe == currentProbe());

timeleft = probe->totalwaitms - timeused;
return timeused;
}

int ServiceNFO::probe_timemsleft(const ServiceProbe *probe, const struct timeval *now) {

// Historically this function was always called with the assumption that
// probe == currentProbe(). Check that this remains the case.
assert(probe == currentProbe());

int timeleft = probe->totalwaitms - probe_timemsused(probe, now);
return (timeleft < 0)? 0 : timeleft;
}

Expand Down Expand Up @@ -2154,7 +2175,7 @@ static void end_svcprobe(nsock_pool nsp, enum serviceprobestate probe_state, Ser
std::list<ServiceNFO *>::iterator member;
Target *target = svc->target;

svc->probe_state = probe_state;
svc->probe_state = svc->tcpwrap_possible ? PROBESTATE_FINISHED_TCPWRAPPED : probe_state;
member = find(SG->services_in_progress.begin(), SG->services_in_progress.end(),
svc);
if (member != SG->services_in_progress.end()) {
Expand Down Expand Up @@ -2378,9 +2399,11 @@ static void servicescan_read_handler(nsock_pool nsp, nsock_event nse, void *myda
assert(type == NSE_TYPE_READ);

if (svc->target->timedOut(nsock_gettimeofday())) {
svc->tcpwrap_possible = false;
end_svcprobe(nsp, PROBESTATE_INCOMPLETE, SG, svc, nsi);
} else if (status == NSE_STATUS_SUCCESS) {
// w00p, w00p, we read something back from the port.
svc->tcpwrap_possible = false;
readstr = (u8 *) nse_readbuf(nse, &readstrlen);
adjustPortStateIfNecessary(svc); /* A response means PORT_OPENFILTERED is really PORT_OPEN */
svc->appendtocurrentproberesponse(readstr, readstrlen);
Expand Down Expand Up @@ -2466,6 +2489,7 @@ static void servicescan_read_handler(nsock_pool nsp, nsock_event nse, void *myda
// move on to the next probe. If this was a NULL probe, we can simply
// send the new probe text immediately. Otherwise we make a new connection.

svc->tcpwrap_possible = false;
readstr = svc->getcurrentproberesponse(&readstrlen);
if (readstrlen > 0)
svc->addToServiceFingerprint(svc->currentProbe()->getName(), readstr,
Expand All @@ -2477,14 +2501,15 @@ static void servicescan_read_handler(nsock_pool nsp, nsock_event nse, void *myda
// If this was during the NULL probe, let's (for now) assume
// the port is TCP wrapped. Otherwise, we'll treat it as a nomatch
readstr = svc->getcurrentproberesponse(&readstrlen);
if (readstrlen > 0)
if (readstrlen > 0) {
svc->addToServiceFingerprint(svc->currentProbe()->getName(), readstr,
readstrlen);
if (probe->isNullProbe() && readstrlen == 0) {
svc->tcpwrap_possible = false;
}
if (svc->tcpwrap_possible && probe->isNullProbe() && readstrlen == 0 && svc->probe_timemsused(probe) < probe->tcpwrappedms) {
// TODO: Perhaps should do further verification before making this assumption
end_svcprobe(nsp, PROBESTATE_FINISHED_TCPWRAPPED, SG, svc, nsi);
} else {

// Perhaps this service didn't like the particular probe text.
// We'll try the next one
startNextProbe(nsp, nsi, SG, svc, true);
Expand All @@ -2498,7 +2523,7 @@ static void servicescan_read_handler(nsock_pool nsp, nsock_event nse, void *myda
// BSD sometimes gives it
case ECONNABORTED:
// Jerk hung up on us. Probably didn't like our probe. We treat it as with EOF above.
if (probe->isNullProbe()) {
if (svc->tcpwrap_possible && probe->isNullProbe() && svc->probe_timemsused(probe) < probe->tcpwrappedms) {
// TODO: Perhaps should do further verification before making this assumption
end_svcprobe(nsp, PROBESTATE_FINISHED_TCPWRAPPED, SG, svc, nsi);
} else {
Expand All @@ -2520,6 +2545,7 @@ static void servicescan_read_handler(nsock_pool nsp, nsock_event nse, void *myda
case EHOSTUNREACH:
// That is funny. The port scanner listed the port as open. Maybe it got unplugged, or firewalled us, or did
// something else nasty during the scan. Shrug. I'll give up on this port
svc->tcpwrap_possible = false;
end_svcprobe(nsp, PROBESTATE_INCOMPLETE, SG, svc, nsi);
break;
#ifdef ENOPROTOOPT
Expand Down Expand Up @@ -2559,6 +2585,7 @@ static void servicescan_read_handler(nsock_pool nsp, nsock_event nse, void *myda
} else if (status == NSE_STATUS_KILL) {
/* User probably specified host_timeout and so the service scan is
shutting down */
svc->tcpwrap_possible = false;
end_svcprobe(nsp, PROBESTATE_INCOMPLETE, SG, svc, nsi);
return;
} else {
Expand Down
3 changes: 3 additions & 0 deletions service_scan.h
Expand Up @@ -141,6 +141,7 @@

/********************** DEFINES/ENUMS ***********************************/
#define DEFAULT_SERVICEWAITMS 5000
#define DEFAULT_TCPWRAPPEDMS 2000 // connections closed after this timeout are not considered "tcpwrapped"
#define DEFAULT_CONNECT_TIMEOUT 5000
#define DEFAULT_CONNECT_SSL_TIMEOUT 8000 // includes connect() + ssl negotiation
#define SERVICEMATCH_REGEX 1
Expand Down Expand Up @@ -264,6 +265,8 @@ class ServiceProbe {
// probe (e.g. an SMTP probe would commonly identify port 25)
// Amount of time to wait after a connection succeeds (or packet sent) for a responses.
int totalwaitms;
// If the connection succeeds but closes before this time, it's tcpwrapped.
int tcpwrappedms;

// Parses the "probe " line in the nmap-service-probes file. Pass the rest of the line
// after "probe ". The format better be:
Expand Down

0 comments on commit 90a9f8b

Please sign in to comment.