Bugtraq mailing list archives

Re: Jeffrey Mogul: Re: screend IP firewall bug


From: pst () CISCO COM (Paul Traina)
Date: Thu, 24 Aug 1995 20:47:38 -0700


The proposed fixes do not solve the problems (at least in the Reed/Ugen
firewall code (it's originally written by Darren Reed, who -discovered-
the first varient of this bug)).

I came up with a simple solution that solves all the attack varients I
could come up with, including out of order packets, garbage in the packet,
and overlapping data.

There is NO need for minimum sized header length checks.

The fix that fixes all known attacks is to simply deny any TCP, and ONLY TCP,
(this is not a typo), packet that has the FO field equal to 1.

I would strongly advise NOT applying any fix based upon the scenario analysis
performed by root () ifreak swan ac uk, as it is incomplete and based upon an
IP implementation that has not been shown to conform to the BSD implementation
in packet reassembly  (in -any- case, I don't want to assume only linux boxes
live behind my firewall).

Given that screend has (was?) well behaved has it kept state of packets that
were fragmented, I do not believe all of the attack methods that could be
used against less sophisticated firewalls, such as the code present in
FreeBSD and Linux distribution, could ever have been applied to screend.
Nevertheless, I would advise putting in the fix I proposed above to screend.


  From: Paul A Vixie <paul () vix com>
  Subject: Jeffrey Mogul: Re: screend IP firewall bug
  this is what we were discussing, yes?

  ------- Forwarded Message

  Delivery-Date: Thu, 24 Aug 1995 12:18:42 -0700
  Return-Path: mogul () pa dec com
  Received: by gw.home.vix.com id AA04500; Thu, 24 Aug 95 12:18:41 -0700
  Received: from acetes.pa.dec.com by mail1.digital.com; (5.65 EXP 4/12/95 for
V3.2/1.0/WV)
        id AA14951; Thu, 24 Aug 1995 11:59:56 -0700
  Received: by acetes.pa.dec.com; id AA02142; Thu, 24 Aug 95 11:59:55 -0700
  Message-Id: <9508241859.AA02142 () acetes pa dec com>
  To: Richard Black <Richard.Black () cl cam ac uk>
  Cc: mogul () pa dec com, paul () vix com
  Subject: Re: screend IP firewall bug
  In-Reply-To: Your message of "Thu, 24 Aug 95 17:58:41 BST."             <"swa
n.cl.cam.:136610:950824165853"@cl.cam.ac.uk>
  Date: Thu, 24 Aug 95 11:59:54 MDT
  From: Jeffrey Mogul <mogul () pa dec com>

      The included message describes a bug in some "vendor"s' IP
      firewalling code which was discussed on a linux group. Having seen
      it I decided to check out the Ultrix and OSF1 firewalling code
      (screend). It seems to have a similar hole.

      This fact is irrelevant as far as we are concerned because we don't
      use screend here, but it seemed like a good idea for me to report
      it to somewhere. Since you wrote it you seemed like a good choice.

      The only source I have access to is Ultrix 4.3 and OSF1 V2.0 so if
      its been fixed since then, then appologies in advance.

  Paul, the original report is appended below.

  Richard, this is the first I've heard of this bug.  Thanks for
  reporting it.

  As far as I can tell, though, the fix included solves a different
  problem from the attack mentioned in "root"'s message.

  There seem to be two different kinds of attack:
  (1) send a fragment starting at offset 8 that overlaps header fields
  which have already been checked.
  (2) send a first packet whose tot_len field is too small, so that
  the firewall checks fields that the reassembler does not include in
  the final packet.

  Am I confused, or does the proposed fix not solve #1 if the reassembler
  uses an algorithm that allows overwriting of overlapping packets?

  Paul, for use in screend I would suggest the code appended after the
  forwarded message.

  - -Jeff

  - - ---------- Forwarded message ----------
  Date: Wed, 23 Aug 1995 10:24:58 +0100 (BST)
  From: System Administrator <root () iifeak swan ac uk>
  To: bugtraq () crimelab com
  Cc: linux-security () tarsier cv nrao edu
  Subject: IP firewalling bugs

  A variety of systems based on the Ugen firewall code (FreeBSD/Linux probably
  NetBSD) are vulnerable to the following reported attack:

        Send an IP fragment 0 acceptable to the firewall
        Send an IP fragment at offset 8 to rewrite most of the header
                and all the data

  For Linux at least the IP header should not be vulnerable to overwriting
  because of the way the fragment merging is done. The following is a provisona
l
  not very tested fix (I only found out about the bug 30 minutes ago). Linux
  is only vulnerable to tcp/udp header overwriting so host level blocking is
  unaffected.

  Because the Ugen firewall is virtually PD a variety of low end
  routers seem to use it and may also be affected.

  I will be issuing a tested fix to Linus for 1.2.14 once he returns from sunni
