Bugtraq mailing list archives

A quick patch to help against TCP ISN guessing.


From: avalon () coombs anu edu au (Darren Reed)
Date: Tue, 24 Jan 1995 17:59:30 +1100 (EDT)


It would seem that it isn't as hard to patch the initial sequence
number problem for TCP as it might seem.  I might add, that this
*ISN'T* a complete fix for the problem, but it will make it harder
for people to perform attacks as described in Steve Bellovin's paper.
Whilst swIPe is nice, it suffers from export restrictions (:-) and
requires being setup at both ends to work.  This patch should increase
the difficulty level of someone taking over an arbitary connection
to your host.

First, let me point out that this patch won't solve *all* cases of
TCP connections being guessable, (I don't think), and definately
not the case described by this piece of code (from NetBSD):
....
                        /*
                         * If a new connection request is received
                         * while in TIME_WAIT, drop the old connection
                         * and start over if the sequence numbers
                         * are above the previous ones.
                         */
                        if (tiflags & TH_SYN &&
                            tp->t_state == TCPS_TIME_WAIT &&
                            SEQ_GT(ti->ti_seq, tp->rcv_nxt)) {
                                iss = tp->rcv_nxt + TCP_ISSINCR;
                                tp = tcp_close(tp);
                                goto findpcb;
                        }
....

Here's a sample output from adb /vmunix /dev/kmem from a standard
SunOS kernel:

tcp_iss/
_tcp_iss:       880c200         = bleu          f81873e8
tcp_iss/
_tcp_iss:
_tcp_iss:       8869e00         = bleu          f82fe3e8
tcp_iss/
_tcp_iss:
_tcp_iss:       8879800         = bleu          f833cbe8
tcp_iss/
_tcp_iss:
_tcp_iss:       8889200         = bleu          f837b3e8
tcp_iss/
_tcp_iss:
_tcp_iss:       8898c00         = bleu          f83b9be8
tcp_iss/
_tcp_iss:
_tcp_iss:       88a8600         = bleu          f83f83e8
tcp_iss/
_tcp_iss:
_tcp_iss:       88a8600         = bleu          f83f83e8

(tcp_iss is used to initialize the sequence/ack numbers).

Proposed Solution: assign random numbers to tcp_iss.

Problem: this approach *totally* flies in the face of RFC-793 (and
RFC-1122) with regard to choice of the initial TCP sequence number,
giving consideration to the MSL.

Next Solution: increase rate of exhausting the sequence number window.

The usual rate of increase is 125*1024 each second.  If we assume the
MSL to be 2 minutes (it is interesting to speculate about what the effect
of flooding TCP over a high bandwidth will have here - high as in around
0.5 GB/s and above - as it is possible to exhaust the tcp sequence space
through data) then we can increase the initial sequence number by a good
deal more than the 250,000 per second.  It would seem, that from reading
through the code, that to maintain compatibility with (a bug ?) 4.2BSD,
the initial sequence number is limited to 0 - 2147483647.  If we choose
to exhaust the sequence number space in less than the 4.55 hours
(125*1024*3600*4.55 = 2096640000), then we can increment the tcp_iss
faster.  Incrementing the ISN by 4356761 every second will make it wrap
in around 8 minutes.  This is around 4 times the MSL, and I've seen
connections hang around in various wait states for quite some time...
hmmm...

Something which both papers seem to have missed is that on reboot,
the ISN is initialised to 1, every time!  This can make the prediction
of sequence numbers almost trivial.

In the patch provided, there are two different ways of changing the
ISN: increased speed of cycling through the window using a prime number
to increment with and changing the ISN using the internal clock as a
random number generator (no flames please, I know it's not that good).
I'd recommend trying the first in preference to the second unless you
know what you're doing and what to expect.  The patch should be easily
applied to any of the BSD based operating systems (Ultrix/SunOS/NetBSD/
FreeBSD/BSDI/BSD386) which require compiling /sys/netinet/in_proto.c to
build a kernel.  If you have the full source, you could do it properly
and not need this abomination.

Cheers,
Darren

*** /sys/netinet/in_proto.c.orig        Fri Oct 14 06:45:48 1994
--- /sys/netinet/in_proto.c     Tue Jan 24 17:49:16 1995
***************
*** 21,26 ****
--- 21,27 ----
  #include <netinet/in.h>
  #include <netinet/in_systm.h>
  #include <netinet/tcp_timer.h>
+ #include <netinet/tcp.h>
  
  /*
   * TCP/IP protocol family: IP, ICMP, UDP, TCP.
***************
*** 35,40 ****
--- 36,42 ----
  int   tcp_usrreq(),tcp_ctloutput();
  int   tcp_init(),tcp_fasttimo(),tcp_slowtimo(),tcp_drain();
  int   rip_input(),rip_output(),rip_ctloutput();
+ int   rand_tcp_slowtimer(), rand_tcp_fasttimo(), rand_tcp_init();
  extern        int raw_usrreq();
  
  #ifdef NSIP
***************
*** 57,63 ****
  { SOCK_STREAM,        &inetdomain,    IPPROTO_TCP,    PR_CONNREQUIRED|PR_WANTRCVD,
    tcp_input,  0,              tcp_ctlinput,   tcp_ctloutput,
    tcp_usrreq,
!   tcp_init,   tcp_fasttimo,   tcp_slowtimo,   tcp_drain,
  },
  { SOCK_RAW,   &inetdomain,    IPPROTO_RAW,    PR_ATOMIC|PR_ADDR,
    rip_input,  rip_output,     0,              rip_ctloutput,
--- 59,65 ----
  { SOCK_STREAM,        &inetdomain,    IPPROTO_TCP,    PR_CONNREQUIRED|PR_WANTRCVD,
    tcp_input,  0,              tcp_ctlinput,   tcp_ctloutput,
    tcp_usrreq,
!   rand_tcp_init,      rand_tcp_fasttimo,      rand_tcp_slowtimer,     tcp_drain,
  },
  { SOCK_RAW,   &inetdomain,    IPPROTO_RAW,    PR_ATOMIC|PR_ADDR,
    rip_input,  rip_output,     0,              rip_ctloutput,
***************
*** 148,150 ****
--- 150,233 ----
  int   udp_sendspace = 9000;           /* really max datagram size */
  int   udp_recvspace = 2*(9000+sizeof(struct sockaddr)); /* 2 8K dgrams */
  
+ /*
+  * This is designed to introduce some randomness into the TCP sequence
+  * numbers.
+  * - Darren Reed 24/1/95 (avalon () coombs anu edu au)
+  */
+ extern        tcp_seq tcp_iss;
+ 
+ #define       FASTER_ISN
+ #undef        RANDOM_ISN
+ #ifndef       TCP_COMPAT_42
+ #define       TCP_COMPAT_42           /* better defined than not */
+ #endif
+ /*
+  * Make initial choice more random.
+  */
+ rand_tcp_init()
+ {
+       int     r = tcp_init();
+       unsigned long ul[2];
+ 
+       uniqtime(ul);
+       tcp_iss = ul[0] * ul[1];
+       if (tcp_iss < 0)
+               tcp_iss = -tcp_iss;
+       return r;
+ }
+ 
+ 
+ /*
+  * Get this 2 times a second (PR_SLOWHZ)
+  */
+ rand_tcp_slowtimer()
+ {
+       int     s = splnet(), r;
+       unsigned long ul[2];
+ 
+       r = tcp_slowtimo();
+       /*
+        * we should still be at splnet()
+        */
+ #ifdef        RANDOM_ISN
+       uniqtime(ul);
+       if (!tcp_iss)
+               tcp_iss = ul[0] * 16381;        /* prime multiplier */
+       tcp_iss += ul[1];
+ #else
+ # ifdef       FASTER_ISN
+       tcp_iss += 4356761 / PR_SLOWHZ;         /* prime / non prime */
+ # endif
+ #endif
+ #ifdef        TCP_COMPAT_42
+       if (tcp_iss < 0)
+               tcp_iss = 1;
+ #endif
+       splx(s);
+       return r;
+ }
+ 
+ 
+ /*
+  * Get this 5 times a second (PR_FASTHZ)
+  */
+ rand_tcp_fasttimo()
+ {
+       int     s = splnet(), r;
+ 
+ #ifdef        RANDOM_ISN
+       tcp_iss *= 32749;       /* prime multiplier */
+ #else
+ # ifdef       FASTER_ISN
+       tcp_iss += 149;         /* prime addition */
+ # endif
+ #endif
+ #ifdef        TCP_COMPAT_42
+       if (tcp_iss < 0)
+               tcp_iss = 1;
+ #endif
+       r = tcp_fasttimo();
+       splx(s);
+       return r;
+ }



Current thread: