mirror of
https://github.com/fdiskyou/Zines.git
synced 2025-03-09 00:00:00 +01:00
453 lines
19 KiB
Text
453 lines
19 KiB
Text
![]() |
_ _
|
||
|
_/B\_ _/W\_
|
||
|
(* *) Phrack #64 file 13 (* *)
|
||
|
| - | | - |
|
||
|
| | Blind TCP/IP hijacking is still alive | |
|
||
|
| | | |
|
||
|
| | By lkm <lkm@phrack.org> | |
|
||
|
| | | |
|
||
|
| | | |
|
||
|
(______________________________________________________)
|
||
|
|
||
|
|
||
|
|
||
|
--[ Contents
|
||
|
|
||
|
1 - Introduction
|
||
|
2 - Prerequisites
|
||
|
2.1 - A brief reminder on TCP
|
||
|
2.2 - The interest of IP ID
|
||
|
2.3 - List of informations to gather
|
||
|
|
||
|
3 - Attack description
|
||
|
3.1 - Finding the client-port
|
||
|
3.2 - Finding the server's SND.NEXT
|
||
|
3.3 - Finding the client's SND.NEXT
|
||
|
|
||
|
4 - Discussion
|
||
|
4.1 - Vulnerable systems
|
||
|
4.2 - Limitations
|
||
|
|
||
|
5 - Conclusion
|
||
|
|
||
|
6 - References
|
||
|
|
||
|
|
||
|
--[ 1 - Introduction
|
||
|
|
||
|
Fun with TCP (blind spoofing/hijacking, etc...) was very popular several
|
||
|
years ago when the initials TCP sequence numbers (ISN) were guessable (64K rule,
|
||
|
etc...). Now that the ISNs are fairly well randomized, this stuff seems to be
|
||
|
impossible.
|
||
|
|
||
|
In this paper we will show that it is still possible to perform blind TCP
|
||
|
hijacking nowadays (without attacking the PRNG responsible for generating
|
||
|
the ISNs, like in [1]). We will present a method which works against a number
|
||
|
of systems (Windows 2K, windows XP, and FreeBSD 4). This method is not really
|
||
|
straightforward to implement, but is nonetheless entirely feasible, as we've
|
||
|
coded a tool which was successfully used to perform this attack against all
|
||
|
the vulnerable systems.
|
||
|
|
||
|
|
||
|
--[ 2 - Prerequisites
|
||
|
|
||
|
In this section we will give some informations that are necessary to
|
||
|
understand this paper.
|
||
|
|
||
|
----[ 2.1 - A brief reminder on TCP
|
||
|
|
||
|
A TCP connection between two hosts (which will be called respectively
|
||
|
"client" and "server" in the rest of the paper) can be identified by a tuple
|
||
|
[client-IP, server-IP, client-port, server-port]. While the server port is
|
||
|
well known, the client port is usually in the range 1024-5000, and
|
||
|
automatically assigned by the operating system. (Exemple: the connection
|
||
|
from some guy to freenode may be represented by [ppp289.someISP.com,
|
||
|
irc.freenode.net, 1207, 6667]).
|
||
|
|
||
|
When communication occurs on a TCP connexion, the exchanged TCP packet
|
||
|
headers are containing these informations (actually, the IP header contains
|
||
|
the source/destination IP, and the TCP header contains the
|
||
|
source/destination port). Each TCP packet header also contains fields for a
|
||
|
sequence number (SEQ), and an acknowledgement number (ACK).
|
||
|
|
||
|
Each of the two hosts involved in the connection computes a 32bits SEQ
|
||
|
number randomly at the establishment of the connection. This initial SEQ
|
||
|
number is called the ISN. Then, each time an host sends some packet with
|
||
|
N bytes of data, it adds N to the SEQ number.
|
||
|
The sender put his current SEQ in the SEQ field of each outgoing TCP packet.
|
||
|
The ACK field is filled with the next *expected* SEQ number from the other
|
||
|
host. Each host will maintain his own next sequence number (called
|
||
|
SND.NEXT), and next expected SEQ number from the other host (called
|
||
|
RCV.NEXT).
|
||
|
|
||
|
Let's clarify with an exemple (for the sake of simplicity, we consider that
|
||
|
the connection is already established, and the ports are not shown.)
|
||
|
|
||
|
|
||
|
[===============================================================================]
|
||
|
Client Server
|
||
|
|
||
|
[SND.NEXT=1000] [SND.NEXT=2000]
|
||
|
--[SEQ=1000, ACK=2000, size=20]->
|
||
|
[SND.NEXT=1020] [SND.NEXT=2000]
|
||
|
<-[SEQ=2000, ACK=1020, size=50]--
|
||
|
[SND.NEXT=1020] [SND.NEXT=2050]
|
||
|
--[SEQ=1020, ACK=2050, size=0]->
|
||
|
[===============================================================================]
|
||
|
|
||
|
In the above example, first the client sends 20 bytes of data. Then, the
|
||
|
server acknowledges this data (ACK=1020), and send its own 50 bytes of data
|
||
|
in the same packet. The last packet sent by the client is what we will call
|
||
|
a "simple ACK". It acknowledges the 50-bytes data sent by the server, but
|
||
|
carry no data payload. The "simple ACK" is used, among other cases, where a
|
||
|
host acknowledge received data, but has no data to transmit yet. Obviously,
|
||
|
any well-formed "simple ACK" packet will not be acknowledged, as this would
|
||
|
lead to an infinite loop. Conceptually, each byte has a sequence number,
|
||
|
it's just that the SEQ contained in the TCP header field represents the
|
||
|
sequence number of the first byte. For example, the 20 bytes of the first
|
||
|
packet have sequence numbers 1000..1019.
|
||
|
|
||
|
TCP implements a flow control mechanism by defining the concept of "window".
|
||
|
Each host has a TCP window size (which is dynamic, specific to each TCP
|
||
|
connection, and announced in TCP packets), that we will call RCV.WND.
|
||
|
|
||
|
At any given time, a host will accept bytes with sequence number
|
||
|
between RCV.NXT and (RCV.NXT+RCV.WND-1). This mechanism ensures that at any
|
||
|
tyme, there can be no more than RCV.WND bytes "in transit" to the host.
|
||
|
|
||
|
The establishment and teardown of the connection is managed by flags in the
|
||
|
TCP header. The only useful flags in this paper are SYN, ACK, and RST (for
|
||
|
more information, see RFC793 [2]). The SYN and ACK flags are used in the
|
||
|
connection establishment, as follows:
|
||
|
|
||
|
[===============================================================================]
|
||
|
Client Server
|
||
|
|
||
|
[client picks an ISN]
|
||
|
[SND.NEXT=5000]
|
||
|
--[flags=SYN, SEQ=5000]--> [server picks an ISN]
|
||
|
[SND.NEXT=5001] [SND.NEXT=9000]
|
||
|
<-[flags=SYN+ACK, SEQ=9000, ACK=5001]--
|
||
|
[SND.NEXT=5001] [SND.NEXT=9001]
|
||
|
--[flags=ACK, SEQ=5001, ACK=9001]-->
|
||
|
...connection established...
|
||
|
[===============================================================================]
|
||
|
|
||
|
You'll remark that during the establishment, the SND.NEXT of each hosts is
|
||
|
incremented by 1. That's because the SYN flag counts as one (virtual) byte,
|
||
|
as far as the sequence number is concerned. Thus, any packet with the SYN
|
||
|
flag set will increment the SND.NEXT by 1+packet_data_size (here, the data size
|
||
|
is 0). You'll also note that the ACK field is optional. The ACK field is not
|
||
|
to be confused with the ACK flag, even if they are related: The ACK flag is
|
||
|
set if the ACK field exists. The ACK flag is always set on packets beloning
|
||
|
to an established connection.
|
||
|
|
||
|
The RST flag is used to close a connection abnormally (due to an error, for
|
||
|
example a connection attempt to a closed port).
|
||
|
|
||
|
|
||
|
---- [ 2.2 - The interest of the IP ID
|
||
|
|
||
|
The IP header contains a flag named IP_ID, which is a 16-bits integer used by
|
||
|
the IP fragmentation/reassembly mechanism. This number needs to be unique
|
||
|
for each IP packet sent by an host, but will be unchanged by fragmentation
|
||
|
(thus, fragments of the same packet will have the same IP ID).
|
||
|
|
||
|
Now, you must be wondering why the IP_ID is so interesting? Well, there's a
|
||
|
nifty "feature" in some TCP/IP stacks (including Windows 98, 2K, and XP) :
|
||
|
these stacks store the IP_ID in a global counter, which is simply incremeted
|
||
|
with each IP packet sent. This enables an attacker to probe the IP_ID
|
||
|
counter of an host (with a ping, for exemple), and so, know when the host is
|
||
|
sending packets.
|
||
|
|
||
|
Exemple:
|
||
|
|
||
|
[===============================================================================]
|
||
|
attacker Host
|
||
|
--[PING]->
|
||
|
<-[PING REPLY, IP_ID=1000]--
|
||
|
|
||
|
... wait a little ...
|
||
|
|
||
|
--[PING]->
|
||
|
<-[PING REPLY, IP_ID=1010]--
|
||
|
|
||
|
<attacker> Uh oh, the Host sent 9 IP packets between my pings.
|
||
|
[===============================================================================]
|
||
|
|
||
|
This technique is well known, and has already been exploited to perform
|
||
|
really stealth portscans ([3] and [5]).
|
||
|
|
||
|
|
||
|
----[ 2.3 - List of informations to gather
|
||
|
|
||
|
Well, now, what we need to hijack an existing TCP connection?
|
||
|
|
||
|
First, we need to know the client IP, server IP, client port, and server
|
||
|
port.
|
||
|
In this paper we'll assume that the client IP, server IP, and server port
|
||
|
are known. The difficulty resides in detecting the client port, since it is
|
||
|
randomly assigned by the client's OS. We will see in the following section
|
||
|
how to do that, with the IP_ID.
|
||
|
|
||
|
The next thing we need if we want to be able to hijack both ways (send data
|
||
|
to client from the server, and send data from server to client) is to know
|
||
|
the sequence number of the server, and the client.
|
||
|
|
||
|
Obviously, the most interesting is the client sequence number, because it
|
||
|
enables us to send data to the server that appears to have been sent by the
|
||
|
client. But, as the rest of the paper will show, we'll need to detect the
|
||
|
server's sequence number first, because we will need it to detect the
|
||
|
client's sequence number.
|
||
|
|
||
|
|
||
|
|
||
|
--[ 3 - Attack description
|
||
|
|
||
|
|
||
|
In this section, we will show how to determine the client's port, then the
|
||
|
server's sequence number, and finally the client's sequence number. We will
|
||
|
consider that the client's OS is a vulnerable OS. The server can run on any
|
||
|
OS.
|
||
|
|
||
|
|
||
|
----[ 3.1 - Finding the client-port
|
||
|
|
||
|
Assuming we already know the client/server IP, and the server port, there's
|
||
|
a well known method to test if a given port is the correct client port.
|
||
|
In order to do this, we can send a TCP packet with the SYN flag set to
|
||
|
server-IP:server-port, from client-IP:guessed-client-port (we need to be
|
||
|
able to send spoofed IP packets for this technique to work).
|
||
|
|
||
|
|
||
|
Here's what will happen when we send our packet if the guessed-client-port
|
||
|
is NOT the correct client port:
|
||
|
|
||
|
[===============================================================================]
|
||
|
Attacker (masquerading as client) Server
|
||
|
|
||
|
--[flags=SYN, SEQ=1000]->
|
||
|
|
||
|
Real client
|
||
|
|
||
|
<-[flags=SYN+ACK, SEQ=2000, ACK=1001]--
|
||
|
|
||
|
... the real client didn't start this connection, so it aborts with RST ...
|
||
|
|
||
|
--[flags=RST]->
|
||
|
[===============================================================================]
|
||
|
|
||
|
|
||
|
Here's what will happen when we send our packet if the guessed-client-port
|
||
|
IS the correct client port:
|
||
|
|
||
|
[===============================================================================]
|
||
|
Attacker (masquerading as client) Server
|
||
|
|
||
|
--[flags=SYN, SEQ=1000]->
|
||
|
|
||
|
Real client
|
||
|
|
||
|
... upon reception of our SYN, the server replies by a simple ACK ...
|
||
|
|
||
|
<-[flags=ACK, SEQ=xxxx, ACK=yyyy]--
|
||
|
|
||
|
... the client sends nothing in reply of a simple ACK ...
|
||
|
[===============================================================================]
|
||
|
|
||
|
|
||
|
Now, what's important in all this, is that in the first case the client
|
||
|
sends a packet, and in the second case it doesn't. If you have carefully
|
||
|
read the section 2.2, you know this particular thing can be detected by
|
||
|
probing the IP ID counter of the client.
|
||
|
|
||
|
So, all we have to do to test if a guessed client-port is the correct one
|
||
|
is:
|
||
|
|
||
|
- Send a PING to the client, note the IP ID
|
||
|
- Send our spoofed SYN packet
|
||
|
- Resend a PING to the client, note the new IP ID
|
||
|
- Compare the two IP IDs to determine if the guessed port was correct.
|
||
|
|
||
|
Obviously, if one want to make an efficient scanner, there's many
|
||
|
difficulties, notably the fact that the client may transmit packets on his
|
||
|
own between our two PINGs, and the latency between the client and the server
|
||
|
(which affects the delay after which the client will send his RST packet in
|
||
|
case of an incorrect guess). Coding an efficient client-port scanner is left as
|
||
|
an exercise to the reader :). With our tool - which measures the latency
|
||
|
before the attack and tries to adapt itself to the client's traffic in
|
||
|
real-time - the client-port is usually found in less than 3 minutes.
|
||
|
|
||
|
|
||
|
----[ 3.2 - Finding the server's SND.NEXT
|
||
|
|
||
|
Now that we (hopefully :)) have the client port, we need to know the
|
||
|
server's SND.NEXT (in other words, his current sequence number).
|
||
|
|
||
|
|
||
|
Whenever a host receive a TCP packet with the good source/destination ports,
|
||
|
but an incorrect seq and/or ack, it sends back a simple ACK with the correct
|
||
|
SEQ/ACK numbers. Before we investigate this matter, let's define exactly what
|
||
|
is a correct seq/ack combination, as defined by the RFC793 [2]:
|
||
|
|
||
|
A correct SEQ is a SEQ which is between the RCV.NEXT and (RCV.NEXT+RCV.WND-1)
|
||
|
of the host receiving the packet. Typically, the RCV.WND is a fairly large
|
||
|
number (several dozens of kilobytes at last).
|
||
|
|
||
|
A correct ACK is an ACK which corresponds to a sequence number of something
|
||
|
the host receiving the ACK has already sent. That is, the ACK field of the
|
||
|
packet received by an host must be lower or equal than the host's own
|
||
|
current SND.SEQ, otherwise the ACK is invalid (you can't acknowledge data that
|
||
|
were never sent!).
|
||
|
|
||
|
It is important to node that the sequence number space is "circular".
|
||
|
For exemple, the condition used by the receiving host to check the ACK validity
|
||
|
is not simply the unsigned comparison "ACK <= receiver's SND.NEXT",
|
||
|
but the signed comparison "(ACK - receiver's SND.NEXT) <= 0".
|
||
|
|
||
|
Now, let's return to our original problem: we want to guess server's
|
||
|
SND.NEXT. We know that if we send a wrong SEQ or ACK to the client from the
|
||
|
server, the client will send back an ACK, while if we guess right, the
|
||
|
client will send nothing. As for the client-port detection, this may be
|
||
|
tested with the IP ID.
|
||
|
|
||
|
If we look at the ACK checking formula, we note that if we pick
|
||
|
randomly two ACK values, let's call them ack1 and ack2, such as
|
||
|
|ack1-ack2| = 2^31, then exactly one of them will be valid. For example, let
|
||
|
ack1=0 and ack2=2^31. If the real ACK is between 1 and 2^31 then the ack2
|
||
|
will be an acceptable ack. If the real ACK is 0, or is between (2^32 - 1)
|
||
|
and (2^31 + 1), then, the ack1 will be acceptable.
|
||
|
|
||
|
Taking this into consideration, we can more easily scan the sequence number
|
||
|
space to find the server's SND.NEXT. Each guess will involve the sending of
|
||
|
two packets, each with its SEQ field set to the guessed server's SND.NEXT. The
|
||
|
first packet (resp. second packet) will have his ACK field set to ack1
|
||
|
(resp. ack2), so that we are sure that if the guessed's SND.NEXT is correct, at
|
||
|
least one of the two packet will be accepted.
|
||
|
|
||
|
The sequence number space is way bigger than the client-port space, but two
|
||
|
facts make this scan easier:
|
||
|
|
||
|
First, when the client receive our packet, it replies immediately. There's
|
||
|
not a problem with latency between client and server like in the client-port
|
||
|
scan. Thus, the time between the two IP ID probes can be very small,
|
||
|
speeding up our scanning and reducing greatly the odds that the client will
|
||
|
have IP traffic between our probes and mess with our detection.
|
||
|
|
||
|
Secondly, it's not necessary to test all the possible sequence numbers,
|
||
|
because of the receiver's window. In fact, we need only to do approx.
|
||
|
(2^32 / client's RCV.WND) guesses at worst (this fact has already been
|
||
|
mentionned in [6]). Of course, we don't know the client's RCV.WND.
|
||
|
We can take a wild guess of RCV.WND=64K, perform the
|
||
|
scan (trying each SEQ multiple of 64K). Then, if we didn't find anything,
|
||
|
wen can try all SEQs such as seq = 32K + i*64K for all i. Then, all SEQ such
|
||
|
as seq=16k + i*32k, and so on... narrowing the window, while avoiding to
|
||
|
re-test already tried SEQs. On a typical "modern" connection, this scan
|
||
|
usually takes less than 15 minutes with our tool.
|
||
|
|
||
|
With the server's SND.NEXT known, and a method to work around our ignorance
|
||
|
of the ACK, we may hijack the connection in the way "server -> client". This
|
||
|
is not bad, but not terribly useful, we'd prefer to be able to send data
|
||
|
from the client to the server, to make the client execute a command, etc...
|
||
|
In order to do this, we need to find the client's SND.NEXT.
|
||
|
|
||
|
----[ 3.3 - Finding the client's SND.NEXT
|
||
|
|
||
|
What we can do to find the client's SND.NEXT ? Obviously we can't use the
|
||
|
same method as for the server's SND.NEXT, because the server's OS is
|
||
|
probably not vunerable to this attack, and besides, the heavy network
|
||
|
traffic on the server would render the IP ID analysis infeasible.
|
||
|
|
||
|
However, we know the server's SND.NEXT. We also know that the client's
|
||
|
SND.NEXT is used for checking the ACK fields of client's incoming packets.
|
||
|
So we can send packets from the server to the client with SEQ field set to
|
||
|
server's SND.NEXT, pick an ACK, and determine (again with IP ID) if our ACK
|
||
|
was acceptable.
|
||
|
|
||
|
If we detect that our ACK was acceptable, that means that
|
||
|
(guessed_ACK - SND.NEXT) <= 0. Otherwise, it means.. well, you guessed it,
|
||
|
that (guessed_ACK - SND_NEXT) > 0.
|
||
|
|
||
|
Using this knowledge, we can find the exact SND_NEXT in at most 32 tries
|
||
|
by doing a binary search (a slightly modified one, because the sequence
|
||
|
space is circular).
|
||
|
|
||
|
Now, at last we have all the required informations and we can perform the
|
||
|
session hijacking from either client or server.
|
||
|
|
||
|
--[ 4 - Discussion
|
||
|
|
||
|
In this section we'll attempt to identify the affected systems, discuss
|
||
|
limitations of this attacks, present similar attacks against older systems.
|
||
|
|
||
|
----[ 4.1 - Vulnerable systems
|
||
|
|
||
|
This attack has been tested on Windows 2K, Windows XP <= SP2, and FreeBSD 4.
|
||
|
It should be noted that FreeBSD has a kernel option to randomize the IP ID,
|
||
|
which makes this attack impossible. As far as we know, there's no fix for
|
||
|
Windows 2K and XP.
|
||
|
|
||
|
The only "bug" which makes this attack possible on the vulnerable systems is
|
||
|
the non-randomized IP ID. The other behaviors (ACK checking that enables us
|
||
|
to do a binary search, etc...) are expected by the RFC793 [2] (however, there's
|
||
|
been work to improve these problems in [4]).
|
||
|
|
||
|
It's interesting to see that, as far as we could test, only Windows 2K,
|
||
|
Windows XP, and FreeBSD 4 were vulnerable. There's other OS which use the
|
||
|
same IP ID incrementation system, but they don't use the same ACK checking
|
||
|
mechanism. Hmm.. this similarity between Windows's and FreeBSD's TCP/IP
|
||
|
stack behavior is troubling... :) MacOS X is based on FreeBSD but is not
|
||
|
vulnerable because it uses a different IP ID numbering scheme. Windows Vista
|
||
|
wasn't tested.
|
||
|
|
||
|
|
||
|
----[ 4.2 - Limitations
|
||
|
|
||
|
The described attack has various limitations:
|
||
|
|
||
|
First, the attack doesn't work "as is" on Windows 98. That's not really a
|
||
|
limitation, because the initial SEQ of Windows 98 is equal to the uptime of
|
||
|
the machine in milliseconds, modulo 2^32. We won't discuss how to do
|
||
|
hijacking with Windows 98 because it's a trivial joke :)
|
||
|
|
||
|
Secondly, the attack will be difficult if the client has a slow connection,
|
||
|
or has a lot of traffic (messing with the IP ID analysis). Also, there's the
|
||
|
problem of the latency between the client and the server. These problems can
|
||
|
be mitigated by writing an intelligent tool which measures the latency,
|
||
|
detects when the host has traffic, etc...
|
||
|
|
||
|
Furthermore, we need access to the client host. We need to be able to send
|
||
|
packets and receive replies to get the IP ID. Any type of packet will do, ICMP
|
||
|
or TCP or whatever. The attack will not be possible if the host is behind a
|
||
|
firewall/NAT/... which blocks absolutely all type of packets, but 1
|
||
|
unfiltered port (even closed on the client) suffices to make the attack
|
||
|
possible. This problem is present against Windows XP SP2 and later, which
|
||
|
comes with an integrated firewall. Windows XP SP2 is vulnerable, but the
|
||
|
firewall may prevent the attack in some situations.
|
||
|
|
||
|
|
||
|
--[ 5 - Conclusion
|
||
|
|
||
|
In this paper we have presented a method of blind TCP hijacking which works
|
||
|
on Windows 2K/XP, and FreeBSD 4. While this method has a number of
|
||
|
limitations, it's perfectly feasible and works against a large number of
|
||
|
hosts. Furthermore, a large number of protocols over TCP still use
|
||
|
unencrypted communication, so the impact on security of the blind TCP
|
||
|
hijacking is not negligible.
|
||
|
|
||
|
|
||
|
--[ 6 - References
|
||
|
|
||
|
[1] http://lcamtuf.coredump.cx/newtcp/
|
||
|
|
||
|
[2] http://www.ietf.org/rfc/rfc793.txt
|
||
|
|
||
|
[3] http://insecure.org/nmap/idlescan.html
|
||
|
|
||
|
[4] http://www.ietf.org/internet-drafts/draft-ietf-tcpm-tcpsecure-07.txt
|
||
|
|
||
|
[5] http://seclists.org/bugtraq/1998/Dec/0079.html
|
||
|
|
||
|
[6] http://osvdb.org/reference/SlippingInTheWindow_v1.0.doc
|
||
|
|