diff --git a/CHANGELOG b/CHANGELOG index 04c274b49..da614c554 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -2,7 +2,7 @@ 4.20ALPHA5 o Worked with Zhao to improve the new OS detection system with - better algorithms and bug fixes. + better algorithms, some probe changes, and some bug fixes. o Nmap now uses the (relatively) new libpcap pcap_get_selectable_fd API on systems which support it. This means that we no longer need diff --git a/docs/nmap.1 b/docs/nmap.1 index 323d131d0..7d662769c 100644 --- a/docs/nmap.1 +++ b/docs/nmap.1 @@ -2,7 +2,7 @@ .\" It was generated using the DocBook XSL Stylesheets (version 1.69.1). .\" Instead of manually editing it, you probably should edit the DocBook XML .\" source for it and then use the DocBook XSL Stylesheets to regenerate it. -.TH "NMAP" "1" "08/16/2006" "" "Nmap Reference Guide" +.TH "NMAP" "1" "08/19/2006" "" "Nmap Reference Guide" .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) diff --git a/osscan2.cc b/osscan2.cc index 1ee6a6e5a..4aac586dd 100644 --- a/osscan2.cc +++ b/osscan2.cc @@ -301,7 +301,7 @@ public: private: /* Ports of the targets used in os fingerprinting. */ - unsigned long openTCPPort, closedTCPPort, closedUDPPort; + int openTCPPort, closedTCPPort, closedUDPPort; /* Probe list used in tests. At first, probes are linked in * probesToSend; when a probe is sent, it will be removed from @@ -567,9 +567,9 @@ HostOsScanStats::HostOsScanStats(Target * t) { target = t; FP = NULL; - openTCPPort = (unsigned int)-1; - closedTCPPort = (unsigned int)-1; - closedUDPPort = (unsigned int)-1; + openTCPPort = -1; + closedTCPPort = -1; + closedUDPPort = -1; num_probes_sent = 0; sendDelayMs = MAX(o.scan_delay, OS_PROBE_DELAY); @@ -631,7 +631,7 @@ void HostOsScanStats::initScanStats() { int i; /* Lets find an open port to use if we don't already have one */ - openTCPPort = (unsigned long) -1; + openTCPPort = -1; /* target->FPR->osscan_opentcpport = -1; target->FPR->osscan_closedtcpport = -1; target->FPR->osscan_closedudpport = -1; */ @@ -694,8 +694,8 @@ void HostOsScanStats::initScanStats() { closedUDPPort = (get_random_uint() % 14781) + 30000; } - if (o.verbose && openTCPPort != (unsigned long) -1) - log_write(LOG_STDOUT, "OSScan against host %s: assuming TCP port %lu is open, %lu is closed, UDP port %lu is closed and none is firewalled\n", + if (o.verbose && openTCPPort != -1) + log_write(LOG_STDOUT, "OSScan against host %s: assuming TCP port %d is open, %d is closed, UDP port %d is closed and none is firewalled\n", target->targetipstr(), openTCPPort, closedTCPPort, closedUDPPort); FP = NULL; @@ -933,7 +933,7 @@ void HostOsScan::reInitScanSystem() { void HostOsScan::buildSeqProbeList(HostOsScanStats *hss) { assert(hss); int i; - if(hss->openTCPPort == (unsigned long)-1) return; + if(hss->openTCPPort == -1) return; if(hss->FP_TSeq) return; for(i=0; iaddNewProbe(OFP_TUDP, 0); } - if(hss->openTCPPort != (unsigned long)-1) { + if(hss->openTCPPort != -1) { /* tops/twin probes. We send the probe again if we didn't get a response by the corresponding seq probe. */ @@ -1254,7 +1254,7 @@ void HostOsScan::sendTSeqProbe(HostOsScanStats *hss, int probeNo) { assert(hss); assert(probeNo >= 0 && probeNo < NUM_SEQ_SAMPLES); - if(hss->openTCPPort == (unsigned long)-1) return; + if(hss->openTCPPort == -1) return; send_tcp_raw_decoys(rawsd, ethptr, hss->target->v4hostip(), o.ttl, false, tcpPortBase + probeNo, hss->openTCPPort, @@ -1269,21 +1269,24 @@ void HostOsScan::sendTOpsProbe(HostOsScanStats *hss, int probeNo) { assert(hss); assert(probeNo>=0 && probeNo< NUM_SEQ_SAMPLES); - if(hss->openTCPPort == (unsigned long)-1) return; + if(hss->openTCPPort == -1) return; send_tcp_raw_decoys(rawsd, ethptr, hss->target->v4hostip(), o.ttl, false, - tcpPortBase + NUM_SEQ_SAMPLES + probeNo, hss->openTCPPort, tcpSeqBase, - tcpAck, 0, TH_SYN, prbWindowSz[probeNo], 0, prbOpts[probeNo].val, prbOpts[probeNo].len, NULL, 0); + tcpPortBase + NUM_SEQ_SAMPLES + probeNo, + hss->openTCPPort, tcpSeqBase, tcpAck, 0, TH_SYN, + prbWindowSz[probeNo], 0, prbOpts[probeNo].val, + prbOpts[probeNo].len, NULL, 0); } void HostOsScan::sendTEcnProbe(HostOsScanStats *hss) { assert(hss); - if(hss->openTCPPort == (unsigned long)-1) return; + if(hss->openTCPPort == -1) return; send_tcp_raw_decoys(rawsd, ethptr, hss->target->v4hostip(), o.ttl, false, - tcpPortBase + NUM_SEQ_SAMPLES + 6, hss->openTCPPort, tcpSeqBase, 0, 8, - TH_CWR|TH_ECE|TH_SYN, prbWindowSz[6], 63477, prbOpts[6].val, prbOpts[6].len, NULL, 0); + tcpPortBase + NUM_SEQ_SAMPLES + 6, hss->openTCPPort, + tcpSeqBase, 0, 8, TH_CWR|TH_ECE|TH_SYN, prbWindowSz[6], + 63477, prbOpts[6].val, prbOpts[6].len, NULL, 0); } void HostOsScan::sendT1_7Probe(HostOsScanStats *hss, int probeNo) { @@ -1294,51 +1297,51 @@ void HostOsScan::sendT1_7Probe(HostOsScanStats *hss, int probeNo) { switch(probeNo) { case 0: /* T1 */ - if(hss->openTCPPort == (unsigned long)-1) return; + if(hss->openTCPPort == -1) return; send_tcp_raw_decoys(rawsd, ethptr, hss->target->v4hostip(), o.ttl, false, port_base, hss->openTCPPort, tcpSeqBase, tcpAck, 0, TH_SYN, prbWindowSz[0], 0, prbOpts[0].val, prbOpts[0].len, NULL, 0); break; case 1: /* T2 */ - if(hss->openTCPPort == (unsigned long)-1) return; + if(hss->openTCPPort == -1) return; send_tcp_raw_decoys(rawsd, ethptr, hss->target->v4hostip(), o.ttl, true, port_base + 1, hss->openTCPPort, tcpSeqBase, tcpAck, 0, 0, prbWindowSz[7], 0, prbOpts[7].val, prbOpts[7].len, NULL, 0); break; case 2: /* T3 */ - if(hss->openTCPPort == (unsigned long)-1) return; + if(hss->openTCPPort == -1) return; send_tcp_raw_decoys(rawsd, ethptr, hss->target->v4hostip(), o.ttl, false, port_base + 2, hss->openTCPPort, tcpSeqBase, tcpAck, 0, TH_SYN|TH_FIN|TH_URG|TH_PUSH, prbWindowSz[8], 0, prbOpts[8].val, prbOpts[8].len, NULL, 0); break; case 3: /* T4 */ - if(hss->openTCPPort == (unsigned long)-1) return; + if(hss->openTCPPort == -1) return; send_tcp_raw_decoys(rawsd, ethptr, hss->target->v4hostip(), o.ttl, true, port_base + 3, hss->openTCPPort, tcpSeqBase, tcpAck, 0, TH_ACK, prbWindowSz[9], 0, prbOpts[9].val, prbOpts[9].len, NULL, 0); break; case 4: /* T5 */ - if(hss->closedTCPPort == (unsigned long)-1) return; + if(hss->closedTCPPort == -1) return; send_tcp_raw_decoys(rawsd, ethptr, hss->target->v4hostip(), o.ttl, false, - port_base + 4, hss->closedTCPPort, tcpSeqBase, tcpAck, 0, - TH_SYN, prbWindowSz[10], 0, prbOpts[10].val, + port_base + 4, hss->closedTCPPort, tcpSeqBase, tcpAck, + 0, TH_SYN, prbWindowSz[10], 0, prbOpts[10].val, prbOpts[10].len, NULL, 0); break; case 5: /* T6 */ - if(hss->closedTCPPort == (unsigned long)-1) return; + if(hss->closedTCPPort == -1) return; send_tcp_raw_decoys(rawsd, ethptr, hss->target->v4hostip(), o.ttl, true, - port_base + 5, hss->closedTCPPort, tcpSeqBase, tcpAck, 0, - TH_ACK, prbWindowSz[11], 0, prbOpts[11].val, + port_base + 5, hss->closedTCPPort, tcpSeqBase, tcpAck, + 0, TH_ACK, prbWindowSz[11], 0, prbOpts[11].val, prbOpts[11].len, NULL, 0); break; case 6: /* T7 */ - if(hss->closedTCPPort == (unsigned long)-1) return; + if(hss->closedTCPPort == -1) return; send_tcp_raw_decoys(rawsd, ethptr, hss->target->v4hostip(), o.ttl, false, - port_base + 6, hss->closedTCPPort, tcpSeqBase, tcpAck, 0, - TH_FIN|TH_PUSH|TH_URG, prbWindowSz[12], 0, + port_base + 6, hss->closedTCPPort, tcpSeqBase, tcpAck, + 0, TH_FIN|TH_PUSH|TH_URG, prbWindowSz[12], 0, prbOpts[12].val, prbOpts[12].len, NULL, 0); break; } @@ -1360,7 +1363,7 @@ void HostOsScan::sendTIcmpProbe(HostOsScanStats *hss, int probeNo) { void HostOsScan::sendTUdpProbe(HostOsScanStats *hss, int probeNo) { assert(hss); - if(hss->closedUDPPort == (unsigned long)-1) return; + if(hss->closedUDPPort == -1) return; send_closedudp_probe_2(hss->upi, rawsd, ethptr, hss->target->v4hostip(), this->udpttl, udpPortBase + probeNo, hss->closedUDPPort); @@ -1511,7 +1514,10 @@ void HostOsScan::makeFP(HostOsScanStats *hss) { makeTWinFP(hss); for(i=3; i < NUM_FPTESTS; i++) { - if (!hss->FPtests[i] && ((hss->openTCPPort != (unsigned long) -1) || i > 7)) { + if (!hss->FPtests[i] && + ((i>=3 && i<=7 && hss->openTCPPort != -1) || + (i>=8 && i<=10 && hss->target->FPR->osscan_closedtcpport != -1) || + i>=11)) { /* We create a Resp (response) attribute with value of N (no) because it is important here to note whether responses were or were not received */ @@ -1976,7 +1982,7 @@ bool HostOsScan::processTSeqResp(HostOsScanStats *hss, struct ip *ip, int replyN if ((tcp->th_flags & TH_RST)) { if (hss->si.responses == 0) { - fprintf(stderr, "WARNING: RST from %s port %lu -- is this port really open?\n", + fprintf(stderr, "WARNING: RST from %s port %d -- is this port really open?\n", hss->target->targetipstr(), hss->openTCPPort); } return false; @@ -2476,7 +2482,7 @@ bool HostOsScan::processTUdpResp(HostOsScanStats *hss, struct ip *ip) { checksum = *checksumptr; if (checksum == 0) - strcpy(AVs[current_testno].value, "0"); + strcpy(AVs[current_testno].value, "Z"); else { *checksumptr = 0; if (in_cksum((unsigned short *)ip2, 20) == checksum) { diff --git a/tcpip.cc b/tcpip.cc index 8ef8cdebe..f019aa81a 100644 --- a/tcpip.cc +++ b/tcpip.cc @@ -262,6 +262,142 @@ void PacketTrace::traceArp(pdirection pdir, const u8 *frame, u32 len, return; } +/* Get an ASCII information about a tcp option which is pointed by + optp, with a length of len. The result is stored in the result + buffer. The result may look like "" */ +static void tcppacketoptinfo(u8 *optp, int len, char *result, int bufsize) { + assert(optp); + assert(result); + char *p, ch; + u8 *q; + int opcode; + u16 tmpshort; + u32 tmpword1, tmpword2; + + p = result; *p = '\0'; + q = optp; + ch = '<'; + + while(len > 0 && bufsize > 2) { + snprintf(p, bufsize, "%c", ch); + bufsize--; + p++; + opcode=*q++; + if (!opcode) { /* End of List */ + + snprintf(p, bufsize, "eol"); + bufsize -= strlen(p); + p += strlen(p); + + len--; + + } else if (opcode == 1) { /* No Op */ + + snprintf(p, bufsize, "nop"); + bufsize -= strlen(p); + p += strlen(p); + + len--; + + } else if (opcode == 2) { /* MSS */ + + if(len<4) + break; /* MSS has 4 bytes */ + + q++; + memcpy(&tmpshort, q, 2); + + snprintf(p, bufsize, "mss %u", ntohs(tmpshort)); + bufsize -= strlen(p); + p += strlen(p); + + q += 2; + len -= 4; + + } else if (opcode == 3) { /* Window Scale */ + + if(len<3) + break; /* Window Scale option has 3 bytes */ + + q++; + + snprintf(p, bufsize, "wsacle %u", *q); + bufsize -= strlen(p); + p += strlen(p); + + q++; + len -= 3; + + } else if (opcode == 4) { /* SACK permitted */ + + if(len<2) + break; /* SACK permitted option has 2 bytes */ + + snprintf(p, bufsize, "sackOK"); + bufsize -= strlen(p); + p += strlen(p); + + q++; + len -= 2; + + } else if (opcode == 5) { /* SACK */ + + int sackoptlen = *q; + if(len < sackoptlen) + break; + + q++; + + if((sackoptlen-2) % 8 != 0) { + snprintf(p, bufsize, "malformed sack"); + bufsize -= strlen(p); + p += strlen(p); + } else { + snprintf(p, bufsize, "sack %d ", (sackoptlen-2)/8); + bufsize -= strlen(p); + p += strlen(p); + for(int i = 0; i < sackoptlen - 2; i += 8) { + memcpy(&tmpword1, q + i, 4); + memcpy(&tmpword2, q + i + 4, 4); + snprintf(p, bufsize, "{%u:%u}", tmpword1, tmpword2); + bufsize -= strlen(p); + p += strlen(p); + } + } + + q += sackoptlen-2; + len -= sackoptlen; + + } else if (opcode == 8) { /* Timestamp */ + + if(len<10) + break; /* Timestamp option has 10 bytes */ + + q++; + memcpy(&tmpword1, q, 4); + memcpy(&tmpword2, q+4, 4); + + snprintf(p, bufsize, "timestamp %u %u", ntohl(tmpword1), ntohl(tmpword2)); + bufsize -= strlen(p); + p += strlen(p); + + q += 8; + len -= 10; + + } + + ch = ','; + } + + if(len > 0) { + *result = '\0'; + return; + } + + snprintf(p, bufsize, ">"); +} + /* Returns a buffer of ASCII information about a packet that may look like "TCP 127.0.0.1:50923 > 127.0.0.1:3 S ttl=61 id=39516 iplen=40 seq=625950769" or "ICMP PING (0/1) ttl=61 id=39516 iplen=40". @@ -269,7 +405,7 @@ void PacketTrace::traceArp(pdirection pdir, const u8 *frame, u32 len, within (say) printf(). And certainly don't try to free() it! The returned buffer is NUL-terminated */ static const char *ippackethdrinfo(const u8 *packet, u32 len) { - static char protoinfo[256]; + static char protoinfo[512]; struct ip *ip = (struct ip *) packet; struct tcphdr *tcp; udphdr_bsd *udp; @@ -279,7 +415,6 @@ static const char *ippackethdrinfo(const u8 *packet, u32 len) { struct in_addr saddr, daddr; int frag_off = 0, more_fragments = 0; char fragnfo[64] = ""; - char tflags[10]; if (ip->ip_v != 4) return "BOGUS! IP Version in packet is not 4"; @@ -303,12 +438,15 @@ static const char *ippackethdrinfo(const u8 *packet, u32 len) { ip->ip_ttl, ntohs(ip->ip_id), ntohs(ip->ip_len), fragnfo); if (ip->ip_p == IPPROTO_TCP) { + char tflags[10]; char tcpinfo[64] = ""; char buf[32]; + char tcpoptinfo[256] = ""; + tcp = (struct tcphdr *) (packet + ip->ip_hl * 4); if (frag_off > 8 || len < (u32) ip->ip_hl * 4 + 8) snprintf(protoinfo, sizeof(protoinfo), "TCP %s:?? > %s:?? ?? %s (incomplete)", srchost, dsthost, ipinfo); - else if (frag_off == 8 && len >= (u32) ip->ip_hl * 4 + 8) {// we can get TCP flags and ACKn + else if (frag_off == 8) {// at least we can get TCP flags and ACKn tcp = (struct tcphdr *)((u8 *) tcp - frag_off); // ugly? p = tflags; /* These are basically in tcpdump order */ @@ -326,17 +464,25 @@ static const char *ippackethdrinfo(const u8 *packet, u32 len) { if (tcp->th_flags & TH_CWR) *p++ = 'C'; /* rfc 2481/3168 */ *p++ = '\0'; - snprintf(protoinfo, sizeof(protoinfo), "TCP %s:?? > %s:?? %s %s %s", - srchost, dsthost, tflags, ipinfo, tcpinfo); - } else if (len < (u32) ip->ip_hl * 4 + 16) { // we can get ports an seq + if((u32) tcp->th_off * 4 > sizeof(struct tcphdr)) { + // tcp options + if(len < (u32) ip->ip_hl * 4 + (u32) tcp->th_off * 4 - frag_off) { + snprintf(tcpoptinfo, sizeof(tcpoptinfo), "option incomplete"); + + } else { + tcppacketoptinfo((u8*) tcp + sizeof(struct tcphdr), + tcp->th_off*4 - sizeof(struct tcphdr), + tcpoptinfo, sizeof(tcpoptinfo)); + } + } + + snprintf(protoinfo, sizeof(protoinfo), "TCP %s:?? > %s:?? %s %s %s %s", + srchost, dsthost, tflags, ipinfo, tcpinfo, tcpoptinfo); + } else if (len < (u32) ip->ip_hl * 4 + 16) { // we can get ports and seq snprintf(tcpinfo, sizeof(tcpinfo), "seq=%lu (incomplete)", (unsigned long) ntohl(tcp->th_seq)); snprintf(protoinfo, sizeof(protoinfo), "TCP %s:%d > %s:%d ?? %s %s", srchost, ntohs(tcp->th_sport), dsthost, ntohs(tcp->th_dport), ipinfo, tcpinfo); - } else if (len < (u32) ip->ip_hl * 4 + 16 && !frag_off) { // we can't get TCP flags - snprintf(protoinfo, sizeof(protoinfo), "TCP %s:%d > %s:%d ?? %s %s (incomplete)", - srchost, ntohs(tcp->th_sport), dsthost, ntohs(tcp->th_dport), - ipinfo, tcpinfo); - } else { // at least first 16 bytes of TCP header are there (everything we need) + } else { // at least first 16 bytes of TCP header are there snprintf(tcpinfo, sizeof(tcpinfo), "seq=%lu win=%hi", (unsigned long) ntohl(tcp->th_seq), @@ -358,9 +504,21 @@ static const char *ippackethdrinfo(const u8 *packet, u32 len) { if (tcp->th_flags & TH_CWR) *p++ = 'C'; /* rfc 2481/3168 */ *p++ = '\0'; - snprintf(protoinfo, sizeof(protoinfo), "TCP %s:%d > %s:%d %s %s %s", + if((u32) tcp->th_off * 4 > sizeof(struct tcphdr)) { + // tcp options + if(len < (u32) ip->ip_hl * 4 + (u32) tcp->th_off * 4) { + snprintf(tcpoptinfo, sizeof(tcpoptinfo), "option incomplete"); + + } else { + tcppacketoptinfo((u8*) tcp + sizeof(struct tcphdr), + tcp->th_off*4 - sizeof(struct tcphdr), + tcpoptinfo, sizeof(tcpoptinfo)); + } + } + + snprintf(protoinfo, sizeof(protoinfo), "TCP %s:%d > %s:%d %s %s %s %s", srchost, ntohs(tcp->th_sport), dsthost, ntohs(tcp->th_dport), - tflags, ipinfo, tcpinfo); + tflags, ipinfo, tcpinfo, tcpoptinfo); } } else if (ip->ip_p == IPPROTO_UDP && frag_off) { snprintf(protoinfo, sizeof(protoinfo), "UDP %s:?? > %s:?? fragment %s (incomplete)", srchost, dsthost, ipinfo);