mirror of
https://github.com/nmap/nmap.git
synced 2025-12-24 00:19:01 +00:00
Merge the difference between this branch at r15954 and
/nmap-exp/david/nmap-token. This brings in the following changes:
Use a strict tryno equality test in check_tryno_pingseq. This appears to
have no effect in the current code, because the way we traverse the
probe list backwards ensures that probes with a higher tryno are tried
first. However this protects against matching the wrong tryno if that
behavior is ever changed.
Factor out the code that checks for a match of a TCP packet.
Add some extra checks when matching up TCP probes, to avoid confusing
responses to SYN and ACK probes when they are sent to the same host on
the same port, with the same tryno and pingseq, in a ping scan that
includes both -PS and -PA. I think this is the only case where there can
be confusion. The new rules are: A SYN/ACK can only be matched to a SYN
probe. A RST/ACK can only be matched to a SYN or FIN. A bare RST cannot
be matched to a SYN or FIN.
Make an important change in the way the tryno and pingseq are encoded
for TCP probes with the ACK flag set when --source-port is in effect.
According to RFC 793, responses to ACK packets on an unestablished
connection (CLOSED and LISTEN states in particular) should send a RST
response with a SEQ value the same as the received ACK value. So for
example, if it's in the CLOSED state and wants to send a RST, it sends
<SEQ=0><ACK=SEG.SEQ+SEG.LEN><CTL=RST,ACK>
if the received packet does not have the ACK flag set, but
<SEQ=SEG.ACK><CTL=RST>
This caused a problem because in the second case, the response does not
reflect our sent SEQ value, which is where the tryno and pingseq are
encoded. The response's acknowledgement number, while not valid because
the ACK flag is not set, is typically 0. Decoding this with seq32_decode
would result in a decoding error, leading to a
Bad Sequence number from host
message. In this case the probe was allowed to match any TCP probe with
the same ports and address, even though the pingseq and tryno might be
off or the probe is a different kind of probe entirely (like a SYN
probe).
Here's a summary of what has changed, with <tryno,pingseq> standing for
an encoded tryno and pingseq.
Before:
Non-ACK probes sent with SEQ=<tryno,pingseq>, ACK=0.
ACK probes sent with SEQ=<tryno,pingseq>, ACK=random
Probes matched against ACK and ACK - 1.
Now:
Non-ACK probes sent with SEQ=<tryno,pingseq>, ACK=0.
ACK probes sent with SEQ=0, ACK=<tryno,pingseq>.
Probes matched against ACK, ACK - 1, and SEQ.
Matching against the SEQ field may also help in some other weird cases.
In the LISTEN state, the receiving TCP is supposed to check that "the
security/compartment on the incoming segment does not exactly match the
security/compartment in the TCB," and if it doesn't, return
<SEQ=SEG.ACK><CTL=RST>
just like in the ACK case. I don't know how common that sort of thing is.
This commit is contained in:
203
scan_engine.cc
203
scan_engine.cc
@@ -236,7 +236,7 @@ public:
|
||||
void setARP(u8 *arppkt, u32 arplen);
|
||||
// The 4 accessors below all return in HOST BYTE ORDER
|
||||
// source port used if TCP, UDP or SCTP
|
||||
u16 sport() {
|
||||
u16 sport() const {
|
||||
switch (mypspec.proto) {
|
||||
case IPPROTO_TCP:
|
||||
return probes.IP.pd.tcp.sport;
|
||||
@@ -250,7 +250,7 @@ public:
|
||||
/* not reached */
|
||||
}
|
||||
// destination port used if TCP, UDP or SCTP
|
||||
u16 dport() {
|
||||
u16 dport() const {
|
||||
switch (mypspec.proto) {
|
||||
case IPPROTO_TCP:
|
||||
return mypspec.pd.tcp.dport;
|
||||
@@ -265,10 +265,10 @@ public:
|
||||
}
|
||||
/* not reached */
|
||||
}
|
||||
u16 ipid() { return probes.IP.ipid; }
|
||||
u32 tcpseq(); // TCP sequence number if protocol is TCP
|
||||
u16 ipid() const { return probes.IP.ipid; }
|
||||
u32 tcpseq() const; // TCP sequence number if protocol is TCP
|
||||
/* Number, such as IPPROTO_TCP, IPPROTO_UDP, etc. */
|
||||
u8 protocol() { return mypspec.proto; }
|
||||
u8 protocol() const { return mypspec.proto; }
|
||||
ConnectProbe *CP() { return probes.CP; } // if type == UP_CONNECT
|
||||
// Arpprobe removed because not used.
|
||||
// ArpProbe *AP() { return probes.AP; } // if UP_ARP
|
||||
@@ -276,12 +276,12 @@ public:
|
||||
// reading the appropriate fields of the probespec.
|
||||
|
||||
/* Get general details about the probe */
|
||||
const probespec *pspec() { return &mypspec; }
|
||||
const probespec *pspec() const { return &mypspec; }
|
||||
|
||||
/* Returns true if the given tryno and pingseq match those within this
|
||||
probe. */
|
||||
bool check_tryno_pingseq(unsigned int tryno, unsigned int pingseq) {
|
||||
return (pingseq == 0 && tryno >= this->tryno) || (pingseq > 0 && pingseq == this->pingseq);
|
||||
bool check_tryno_pingseq(unsigned int tryno, unsigned int pingseq) const {
|
||||
return (pingseq == 0 && tryno == this->tryno) || (pingseq > 0 && pingseq == this->pingseq);
|
||||
}
|
||||
|
||||
u8 tryno; /* Try (retransmission) number of this probe */
|
||||
@@ -832,7 +832,7 @@ void UltraProbe::setIP(u8 *ippacket, u32 iplen, const probespec *pspec) {
|
||||
return;
|
||||
}
|
||||
|
||||
u32 UltraProbe::tcpseq() {
|
||||
u32 UltraProbe::tcpseq() const {
|
||||
if (mypspec.proto == IPPROTO_TCP)
|
||||
return probes.IP.pd.tcp.seq;
|
||||
else
|
||||
@@ -2319,8 +2319,8 @@ static u32 seq32_encode(UltraScanInfo *USI, unsigned int trynum,
|
||||
|
||||
/* Undoes seq32_encode. This extracts a try number and a port number from a
|
||||
32-bit value. Returns true if the checksum is correct, false otherwise. */
|
||||
static bool seq32_decode(UltraScanInfo *USI, u32 seq, unsigned int *trynum,
|
||||
unsigned int *pingseq) {
|
||||
static bool seq32_decode(const UltraScanInfo *USI, u32 seq,
|
||||
unsigned int *trynum, unsigned int *pingseq) {
|
||||
if (trynum)
|
||||
*trynum = 0;
|
||||
if (pingseq)
|
||||
@@ -2364,7 +2364,7 @@ static u16 sport_encode(UltraScanInfo *USI, u16 portno, unsigned int trynum,
|
||||
a port number given a "magic" original port number (the one given to
|
||||
sport_encode). Returns true if the decoded values seem reasonable, false
|
||||
otherwise. */
|
||||
static bool sport_decode(UltraScanInfo *USI, u16 magic_portno, u16 portno,
|
||||
static bool sport_decode(const UltraScanInfo *USI, u16 magic_portno, u16 portno,
|
||||
unsigned int *trynum, unsigned int *pingseq) {
|
||||
int t;
|
||||
|
||||
@@ -2388,23 +2388,94 @@ static bool sport_decode(UltraScanInfo *USI, u16 magic_portno, u16 portno,
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Extracts a probe response's try number and ping sequence number from a TCP
|
||||
header. The values come from either the destination port number or the ACK
|
||||
field, depending on whether o.magic_port_set is true. */
|
||||
static bool tcp_trynum_pingseq_decode(UltraScanInfo *USI,
|
||||
const struct tcp_hdr *tcp,
|
||||
unsigned int *trynum,
|
||||
unsigned int *pingseq) {
|
||||
static bool tcp_probe_match(const UltraScanInfo *USI, const UltraProbe *probe,
|
||||
const HostScanStats *hss, const struct ip *ip) {
|
||||
const struct tcp_hdr *tcp;
|
||||
const struct probespec_tcpdata *probedata;
|
||||
unsigned int tryno, pingseq;
|
||||
bool goodseq;
|
||||
|
||||
if (ip->ip_p != IPPROTO_TCP)
|
||||
return false;
|
||||
|
||||
tcp = (struct tcp_hdr *) ((u8 *) ip + ip->ip_hl * 4);
|
||||
|
||||
if (o.af() != AF_INET || probe->protocol() != IPPROTO_TCP)
|
||||
return false;
|
||||
|
||||
/* Ensure the connection info matches. */
|
||||
if (probe->dport() != ntohs(tcp->th_sport)
|
||||
|| probe->sport() != ntohs(tcp->th_dport)
|
||||
|| hss->target->v4sourceip()->s_addr != ip->ip_dst.s_addr)
|
||||
return false;
|
||||
|
||||
tryno = 0;
|
||||
pingseq = 0;
|
||||
if (o.magic_port_set) {
|
||||
/* We can't get the values from the port number. Try to get them from the
|
||||
ACK field. First try ACK - 1 because some probes include SYN or FIN
|
||||
packet and thus call for increment. */
|
||||
return seq32_decode(USI, ntohl(tcp->th_ack) - 1, trynum, pingseq)
|
||||
|| seq32_decode(USI, ntohl(tcp->th_ack), trynum, pingseq);
|
||||
/* We are looking to recover the tryno and pingseq of the probe, which are
|
||||
encoded in the ACK field for probes with the ACK flag set and in the SEQ
|
||||
field for all other probes. According to RFC 793, section 3.9, under
|
||||
"SEGMENT ARRIVES", it's supposed to work like this: If our probe had ACK
|
||||
set, our ACK number is reflected in the response's SEQ field. If our
|
||||
probe had SYN or FIN set (and not ACK), then our SEQ is one less than the
|
||||
returned ACK value because SYN and FIN consume a sequence number (section
|
||||
3.3). Otherwise, our SEQ is the returned ACK.
|
||||
|
||||
However, nmap-os-db shows that these assumptions can't be relied on, so
|
||||
we try all three possibilities for each probe. */
|
||||
goodseq = seq32_decode(USI, ntohl(tcp->th_ack) - 1, &tryno, &pingseq)
|
||||
|| seq32_decode(USI, ntohl(tcp->th_ack), &tryno, &pingseq)
|
||||
|| seq32_decode(USI, ntohl(tcp->th_seq), &tryno, &pingseq);
|
||||
} else {
|
||||
/* Get the values from the port number. */
|
||||
return sport_decode(USI, o.magic_port, ntohs(tcp->th_dport), trynum, pingseq);
|
||||
/* Get the values from the destination port (our source port). */
|
||||
sport_decode(USI, o.magic_port, ntohs(tcp->th_dport), &tryno, &pingseq);
|
||||
goodseq = true;
|
||||
}
|
||||
|
||||
if (!goodseq) {
|
||||
/* TODO: I need to do some testing and find out how often this happens
|
||||
and whether other techniques such as the response seq should be
|
||||
used in those cases where it happens. Then I should make this just
|
||||
a debugging > X statement. */
|
||||
if (o.debugging)
|
||||
log_write(LOG_PLAIN, "Bad Sequence number from host %s.\n", inet_ntoa(ip->ip_src));
|
||||
/* I'll just assume it is a response to this (most recent) probe. */
|
||||
tryno = probe->tryno;
|
||||
pingseq = probe->pingseq;
|
||||
}
|
||||
|
||||
/* Make sure that trynum and pingseq match the values in the probe. */
|
||||
if (!probe->check_tryno_pingseq(tryno, pingseq))
|
||||
return false;
|
||||
|
||||
/* Make sure we are matching up the right kind of probe, otherwise just the
|
||||
ports, address, tryno, and pingseq can be ambiguous, between a SYN and an
|
||||
ACK probe during a -PS80 -PA80 scan for example. A SYN/ACK can only be
|
||||
matched to a SYN probe. A RST/ACK can only be matched to a SYN or FIN. A
|
||||
bare RST cannot be matched to a SYN or FIN. */
|
||||
probedata = &probe->pspec()->pd.tcp;
|
||||
if ((tcp->th_flags & (TH_SYN | TH_ACK)) == (TH_SYN | TH_ACK)
|
||||
&& !(probedata->flags & TH_SYN)) {
|
||||
return false;
|
||||
}
|
||||
if ((tcp->th_flags & (TH_RST | TH_ACK)) == (TH_RST | TH_ACK)
|
||||
&& !(probedata->flags & (TH_SYN | TH_FIN))) {
|
||||
return false;
|
||||
}
|
||||
if ((tcp->th_flags & (TH_RST | TH_ACK)) == TH_RST
|
||||
&& (probedata->flags & (TH_SYN | TH_FIN))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Sometimes we get false results when scanning localhost with -p- because we
|
||||
scan localhost with src port = dst port and see our outgoing packet and
|
||||
think it is a response. */
|
||||
if (probe->dport() == probe->sport()
|
||||
&& ip->ip_src.s_addr == ip->ip_dst.s_addr
|
||||
&& probe->ipid() == ntohs(ip->ip_id))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* This function provides the proper cwnd and ssthresh to use. It may
|
||||
@@ -3059,9 +3130,14 @@ static UltraProbe *sendIPScanProbe(UltraScanInfo *USI, HostScanStats *hss,
|
||||
if (pspec->type == PS_TCP) {
|
||||
assert(USI->scantype != CONNECT_SCAN);
|
||||
|
||||
seq = seq32_encode(USI, tryno, pingseq);
|
||||
/* Normally we encode the tryno and pingseq in the SEQ field, because that
|
||||
comes back (possibly incremented) in the ACK field of responses. But if
|
||||
our probe has the ACK flag set, the response reflects our own ACK number
|
||||
instead. */
|
||||
if (pspec->pd.tcp.flags & TH_ACK)
|
||||
ack = get_random_u32();
|
||||
ack = seq32_encode(USI, tryno, pingseq);
|
||||
else
|
||||
seq = seq32_encode(USI, tryno, pingseq);
|
||||
|
||||
if (pspec->pd.tcp.flags & TH_SYN) {
|
||||
tcpops = (u8 *) "\x02\x04\x05\xb4";
|
||||
@@ -3938,8 +4014,6 @@ static bool get_pcap_result(UltraScanInfo *USI, struct timeval *stime) {
|
||||
struct sockaddr_in sin;
|
||||
list<UltraProbe *>::iterator probeI;
|
||||
UltraProbe *probe = NULL;
|
||||
unsigned int trynum = 0;
|
||||
unsigned int pingseq = 0;
|
||||
int newstate = PORT_UNKNOWN;
|
||||
unsigned int probenum;
|
||||
unsigned int listsz;
|
||||
@@ -4027,44 +4101,12 @@ static bool get_pcap_result(UltraScanInfo *USI, struct timeval *stime) {
|
||||
|
||||
/* Find the probe that provoked this response. */
|
||||
for (probenum = 0; probenum < listsz && !goodone; probenum++) {
|
||||
bool goodseq = false;
|
||||
probeI--;
|
||||
probe = *probeI;
|
||||
|
||||
if (o.af() != AF_INET || probe->protocol() != IPPROTO_TCP)
|
||||
continue;
|
||||
|
||||
/* Ensure the connection info matches. */
|
||||
if (probe->dport() != ntohs(tcp->th_sport)
|
||||
|| probe->sport() != ntohs(tcp->th_dport)
|
||||
|| hss->target->v4sourceip()->s_addr != ip->ip_dst.s_addr)
|
||||
continue;
|
||||
|
||||
goodseq = tcp_trynum_pingseq_decode(USI, tcp, &trynum, &pingseq);
|
||||
if (!goodseq) {
|
||||
/* TODO: I need to do some testing and find out how often this happens
|
||||
and whether other techniques such as the response seq should be
|
||||
used in those cases where it happens. Then I should make this just
|
||||
a debugging > X statement. */
|
||||
if (o.debugging)
|
||||
log_write(LOG_PLAIN, "Bad Sequence number from host %s.\n", inet_ntoa(ip->ip_src));
|
||||
/* I'll just assume it is a response to this (most recent) probe. */
|
||||
trynum = probe->tryno;
|
||||
pingseq = probe->pingseq;
|
||||
}
|
||||
|
||||
/* Make sure that trynum and pingseq match the values in the probe. */
|
||||
if (!probe->check_tryno_pingseq(trynum, pingseq))
|
||||
if (!tcp_probe_match(USI, probe, hss, ip))
|
||||
continue;
|
||||
|
||||
/* Sometimes we get false results when scanning localhost with
|
||||
-p- because we scan localhost with src port = dst port and
|
||||
see our outgoing packet and think it is a response. */
|
||||
if (probe->dport() == probe->sport() &&
|
||||
ip->ip_src.s_addr == ip->ip_dst.s_addr &&
|
||||
probe->ipid() == ntohs(ip->ip_id))
|
||||
continue; /* We saw the packet we ourselves sent */
|
||||
|
||||
if (!probe->isPing()) {
|
||||
/* Now that response has been matched to a probe, I interpret it */
|
||||
if (USI->scantype == SYN_SCAN && (tcp->th_flags & (TH_SYN|TH_ACK)) == (TH_SYN|TH_ACK)) {
|
||||
@@ -4400,7 +4442,6 @@ static int get_ping_pcap_result(UltraScanInfo *USI, struct timeval *stime) {
|
||||
list<UltraProbe *>::iterator probeI;
|
||||
UltraProbe *probe = NULL;
|
||||
unsigned int trynum = 0;
|
||||
unsigned int pingseq = 0;
|
||||
unsigned int requiredbytes;
|
||||
int newstate = HOST_UNKNOWN;
|
||||
unsigned int probenum;
|
||||
@@ -4770,44 +4811,12 @@ static int get_ping_pcap_result(UltraScanInfo *USI, struct timeval *stime) {
|
||||
|
||||
/* Find the probe that provoked this response. */
|
||||
for (probenum = 0; probenum < listsz && !goodone; probenum++) {
|
||||
bool goodseq = false;
|
||||
probeI--;
|
||||
probe = *probeI;
|
||||
|
||||
if (o.af() != AF_INET || probe->protocol() != IPPROTO_TCP)
|
||||
if (!tcp_probe_match(USI, probe, hss, ip))
|
||||
continue;
|
||||
|
||||
/* Ensure the connection info matches. */
|
||||
if (probe->dport() != ntohs(tcp->th_sport)
|
||||
|| probe->sport() != ntohs(tcp->th_dport)
|
||||
|| hss->target->v4sourceip()->s_addr != ip->ip_dst.s_addr)
|
||||
continue;
|
||||
|
||||
goodseq = tcp_trynum_pingseq_decode(USI, tcp, &trynum, &pingseq);
|
||||
if (!goodseq) {
|
||||
/* TODO: I need to do some testing and find out how often this happens
|
||||
and whether other techniques such as the response seq should be
|
||||
used in those cases where it happens. Then I should make this just
|
||||
a debugging > X statement. */
|
||||
if (o.debugging)
|
||||
log_write(LOG_PLAIN, "Bad Sequence number from host %s.\n", inet_ntoa(ip->ip_src));
|
||||
/* I'll just assume it is a response to this (most recent) probe. */
|
||||
trynum = probe->tryno;
|
||||
pingseq = probe->pingseq;
|
||||
}
|
||||
|
||||
/* Make sure that trynum and pingseq match the values in the probe. */
|
||||
if (!probe->check_tryno_pingseq(trynum, pingseq))
|
||||
continue;
|
||||
|
||||
/* Sometimes we get false results when scanning localhost with
|
||||
-p- because we scan localhost with src port = dst port and
|
||||
see our outgoing packet and think it is a response. */
|
||||
if (probe->dport() == probe->sport() &&
|
||||
ip->ip_src.s_addr == ip->ip_dst.s_addr &&
|
||||
probe->ipid() == ntohs(ip->ip_id))
|
||||
continue; /* We saw the packet we ourselves sent */
|
||||
|
||||
goodone = true;
|
||||
newstate = HOST_UP;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user