diff --git a/CHANGELOG b/CHANGELOG index 9262b902a..030ee38f7 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,9 @@ #Nmap Changelog ($Id$); -*-text-*- +o Fixed an issue where Nmap OS scan trusts a packet's ip_len to size a CRC32 + computation over TCP RST payload data, which may result in reading arbitrary + heap data. Reported by Michael Bommarito. + o [Nsock][GH#2595] Fixed Nsock's SOCKS4 proxy code on Windows, which was sending an extra 3 null bytes at the beginning of connections. diff --git a/osscan2.cc b/osscan2.cc index ffda38a9d..d967b788e 100644 --- a/osscan2.cc +++ b/osscan2.cc @@ -1924,7 +1924,7 @@ bool HostOsScan::processResp(HostOsScanStats *hss, const u8 *pkt, unsigned int l if (testno >= 0 && testno < NUM_SEQ_SAMPLES) { /* TSeq */ - isPktUseful = processTSeqResp(hss, &ip, pkt, &tcp, l4pkt, testno); + isPktUseful = processTSeqResp(hss, &ip, pkt, &tcp, l4pkt, len, testno); if (isPktUseful) { hss->ipid.tcp_ipids[testno] = ntohs(ip.ip_id); @@ -1937,16 +1937,16 @@ bool HostOsScan::processResp(HostOsScanStats *hss, const u8 *pkt, unsigned int l */ if (testno == 0) { /* the first reply is used to do T1 */ - processT1_7Resp(hss, &ip, &tcp, l4pkt, 0); + processT1_7Resp(hss, &ip, &tcp, l4pkt, len, 0); } /* the 1st NUM_SEQ_SAMPLES replies are used to do TOps and TWin */ - processTOpsResp(hss, l4pkt, testno); + processTOpsResp(hss, l4pkt, len, testno); processTWinResp(hss, &tcp, testno); } else if (testno >= NUM_SEQ_SAMPLES && testno < NUM_SEQ_SAMPLES + 6) { /* TOps/Twin */ - isPktUseful = processTOpsResp(hss, l4pkt, testno - NUM_SEQ_SAMPLES); + isPktUseful = processTOpsResp(hss, l4pkt, len, testno - NUM_SEQ_SAMPLES); isPktUseful |= processTWinResp(hss, &tcp, testno - NUM_SEQ_SAMPLES); if (isPktUseful) { probeI = hss->getActiveProbe(OFP_TOPS, testno - NUM_SEQ_SAMPLES); @@ -1955,14 +1955,14 @@ bool HostOsScan::processResp(HostOsScanStats *hss, const u8 *pkt, unsigned int l } else if (testno == NUM_SEQ_SAMPLES + 6) { /* TEcn */ - isPktUseful = processTEcnResp(hss, &ip, &tcp, l4pkt); + isPktUseful = processTEcnResp(hss, &ip, &tcp, l4pkt, len); if (isPktUseful) { probeI = hss->getActiveProbe(OFP_TECN, 0); } } else if (testno >= NUM_SEQ_SAMPLES + 7 && testno < NUM_SEQ_SAMPLES + 14) { - isPktUseful = processT1_7Resp(hss, &ip, &tcp, l4pkt, testno - NUM_SEQ_SAMPLES - 7); + isPktUseful = processT1_7Resp(hss, &ip, &tcp, l4pkt, len, testno - NUM_SEQ_SAMPLES - 7); if (isPktUseful) { probeI = hss->getActiveProbe(OFP_T1_7, testno - NUM_SEQ_SAMPLES - 7); @@ -2613,7 +2613,7 @@ void HostOsScan::makeTWinFP(HostOsScanStats *hss) { } -bool HostOsScan::processTSeqResp(HostOsScanStats *hss, const struct ip *ip, const u8 *pkt, const struct tcp_hdr *tcp, const u8 *tcppkt, int replyNo) { +bool HostOsScan::processTSeqResp(HostOsScanStats *hss, const struct ip *ip, const u8 *pkt, const struct tcp_hdr *tcp, const u8 *tcppkt, int tcplen, int replyNo) { assert(replyNo >= 0 && replyNo < NUM_SEQ_SAMPLES); int seq_response_num; /* response # for sequencing */ @@ -2659,7 +2659,7 @@ bool HostOsScan::processTSeqResp(HostOsScanStats *hss, const struct ip *ip, cons hss->si.seqs[seq_response_num] = ntohl(tcp->th_seq); /* TCP ISN */ hss->si.ipids[seq_response_num] = ntohs(ip->ip_id); - if ((gettcpopt_ts(tcppkt, ×tamp, NULL) == 0)) + if ((gettcpopt_ts(tcppkt, tcplen, ×tamp, NULL) == 0)) hss->si.ts_seqclass = TS_SEQ_UNSUPPORTED; else { if (timestamp == 0) { @@ -2677,14 +2677,14 @@ bool HostOsScan::processTSeqResp(HostOsScanStats *hss, const struct ip *ip, cons } -bool HostOsScan::processTOpsResp(HostOsScanStats *hss, const u8 *tcp, int replyNo) { +bool HostOsScan::processTOpsResp(HostOsScanStats *hss, const u8 *tcp, int tcplen, int replyNo) { assert(replyNo >= 0 && replyNo < 6); char ops_buf[256]; if (hss->FP_TOps || hss->TOps_AVs[replyNo]) return false; - int opsParseResult = get_tcpopt_string(tcp, this->tcpMss, ops_buf, sizeof(ops_buf)); + int opsParseResult = get_tcpopt_string(tcp, tcplen, this->tcpMss, ops_buf, sizeof(ops_buf)); if (opsParseResult <= 0) { if (opsParseResult < 0 && o.debugging) @@ -2713,7 +2713,7 @@ bool HostOsScan::processTWinResp(HostOsScanStats *hss, const struct tcp_hdr *tcp } -bool HostOsScan::processTEcnResp(HostOsScanStats *hss, const struct ip *ip, const struct tcp_hdr *tcp, const u8 *tcppkt) { +bool HostOsScan::processTEcnResp(HostOsScanStats *hss, const struct ip *ip, const struct tcp_hdr *tcp, const u8 *tcppkt, int tcplen) { char ops_buf[256]; char quirks_buf[10]; char *p; @@ -2740,7 +2740,7 @@ bool HostOsScan::processTEcnResp(HostOsScanStats *hss, const struct ip *ip, cons test.setAVal("W", hss->target->FPR->cp_hex(ntohs(tcp->th_win))); /* Now for the TCP options ... */ - int opsParseResult = get_tcpopt_string(tcppkt, this->tcpMss, ops_buf, sizeof(ops_buf)); + int opsParseResult = get_tcpopt_string(tcppkt, tcplen, this->tcpMss, ops_buf, sizeof(ops_buf)); if (opsParseResult <= 0) { if (opsParseResult < 0 && o.debugging) @@ -2783,11 +2783,10 @@ bool HostOsScan::processTEcnResp(HostOsScanStats *hss, const struct ip *ip, cons } -bool HostOsScan::processT1_7Resp(HostOsScanStats *hss, const struct ip *ip, const struct tcp_hdr *tcp, const u8 *tcppkt, int replyNo) { +bool HostOsScan::processT1_7Resp(HostOsScanStats *hss, const struct ip *ip, const struct tcp_hdr *tcp, const u8 *tcppkt, int tcplen, int replyNo) { assert(replyNo >= 0 && replyNo < 7); int i; - int length; char flags_buf[10]; char quirks_buf[10]; char *p; @@ -2881,7 +2880,7 @@ bool HostOsScan::processT1_7Resp(HostOsScanStats *hss, const struct ip *ip, cons char ops_buf[256]; /* Now for the TCP options ... */ - int opsParseResult = get_tcpopt_string(tcppkt, this->tcpMss, ops_buf, sizeof(ops_buf)); + int opsParseResult = get_tcpopt_string(tcppkt, tcplen, this->tcpMss, ops_buf, sizeof(ops_buf)); if (opsParseResult <= 0) { if (opsParseResult < 0 && o.debugging) error("Option parse error for T%d response from %s.", replyNo, hss->target->targetipstr()); @@ -2893,7 +2892,7 @@ bool HostOsScan::processT1_7Resp(HostOsScanStats *hss, const struct ip *ip, cons } /* Rst Data CRC32 */ - length = (int) ntohs(ip->ip_len) - 4 * ip->ip_hl -4 * tcp->th_off; + int length = tcplen - 4 * tcp->th_off; if ((tcp->th_flags & TH_RST) && length>0) { test.setAVal("RD", hss->target->FPR->cp_hex(nbase_crc32(tcppkt + 4 * tcp->th_off, length))); } else { @@ -3138,7 +3137,7 @@ bool HostOsScan::processTIcmpResp(HostOsScanStats *hss, const struct ip *ip, con } -int HostOsScan::get_tcpopt_string(const u8 *tcp, int mss, char *result, int maxlen) const { +int HostOsScan::get_tcpopt_string(const u8 *tcp, int tcplen, int mss, char *result, int maxlen) const { char *p; const u8 *q; u16 tmpshort; @@ -3149,7 +3148,7 @@ int HostOsScan::get_tcpopt_string(const u8 *tcp, int mss, char *result, int maxl p = result; struct tcp_hdr hdr; memcpy(&hdr, tcp, sizeof(hdr)); - length = (hdr.th_off * 4) - sizeof(struct tcp_hdr); + length = MIN(hdr.th_off * 4, tcplen) - sizeof(struct tcp_hdr); q = tcp + sizeof(struct tcp_hdr); /* diff --git a/osscan2.h b/osscan2.h index 105930ad3..341a07625 100644 --- a/osscan2.h +++ b/osscan2.h @@ -403,11 +403,11 @@ private: void sendTUdpProbe(HostOsScanStats *hss, int probeNo); void sendTIcmpProbe(HostOsScanStats *hss, int probeNo); /* Response process functions. */ - bool processTSeqResp(HostOsScanStats *hss, const struct ip *ip, const u8 *pkt, const struct tcp_hdr *tcp, const u8 *tcppkt, int replyNo); - bool processTOpsResp(HostOsScanStats *hss, const u8 *tcp, int replyNo); + bool processTSeqResp(HostOsScanStats *hss, const struct ip *ip, const u8 *pkt, const struct tcp_hdr *tcp, const u8 *tcppkt, int tcplen, int replyNo); + bool processTOpsResp(HostOsScanStats *hss, const u8 *tcp, int tcplen, int replyNo); bool processTWinResp(HostOsScanStats *hss, const struct tcp_hdr *tcp, int replyNo); - bool processTEcnResp(HostOsScanStats *hss, const struct ip *ip, const struct tcp_hdr *tcp, const u8 *tcppkt); - bool processT1_7Resp(HostOsScanStats *hss, const struct ip *ip, const struct tcp_hdr *tcp, const u8 *tcppkt, int replyNo); + bool processTEcnResp(HostOsScanStats *hss, const struct ip *ip, const struct tcp_hdr *tcp, const u8 *tcppkt, int tcplen); + bool processT1_7Resp(HostOsScanStats *hss, const struct ip *ip, const struct tcp_hdr *tcp, const u8 *tcppkt, int tcplen, int replyNo); bool processTUdpResp(HostOsScanStats *hss, const u8 *ip); bool processTUdpResp(HostOsScanStats *hss, const struct ip *ip, const struct icmp *icmp, const u8 *icmppkt, unsigned int icmplen); bool processTIcmpResp(HostOsScanStats *hss, const struct ip *ip, const u8 *pkt, int replyNo); @@ -429,7 +429,7 @@ private: void makeTOpsFP(HostOsScanStats *hss); void makeTWinFP(HostOsScanStats *hss); - int get_tcpopt_string(const u8 *tcp, int mss, char *result, int maxlen) const; + int get_tcpopt_string(const u8 *tcp, int tcplen, int mss, char *result, int maxlen) const; int rawsd; /* Raw socket descriptor */ netutil_eth_t *ethsd; /* Ethernet handle */ diff --git a/tcpip.cc b/tcpip.cc index 48de9c722..67020c85a 100644 --- a/tcpip.cc +++ b/tcpip.cc @@ -1719,18 +1719,19 @@ int recvtime(int sd, char *buf, int len, int seconds, int *timedout) { parameters (if non-null) are filled with 0. Remember that the correct way to check for errors is to look at the return value since a zero ts or echots could possibly be valid. */ -int gettcpopt_ts(const u8 *tcppkt, u32 *timestamp, u32 *echots) { +int gettcpopt_ts(const u8 *tcppkt, int tcplen, u32 *timestamp, u32 *echots) { const u8 *p; int len = 0; int op; int oplen; struct tcp_hdr tcp; + assert(tcplen >= sizeof(tcp)); memcpy(&tcp, tcppkt, sizeof(tcp)); /* first we find where the tcp options start ... */ p = tcppkt + 20; - len = 4 * tcp.th_off - 20; + len = MIN(4 * tcp.th_off, tcplen) - 20; while (len > 0 && *p != 0 /* TCPOPT_EOL */ ) { op = *p++; if (op == 0 /* TCPOPT_EOL */ ) diff --git a/tcpip.h b/tcpip.h index 79eb43683..8b9e68c4f 100644 --- a/tcpip.h +++ b/tcpip.h @@ -368,7 +368,7 @@ const u8 *readip_pcap(pcap_t *pd, unsigned int *len, long to_usec, parameters (if non-null) are filled with 0. Remember that the correct way to check for errors is to look at the return value since a zero ts or echots could possibly be valid. */ -int gettcpopt_ts(const u8 *tcppkt, u32 *timestamp, u32 *echots); +int gettcpopt_ts(const u8 *tcppkt, int tcplen, u32 *timestamp, u32 *echots); /* Maximize the receive buffer of a socket descriptor (up to 500K) */ void max_rcvbuf(int sd);