ng
  himself.

  [This fix is of course GPL'd and Linux but the BSD fix should be similar and
   obvious]

  - - --- ip_fw.c       Thu Jun 29 17:18:52 1995
  +++ /tmp/ip_fw.c      Wed Aug 23 10:11:22 1995
  @@ -209,6 +209,30 @@
         */

        frag1 = ((ntohs(ip->frag_off) & IP_OFFSET) == 0);
  +
  +     /*
  +      *      Stop any lead fragment attacks (eg sending the IP header
  +      *      and then overwriting it with a new fragment). The fragmenter
  +      *      works correctly to stop the rest of this attack.
  +      */
  +
  +     if(frag1)
  +     {
  +             switch(ip->protocol)
  +             {
  +                     case IPPROTO_UDP:
  +                             if(ip->ihl<<2+sizeof(struct udphdr)
  +                                     >ntohs(ip->tot_len))
  +                                     return 0;
  +                             break;
  +                     case IPPROTO_TCP:
  +                             if(ip->ihl<<2+sizeof(struct udphdr)
  +                                     >ntohs(ip->tot_len))
  +                                     return 0;
  +                             break;
  +             }
  +     }
  +
        if (!frag1 && (opt != 1) && (ip->protocol == IPPROTO_TCP ||
                        ip->protocol == IPPROTO_UDP))
                return(1);


  - ------- End of Forwarded Message

  *** unpack.c  Sat Aug 19 02:55:28 1995
  - --- unpack.c.maybe  Thu Aug 24 11:58:42 1995
  ***************
  *** 131,149 ****

        if (ipp->ip_off & IPOFF_MASK) {
            /* do we have to look for fragment leader */
            switch (ipp->ip_p) {
  - -       case IPPROTO_UDP:
  - -       case IPPROTO_TCP:
  - -       case IPPROTO_ICMP:
                /*
                 * For these protocols, we parse into the "ip data"
                 * segment, so we need information out of fragment
                 * 0.
                 */
  !             return(FindFragLeader(ipp, unpp));

            default:
                /* Other protocols: all fragments have enough info */
                break;          /* drop through */
            }
        }
  - --- 131,187 ----

        if (ipp->ip_off & IPOFF_MASK) {
            /* do we have to look for fragment leader */
  +         int offbytes = ipp->ip_off <<= 3;
  +
            switch (ipp->ip_p) {
                /*
                 * For these protocols, we parse into the "ip data"
                 * segment, so we need information out of fragment
                 * 0.
  +              * WARNING: this code assumes NO IP OPTIONS ALLOWED.
  +              * If you change this, the length calculations need
  +              * to be based on stored IP header lengths!
                 */
  !         case IPPROTO_UDP:
  !             if (offbytes >= (sizeof(struct ip) + sizeof(struct udphdr))
  !                 return(FindFragLeader(ipp, unpp));
  !             else {
  !                 if (debug)
  !                     fprintf(stderr,
  !                             "UDP fragment overlaps headers, off %d\n",
  !                             offbytes);
  !                 return(0);
  !             }
  !         case IPPROTO_TCP:
  !             if (offbytes >= (sizeof(struct ip) + sizeof(struct tcphdr))
  !                 return(FindFragLeader(ipp, unpp));
  !             else {
  !                 if (debug)
  !                     fprintf(stderr,
  !                             "TCP fragment overlaps headers, off %d\n",
  !                             offbytes);
  !                 return(0);
  !             }
  !         case IPPROTO_ICMP:
  !             if (offbytes >= (sizeof(struct ip) + MinIcmpLen)
  !                 return(FindFragLeader(ipp, unpp));
  !             else {
  !                 if (debug)
  !                     fprintf(stderr,
  !                             "ICMP fragment overlaps headers, off %d\n",
  !                             offbytes);
  !                 return(0);
  !             }

            default:
                /* Other protocols: all fragments have enough info */
  +             if (offbytes < sizeof(struct ip)) {
  +                 if (debug)
  +                     fprintf(stderr,
  +                             "IP fragment overlaps headers, off %d\n",
  +                             offbytes);
  +                 return(0);
  +             }
                break;          /* drop through */
            }
        }
  ***************
  *** 156,161 ****
  - --- 194,204 ----
                                sizeof(*udpp) - remlen);
                return(0);
            }
  +         if (((ipp->ip_hl<<2) + (sizeof(struct udphdr))) > ipp->ip_len) {
  +             if (debug)
  +                 fprintf(stderr, "UDP ip_len %d too short\n", ipp->ip_len);
  +             return(0);
  +         }
            udpp = (struct udphdr *)ipdata;
            unpp->src.port = ntohs(udpp->uh_sport);
            unpp->dst.port = ntohs(udpp->uh_dport);
  ***************
  *** 168,173 ****
  - --- 211,221 ----
                        sizeof(*tcpp) - remlen);
                return(0);
            }
  +         if (((ipp->ip_hl<<2) + (sizeof(struct tcphdr))) > ipp->ip_len) {
  +             if (debug)
  +                 fprintf(stderr, "TCP ip_len %d too short\n", ipp->ip_len);
  +             return(0);
  +         }
            tcpp = (struct tcphdr *)ipdata;
            unpp->src.port = ntohs(tcpp->th_sport);
            unpp->dst.port = ntohs(tcpp->th_dport);
  ***************
  *** 179,184 ****
  - --- 227,237 ----
                if (debug)
                    fprintf(stderr, "Runt ICMP header (%d short)\n",
                        MinIcmpLen - remlen);
  +             return(0);
  +         }
  +         if (((ipp->ip_hl<<2) + (sizeof(struct icmp))) > ipp->ip_len) {
  +             if (debug)
  +                 fprintf(stderr, "ICMP ip_len %d too short\n", ipp->ip_len);
                return(0);
            }
            icmpp = (struct icmp *)ipdata;


  ------- End of Forwarded Message



Current thread: