1
0
mirror of https://github.com/nmap/nmap.git synced 2025-12-06 12:41:29 +00:00

OS detection changes to improve timing/reliability, print fprint in more cases, etc. Also some tiny changes from Kris Katterjohn

This commit is contained in:
fyodor
2006-08-24 04:06:08 +00:00
parent 1a50feefd3
commit a15e1e0f05
9 changed files with 166 additions and 67 deletions

View File

@@ -114,6 +114,7 @@ FingerPrintResults::FingerPrintResults() {
distance = -1;
distance_guess = -1;
memset(FPs, 0, sizeof(FPs));
maxTimingRatio = 0;
numFPs = goodFP = 0;
}
@@ -134,22 +135,29 @@ const struct OS_Classification_Results *FingerPrintResults::getOSClassification(
return &OSR;
}
/* Are the attributes of this fingerprint good enough to warrant submission to the official DB? */
bool FingerPrintResults::fingerprintSuitableForSubmission() {
/* If the fingerprint is of potentially poor quality, we don't want to
print it and ask the user to submit it. In that case, the reason
for skipping the FP is returned as a static string. If the FP is
great and should be printed, NULL is returned. */
const char *FingerPrintResults::OmitSubmissionFP() {
if (o.scan_delay > 500) // This can screw up the sequence timing
return false;
return "Scan delay is greater than 500";
if (osscan_opentcpport <= 0 || osscan_closedtcpport <= 0 )
/* The results won't be complete */
return false;
if (o.timing_level > 4)
return "Timing level 5 (Insane) used";
if (osscan_opentcpport <= 0)
return "Missing an open TCP port so results incomplete";
if (osscan_closedtcpport <= 0)
return "Missing a closed TCP port so results incomplete";
if (distance > 5)
/* Too far away from us. */
return false;
return "Host more than five network hops away";
if (osscan_closedudpport == 0)
return false; /* Too much risk of goofy results */
if (maxTimingRatio > 1.4)
return "maxTimingRatio is greater than 1.4";
if (osscan_closedudpport < 0 && !o.udpscan) {
/* If we didn't get a U1 response, that might be just
@@ -157,9 +165,10 @@ bool FingerPrintResults::fingerprintSuitableForSubmission() {
because this OS doesn't respond to that sort of probe.
So we don't print FP if U1 response is lacking AND no UDP
scan was performed. */
return false;
return "Didn't receive UDP response. Please try again with -sU";
}
return true;
return NULL;
}

View File

@@ -148,13 +148,22 @@ class FingerPrintResults {
int distance; /* How "far" is this FP gotten from? */
int distance_guess; /* How "far" is this FP gotten from? by guessing based on ttl. */
/* The largest ratio we have seen of time taken vs. target time
between sending 1st tseq probe and sending first ICMP echo probe.
Zero means we didn't see any ratios (the tseq probes weren't
sent), 1 is ideal, and larger values are undesirable from a
consistancy standpoint. */
double maxTimingRatio;
FingerPrint *FPs[10]; /* Fingerprint data obtained from host */
int numFPs;
int goodFP;
/* Are the attributes of this fingerprint good enough to warrant submission to the official DB? */
bool fingerprintSuitableForSubmission();
/* If the fingerprint is of potentially poor quality, we don't want to
print it and ask the user to submit it. In that case, the reason
for skipping the FP is returned as a static string. If the FP is
great and should be printed, NULL is returned. */
const char *OmitSubmissionFP();
private:
bool isClassified; // Whether populateClassification() has been called

View File

@@ -138,7 +138,7 @@ static BOOL OpenLibs(void) {
extern NmapOps o; /* option structure */
extern char **environ;
int main(int argc, char *argv[], char *envp[]) {
int main(int argc, char *argv[]) {
/* The "real" main is nmap_main(). This function hijacks control at the
beginning to do the following:
1) Check if Nmap called under name listed in INTERACTIVE_NAMES or with
@@ -314,7 +314,7 @@ int main(int argc, char *argv[], char *envp[]) {
} else fatal("Arguments too long.");
}
}
/* First we stick our arguments into envp */
if (o.debugging) {
error("Adding to environment: %s", nmapargs);
}

View File

@@ -1570,7 +1570,7 @@ int nmap_main(int argc, char *argv[]) {
}
if (o.osscan == OS_SCAN_DEFAULT || o.osscan == OS_SCAN_SYS_2_ONLY)
os_scan_2(Targets);
os_scan2(Targets);
for(targetno = 0; targetno < Targets.size(); targetno++) {
currenths = Targets[targetno];

View File

@@ -1447,9 +1447,9 @@ o.current_scantype = OS_SCAN;
}
#endif
if (o.debugging > 2) {
if (o.verbose) {
starttimems = o.TimeSinceStartMS();
log_write(LOG_STDOUT|LOG_NORMAL|LOG_SKID, "Initiating OS Detection against %s at %.3fs\n", target->targetipstr(), starttimems / 1000.0);
log_write(LOG_STDOUT|LOG_NORMAL|LOG_SKID, "Initiating gen1 OS Detection against %s at %.3fs\n", target->targetipstr(), starttimems / 1000.0);
}
if (target->FPR1 == NULL)

View File

@@ -114,11 +114,15 @@
// sent to a single host, in milliseconds.
#define OS_PROBE_DELAY 25
// The minimum (and target) amount of time to wait between sequencing
// probes sent to a single host, in milliseconds. It is important
// that the seq probes (which involves 5 gaps) take more than 500ms so
// we can detect timestamps which increase at a frequency of 2Hz.
#define OS_SEQ_PROBE_DELAY 110
// The target amount of time to wait between sequencing probes sent to
// a single host, in milliseconds. The ideal is 500ms because of the
// common 2Hz timestamp frequencies. Less than 500ms and we might not
// see any change in the TS counter (and it gets less accurate even if
// we do). More than 500MS and we risk having two changes (and it
// gets less accurate even if we have just one). So we delay 100MS
// between probes, leaving 500MS between 1st and 6th.
#define OS_SEQ_PROBE_DELAY 100
using namespace std;
extern NmapOps o;
@@ -299,6 +303,13 @@ public:
int distance;
int distance_guess;
/* Returns the amount of time taken between sending 1st tseq probe
and the 1st ICMP probe divided by the amount of time it should
have taken. Ratios far from 1 can cause bogus results. Zero is
returned if we didn't send the tseq probes because there was no
open tcp port */
double timingRatio();
private:
/* Ports of the targets used in os fingerprinting. */
int openTCPPort, closedTCPPort, closedUDPPort;
@@ -355,6 +366,7 @@ private:
*/
u16 lastipid;
struct timeval seq_send_times[NUM_SEQ_SAMPLES];
struct timeval first_icmp_send_time;
int TWinReplyNum; /* how many TWin replies are received. */
int TOpsReplyNum; /* how many TOps replies are received. Actually it is the same with TOpsReplyNum. */
@@ -720,6 +732,7 @@ void HostOsScanStats::initScanStats() {
}
memset(&seq_send_times, 0, sizeof(seq_send_times));
memset(&first_icmp_send_time, 0, sizeof(first_icmp_send_time));
if (icmpEchoReply) {
free(icmpEchoReply);
@@ -779,6 +792,18 @@ void HostOsScanStats::moveProbeToUnSendList(list<OFProbe *>::iterator probeI) {
probesActive.erase(probeI);
}
/* Compute the ratio of amount of time taken between sending 1st TSEQ
probe and 1st ICMP probe compared to the amount of time it should
have taken. Ratios far from 1 can cause bogus results */
double HostOsScanStats::timingRatio() {
if (openTCPPort < 0)
return 0;
int msec_ideal = OS_SEQ_PROBE_DELAY * 5 + OS_PROBE_DELAY;
int msec_taken = TIMEVAL_MSEC_SUBTRACT(first_icmp_send_time, seq_send_times[0]);
return (double) msec_taken / msec_ideal;
}
/* If there are pending probe timeouts, fills in when with the time of
* the earliest one and returns true. Otherwise returns false and
* puts now in when.
@@ -1351,6 +1376,7 @@ void HostOsScan::sendTIcmpProbe(HostOsScanStats *hss, int probeNo) {
assert(hss);
assert(probeNo>=0&&probeNo<2);
if(probeNo==0) {
gettimeofday(&hss->first_icmp_send_time, NULL);
send_icmp_echo_probe(rawsd, ethptr, hss->target->v4hostip(), IP_TOS_DEFAULT,
true, 9, icmpEchoId, icmpEchoSeq, 120);
}
@@ -1651,7 +1677,8 @@ void HostOsScan::makeTSeqFP(HostOsScanStats *hss) {
if (hss->si.lastboot && (hss->seq_send_times[0].tv_sec - hss->si.lastboot > 63072000)) {
/* Up 2 years? Perhaps, but they're probably lying. */
if (o.debugging) {
error("Ignoring claimed uptime of %lu days",
error("Ignoring claimed %s uptime of %lu days",
hss->target->targetipstr(),
(hss->seq_send_times[0].tv_sec - hss->si.lastboot) / 86400);
}
hss->si.lastboot = 0;
@@ -3639,6 +3666,7 @@ static void endRound(OsScanInfo *OSI, HostOsScan *HOS, int roundNum) {
hsi->FPs[roundNum] = hsi->hss->getFP();
hsi->target->FPR->FPs[roundNum] = hsi->FPs[roundNum];
hsi->target->FPR->maxTimingRatio = MAX(hsi->target->FPR->maxTimingRatio, hsi->hss->timingRatio());
match_fingerprint(hsi->FPs[roundNum], &hsi->FP_matches[roundNum],
o.reference_FPs, OSSCAN_GUESS_THRESHOLD);
@@ -3753,14 +3781,20 @@ static void doOsScan1(OsScanInfo *OSI) {
for(hostI = OSI->incompleteHosts.begin();
hostI != OSI->incompleteHosts.end(); hostI++) {
if(o.verbose)
log_write(LOG_STDOUT, "OSScan against host %s now falls back on the old OS scan system\n",
(*hostI)->target->targetipstr());
/* If the fingerprint found was so good that we want the user to
submit it, don't do gen1 os scan because the results might bias
the user into a wrong submission (or make the user less likely
to actually submit */
if ((*hostI)->target->FPR->OmitSubmissionFP()) {
os_scan((*hostI)->target);
}
}
}
int os_scan_2(vector<Target *> &Targets) {
/* You should call os_scan2 rather than this function */
static int os_scan_2(vector<Target *> &Targets) {
OsScanInfo *OSI;
HostOsScan *HOS;
int itry;
@@ -3783,6 +3817,9 @@ int os_scan_2(vector<Target *> &Targets) {
startTimeOutClocks(OSI);
itry = 0;
begin_sniffer(HOS, Targets); /* initial the pcap session handler in HOS */
while(OSI->numIncompleteHosts() != 0 && itry < MAX_SCAN_ROUND) {
if (itry > 0) sleep(1);
@@ -3808,14 +3845,48 @@ int os_scan_2(vector<Target *> &Targets) {
printFP(OSI);
/*
* OK. Now let's fall back on the former os_scan engine which has
* For the incomplete hosts, we fall back on the former os_scan engine which has
* a larger os-fingerprint db.
*/
if(o.osscan != OS_SCAN_SYS_2_ONLY)
if(o.osscan != OS_SCAN_SYS_2_ONLY) {
doOsScan1(OSI);
}
}
delete HOS;
delete OSI;
return 0;
}
/* This is the primary OS detection function. If many Targets are
passed in (the threshold is based on timing level), they are
processed as smaller groups to improve accuracy */
void os_scan2(vector<Target *> &Targets) {
unsigned int max_os_group_sz = 20;
double fudgeratio = 1.2; /* Allow a slightly larger final group rather than finish with a tiny one */
vector<Target *> tmpTargets;
unsigned int startidx = 0;
if (o.timing_level == 4)
max_os_group_sz = (unsigned int) (max_os_group_sz * 1.5);
if (o.timing_level > 4 || Targets.size() <= max_os_group_sz * fudgeratio) {
os_scan_2(Targets);
return;
}
/* We need to split it up */
while(startidx < Targets.size()) {
int diff = Targets.size() - startidx;
if (diff > max_os_group_sz * fudgeratio) {
diff = max_os_group_sz;
}
tmpTargets.assign(Targets.begin() + startidx,
Targets.begin() + startidx + diff);
os_scan_2(tmpTargets);
startidx += diff;
}
return;
}

View File

@@ -111,7 +111,11 @@
/********************** PROTOTYPES ***********************************/
int os_scan_2(std::vector<Target *> &Targets);
/* This is the primary OS detection function. If many Targets are
passed in (the threshold is based on timing level), they are
processed as smaller groups to improve accuracy */
void os_scan2(std::vector<Target *> &Targets);
int send_closedudp_probe_2(struct udpprobeinfo &upi, int sd,
struct eth_nfo *eth, const struct in_addr *victim,

View File

@@ -1276,7 +1276,7 @@ void printosscanoutput(Target *currenths) {
// If the FP can't be submitted anyway, might as well make a guess.
printosclassificationoutput(FPR->getOSClassification(),
o.osscan_guess || !FPR->fingerprintSuitableForSubmission());
o.osscan_guess || FPR->OmitSubmissionFP());
if (FPR->overall_results == OSSCAN_SUCCESS && (FPR->num_perfect_matches <= 8 || o.debugging)) {
if (FPR->num_perfect_matches > 0) {
@@ -1312,7 +1312,10 @@ void printosscanoutput(Target *currenths) {
}
}
} else {
if ((o.osscan_guess || !FPR->fingerprintSuitableForSubmission()) && FPR->num_matches > 0) {
const char *reason = FPR->OmitSubmissionFP();
if ((o.verbose > 1 || o.debugging) && reason)
log_write(LOG_NORMAL|LOG_SKID_NOXLT|LOG_STDOUT,"OS fingerprint not ideal because: %s\n", reason);
if ((o.osscan_guess || reason) && FPR->num_matches > 0) {
/* Print the best guesses available */
log_write(LOG_NORMAL|LOG_SKID|LOG_STDOUT,"Aggressive OS guesses: %s (%d%%)", FPR->prints[0]->OS_name, (int) (FPR->accuracy[0] * 100));
for(i=1; i < 10 && FPR->num_matches > i && FPR->accuracy[i] > FPR->accuracy[0] - 0.10; i++) {
@@ -1326,7 +1329,7 @@ void printosscanoutput(Target *currenths) {
}
log_write(LOG_NORMAL|LOG_SKID|LOG_STDOUT, "\n");
}
if (osscanSys == 2 && FPR->fingerprintSuitableForSubmission()) {
if (osscanSys == 2 && !reason) {
log_write(LOG_NORMAL|LOG_SKID_NOXLT|LOG_STDOUT,"No exact OS matches for host (If you know what OS is running on it, see http://www.insecure.org/cgi-bin/nmap-submit.cgi).\nTCP/IP fingerprint:\n%s\n",
mergeFPs(FPR->FPs, FPR->numFPs, true,
currenths->v4hostip(), distance, currenths->MACAddress(),
@@ -1349,14 +1352,17 @@ void printosscanoutput(Target *currenths) {
log_write(LOG_NORMAL|LOG_SKID|LOG_STDOUT,"OS Fingerprint:\n%s\n", fp2ascii(FPR->FPs[FPR->goodFP]));
}
} else if (FPR->overall_results == OSSCAN_NOMATCHES) {
if (osscanSys == 2 && FPR->fingerprintSuitableForSubmission()) {
const char *reason = FPR->OmitSubmissionFP();
if ((o.verbose > 1 || o.debugging) && reason)
log_write(LOG_NORMAL|LOG_SKID_NOXLT|LOG_STDOUT,"OS fingerprint not ideal because: %s\n", reason);
if (osscanSys == 2 && !reason) {
log_write(LOG_NORMAL|LOG_SKID_NOXLT|LOG_STDOUT,"No OS matches for host (If you know what OS is running on it, see http://www.insecure.org/cgi-bin/nmap-submit.cgi).\nTCP/IP fingerprint:\n%s\n",
mergeFPs(FPR->FPs, FPR->numFPs, true,
currenths->v4hostip(), distance, currenths->MACAddress(),
FPR->osscan_opentcpport, FPR->osscan_closedtcpport, FPR->osscan_closedudpport,
true));
} else {
log_write(LOG_NORMAL|LOG_SKID_NOXLT|LOG_STDOUT,"No OS matches for host (test conditions non-ideal).\n");
log_write(LOG_NORMAL|LOG_SKID_NOXLT|LOG_STDOUT,"No OS matches for host\n");
if (o.verbose > 1)
log_write(LOG_NORMAL|LOG_SKID_NOXLT|LOG_STDOUT, "\nTCP/IP fingerprint by osscan system #%d:\n%s",
osscanSys, mergeFPs(FPR->FPs, FPR->numFPs, false,

View File

@@ -785,7 +785,7 @@ int resolve(char *hostname, struct sockaddr_storage *ss, size_t *sslen,
int islocalhost(const struct in_addr * const addr) {
char dev[128];
/* If it is 0.0.0.0 or starts with 127.0.0.1 then it is
/* If it is 0.0.0.0 or starts with 127 then it is
probably localhost */
if ((addr->s_addr & htonl(0xFF000000)) == htonl(0x7F000000))
return 1;