mirror of
https://github.com/nmap/nmap.git
synced 2025-12-25 00:49:01 +00:00
Adding packet validity checking to readip_pcap() so the caller can assume the
packet is OK from the get-go rather than running basic checks of it's own. In a nutshell this patch checks to make sure: 1) there is enough room for an IP header in the amount of bytes read 2) the IP version number is correct 3) the IP length fields are at least as big as the standard header 4) the IP packet received isn't a fragment, or is the initial fragment 5) that next level headers seem reasonable For TCP, this checks that there is enough room for the header in the number of bytes read, and that any option lengths are correct. The options checked are MSS, WScale, SackOK, Sack, and Timestamp. This also fixes a bug I discovered while testing. Since the Ethernet CRC (and other datalink-layer data) could be read and counted, it was being returned that there was more IP packet than there really was. This didn't cause an overrun of the buffer or anything, just that garbage data could have easily been read instead of real packet data. Now, if validity is checked for and the number of total bytes read is larger than the IP's length, the length is set to the IP header's total length field. This seems to work great after doing what testing I could. It's been out on nmap-dev for a couple of weeks without any bad reports (none at all for that matter). I reviewed this patch again before committing and it looks good as well.
This commit is contained in:
@@ -3581,7 +3581,7 @@ static bool get_pcap_result(UltraScanInfo *USI, struct timeval *stime) {
|
||||
do {
|
||||
to_usec = TIMEVAL_SUBTRACT(*stime, USI->now);
|
||||
if (to_usec < 2000) to_usec = 2000;
|
||||
ip = (struct ip *) readip_pcap(USI->pd, &bytes, to_usec, &rcvdtime, &linkhdr);
|
||||
ip = (struct ip *) readip_pcap(USI->pd, &bytes, to_usec, &rcvdtime, &linkhdr, true);
|
||||
gettimeofday(&USI->now, NULL);
|
||||
if (!ip && TIMEVAL_SUBTRACT(*stime, USI->now) < 0) {
|
||||
timedout = true;
|
||||
@@ -3595,13 +3595,11 @@ static bool get_pcap_result(UltraScanInfo *USI, struct timeval *stime) {
|
||||
timedout = true;
|
||||
}
|
||||
|
||||
/* OK, we got a packet. Let's make sure it is well-formed */
|
||||
/* OK, we got a packet. Most packet validity tests are taken care
|
||||
* of in readip_pcap, so this is simple
|
||||
*/
|
||||
if (bytes < 28)
|
||||
continue;
|
||||
if (ip->ip_v != 4)
|
||||
continue;
|
||||
if (ip->ip_hl < 5)
|
||||
continue;
|
||||
|
||||
if (USI->prot_scan) {
|
||||
memset(&sin, 0, sizeof(sin));
|
||||
@@ -3640,8 +3638,6 @@ static bool get_pcap_result(UltraScanInfo *USI, struct timeval *stime) {
|
||||
}
|
||||
|
||||
if (ip->ip_p == IPPROTO_TCP && !USI->prot_scan) {
|
||||
if ((unsigned) ip->ip_hl * 4 + 20 > bytes)
|
||||
continue;
|
||||
struct tcp_hdr *tcp = (struct tcp_hdr *) ((u8 *) ip + ip->ip_hl * 4);
|
||||
/* Now ensure this host is even in the incomplete list */
|
||||
memset(&sin, 0, sizeof(sin));
|
||||
@@ -3832,8 +3828,6 @@ static bool get_pcap_result(UltraScanInfo *USI, struct timeval *stime) {
|
||||
}
|
||||
}
|
||||
} else if (ip->ip_p == IPPROTO_UDP && !USI->prot_scan) {
|
||||
if ((unsigned) ip->ip_hl * 4 + 8 > bytes)
|
||||
continue;
|
||||
struct udp_hdr *udp = (struct udp_hdr *) ((u8 *) ip + ip->ip_hl * 4);
|
||||
/* Search for this host on the incomplete list */
|
||||
memset(&sin, 0, sizeof(sin));
|
||||
@@ -3971,7 +3965,7 @@ static int get_ping_pcap_result(UltraScanInfo *USI, struct timeval *stime) {
|
||||
do {
|
||||
to_usec = TIMEVAL_SUBTRACT(*stime, USI->now);
|
||||
if (to_usec < 2000) to_usec = 2000;
|
||||
ip = (struct ip *) readip_pcap(USI->pd, &bytes, to_usec, &rcvdtime, &linkhdr);
|
||||
ip = (struct ip *) readip_pcap(USI->pd, &bytes, to_usec, &rcvdtime, &linkhdr, true);
|
||||
gettimeofday(&USI->now, NULL);
|
||||
if (!ip) {
|
||||
if (TIMEVAL_SUBTRACT(*stime, USI->now) < 0) {
|
||||
@@ -3988,17 +3982,11 @@ static int get_ping_pcap_result(UltraScanInfo *USI, struct timeval *stime) {
|
||||
timedout = true;
|
||||
}
|
||||
|
||||
/* OK, we got a packet. Let's make sure it is well-formed */
|
||||
/* OK, we got a packet. Most packet validity tests are taken care
|
||||
* of in readip_pcap, so this is simple
|
||||
*/
|
||||
if (bytes == 0)
|
||||
continue;
|
||||
if (bytes <= 20) {
|
||||
error("%d byte micro packet received in %s", bytes, __func__);
|
||||
continue;
|
||||
}
|
||||
if (ip->ip_v != 4)
|
||||
continue;
|
||||
if (ip->ip_hl < 5)
|
||||
continue;
|
||||
|
||||
if (USI->ptech.rawprotoscan) {
|
||||
memset(&sin, 0, sizeof(sin));
|
||||
@@ -4333,10 +4321,6 @@ static int get_ping_pcap_result(UltraScanInfo *USI, struct timeval *stime) {
|
||||
if (!USI->ptech.rawtcpscan) {
|
||||
continue;
|
||||
}
|
||||
if (bytes < 4 * ip->ip_hl + 16U) {
|
||||
error("TCP packet is only %d bytes, we can't get enough information from it", bytes);
|
||||
continue;
|
||||
}
|
||||
struct tcp_hdr *tcp = (struct tcp_hdr *) (((u8 *) ip) + 4 * ip->ip_hl);
|
||||
/* Check that the packet has useful flags. */
|
||||
if (!(tcp->th_flags & TH_RST)
|
||||
|
||||
Reference in New Issue
Block a user