Search This Blog

Friday, November 9, 2012

How to forcibly kill an established TCP connection in Linux

During one of the repros I needed to find a way to kill an already established TCP session but without killing the process that opened it.

Problem

How to kill an ESTABLISHED TCP connection in Linux.

Solution #1: passive mechanism

We can use a little tool called tcpkill that you can find desription and example usage here link 

tcpkill -i eth0 { expression }

When started it first listens on a wire for any traffic matching your expression filter (that is compatible with tcpdump expressions). This is passive mechanism to learn about the TCP ACK and SEQ numbers. Once it knows the numbers it spoofs a TCP segments and try to reset the TCP session on both sides.

If the TCP session is idle or in generic there is no data being exchanged than this passive mechanism unfortunately is not going to work.

Solution #2: active mechanism

This killcx tool is taking a different approach and tries to generate traffic on the wire to discover what the ACK and SEQ numbers are. Once it forces the peer to replay to its spoofed traffic it kills the TCP session immediately after.

killcx.pl dest_ip:dest_port 

Simple demonstration
  1. tcpkill (passive)
On 2 different hosts we simulate a daemon that actively listens on incoming traffic and a client that initiate a connection to a server. Once a TCP session is created the client sent single byte. The commands show the outputs.

root@server:~# nc -v -l 7777                                                                                       
Connection from 164.177.146.87 port 7777 [tcp/*] accepted
a

root@client:~# nc -D 5.79.21.166 7777
a
b

Below are tcpdumps showing TCP handshaking and 1 byte exchange on both hosts.

root@server:~# tcpdump -i any -nn port 7777
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on any, link-type LINUX_SLL (Linux cooked), capture size 65535 bytes
23:15:59.049739 IP 164.177.146.87.50185 > 5.79.21.166.7777: Flags [S], seq 2946797440, win 14600, options [mss 1460,sackOK,TS val 726987371 ecr 0,nop,wscale 2], length 0
23:15:59.049803 IP 5.79.21.166.7777 > 164.177.146.87.50185: Flags [S.], seq 2303834803, ack 2946797441, win 14480, options [mss 1460,sackOK,TS val 350774705 ecr 726987371,nop,wscale 3], length 0
23:15:59.051394 IP 164.177.146.87.50185 > 5.79.21.166.7777: Flags [.], ack 1, win 3650, options [nop,nop,TS val 726987375 ecr 350774705], length 0
23:16:00.815434 IP 164.177.146.87.50185 > 5.79.21.166.7777: Flags [P.], seq 1:3, ack 1, win 3650, options [nop,nop,TS val 726987816 ecr 350774705], length 2
23:16:00.815493 IP 5.79.21.166.7777 > 164.177.146.87.50185: Flags [.], ack 3, win 1810, options [nop,nop,TS val 350775146 ecr 726987816], length 0

root@client:~# tcpdump -i any -nn port 7777
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on any, link-type LINUX_SLL (Linux cooked), capture size 65535 bytes
23:20:38.159839 IP 164.177.146.87.50185 > 5.79.21.166.7777: Flags [S], seq 2946797440, win 14600, options [mss 1460,sackOK,TS val 726987371 ecr 0,nop,wscale 2], length 0
23:20:38.174837 IP 5.79.21.166.7777 > 164.177.146.87.50185: Flags [S.], seq 2303834803, ack 2946797441, win 14480, options [mss 1460,sackOK,TS val 350774705 ecr 726987371,nop,wscale 3], length 0
23:20:38.174880 IP 164.177.146.87.50185 > 5.79.21.166.7777: Flags [.], ack 1, win 3650, options [nop,nop,TS val 726987375 ecr 350774705], length 0
23:20:39.938895 IP 164.177.146.87.50185 > 5.79.21.166.7777: Flags [P.], seq 1:3, ack 1, win 3650, options [nop,nop,TS val 726987816 ecr 350774705], length 2
23:20:39.939313 IP 5.79.21.166.7777 > 164.177.146.87.50185: Flags [.], ack 3, win 1810, options [nop,nop,TS val 350775146 ecr 726987816], length 0 

At this point from another terminal on the server we can verify that the connection exists and start the tcpkill to try to kill it.

root@server:~# netstat -tulpan | grep 7777
tcp        0      0 0.0.0.0:7777            0.0.0.0:*               LISTEN      1338/nc
tcp        0      0 5.79.21.166:7777        164.177.146.87:50185    ESTABLISHED 1338/nc
root@server:~# tcpkill -i eth0 -9 port 50185

At this stage nothing is going to happen because neither the client or server sending any data. The tcpkill simply hangs and waits. The open TCP session continue to exists. But as soon as we sent another byte from the client to the server these is being logged on the tcpdump sessions  for the tcpkill, client and server.

tcpkill

root@server:~# tcpkill -i eth0 -9 port 50185
tcpkill: listening on eth0 [port 50185]
164.177.146.87:50185 > 5.79.21.166:7777: R 2303834804:2303834804(0) win 0             
164.177.146.87:50185 > 5.79.21.166:7777: R 2303838454:2303838454(0) win 0
164.177.146.87:50185 > 5.79.21.166:7777: R 2303845754:2303845754(0) win 0
164.177.146.87:50185 > 5.79.21.166:7777: R 2303856704:2303856704(0) win 0
164.177.146.87:50185 > 5.79.21.166:7777: R 2303871304:2303871304(0) win 0
164.177.146.87:50185 > 5.79.21.166:7777: R 2303889554:2303889554(0) win 0
164.177.146.87:50185 > 5.79.21.166:7777: R 2303911454:2303911454(0) win 0
164.177.146.87:50185 > 5.79.21.166:7777: R 2303937004:2303937004(0) win 0
164.177.146.87:50185 > 5.79.21.166:7777: R 2303966204:2303966204(0) win 0
5.79.21.166:7777 > 164.177.146.87:50185: R 2946797445:2946797445(0) win 0
5.79.21.166:7777 > 164.177.146.87:50185: R 2946799255:2946799255(0) win 0
5.79.21.166:7777 > 164.177.146.87:50185: R 2946802875:2946802875(0) win 0
5.79.21.166:7777 > 164.177.146.87:50185: R 2946808305:2946808305(0) win 0
5.79.21.166:7777 > 164.177.146.87:50185: R 2946815545:2946815545(0) win 0
5.79.21.166:7777 > 164.177.146.87:50185: R 2946824595:2946824595(0) win 0
5.79.21.166:7777 > 164.177.146.87:50185: R 2946835455:2946835455(0) win 0
5.79.21.166:7777 > 164.177.146.87:50185: R 2946848125:2946848125(0) win 0
5.79.21.166:7777 > 164.177.146.87:50185: R 2946862605:2946862605(0) win 0

client

root@client:~# tcpdump -i any -nn port 7777
...
23:21:03.938535 IP 164.177.146.87.50185 > 5.79.21.166.7777: Flags [P.], seq 3:5, ack 1, win 3650, options [nop,nop,TS val 726993816 ecr 350775146], length 2
23:21:03.943047 IP 5.79.21.166.7777 > 164.177.146.87.50185: Flags [.], ack 5, win 1810, options [nop,nop,TS val 350781146 ecr 726993816], length 0
23:21:03.943070 IP 5.79.21.166.7777 > 164.177.146.87.50185: Flags [R], seq 2303834804, win 0, length 0
23:21:03.943082 IP 5.79.21.166.7777 > 164.177.146.87.50185: Flags [R], seq 2303838454, win 0, length 0
23:21:03.943086 IP 5.79.21.166.7777 > 164.177.146.87.50185: Flags [R], seq 2303845754, win 0, length 0
23:21:03.943090 IP 5.79.21.166.7777 > 164.177.146.87.50185: Flags [R], seq 2303856704, win 0, length 0
23:21:03.943093 IP 5.79.21.166.7777 > 164.177.146.87.50185: Flags [R], seq 2303871304, win 0, length 0
23:21:03.943096 IP 5.79.21.166.7777 > 164.177.146.87.50185: Flags [R], seq 2303889554, win 0, length 0
23:21:03.943099 IP 5.79.21.166.7777 > 164.177.146.87.50185: Flags [R], seq 2303911454, win 0, length 0
23:21:03.943102 IP 5.79.21.166.7777 > 164.177.146.87.50185: Flags [R], seq 2303937004, win 0, length 0 
23:21:03.943106 IP 5.79.21.166.7777 > 164.177.146.87.50185: Flags [R], seq 2303966204, win 0, length 0

Server

root@server:~# tcpdump -i any -nn port 7777
...
23:16:24.815402 IP 164.177.146.87.50185 > 5.79.21.166.7777: Flags [P.], seq 3:5, ack 1, win 3650, options [nop,nop,TS val 726993816 ecr 350775146], length 2
23:16:24.815449 IP 5.79.21.166.7777 > 164.177.146.87.50185: Flags [.], ack 5, win 1810, options [nop,nop,TS val 350781146 ecr 726993816], length 0
23:16:24.816040 IP 5.79.21.166.7777 > 164.177.146.87.50185: Flags [R], seq 2303834804, win 0, length 0
23:16:24.816080 IP 5.79.21.166.7777 > 164.177.146.87.50185: Flags [R], seq 2303838454, win 0, length 0
23:16:24.816103 IP 5.79.21.166.7777 > 164.177.146.87.50185: Flags [R], seq 2303845754, win 0, length 0
23:16:24.816130 IP 5.79.21.166.7777 > 164.177.146.87.50185: Flags [R], seq 2303856704, win 0, length 0
23:16:24.816153 IP 5.79.21.166.7777 > 164.177.146.87.50185: Flags [R], seq 2303871304, win 0, length 0
23:16:24.816177 IP 5.79.21.166.7777 > 164.177.146.87.50185: Flags [R], seq 2303889554, win 0, length 0
23:16:24.816199 IP 5.79.21.166.7777 > 164.177.146.87.50185: Flags [R], seq 2303911454, win 0, length 0
23:16:24.816225 IP 5.79.21.166.7777 > 164.177.146.87.50185: Flags [R], seq 2303937004, win 0, length 0
23:16:24.816245 IP 5.79.21.166.7777 > 164.177.146.87.50185: Flags [R], seq 2303966204, win 0, length 0
23:16:24.816280 IP 164.177.146.87.50185 > 5.79.21.166.7777: Flags [R], seq 2946797445, win 0, length 0
23:16:24.816314 IP 164.177.146.87.50185 > 5.79.21.166.7777: Flags [R], seq 2946799255, win 0, length 0
23:16:24.816339 IP 164.177.146.87.50185 > 5.79.21.166.7777: Flags [R], seq 2946802875, win 0, length 0
23:16:24.816362 IP 164.177.146.87.50185 > 5.79.21.166.7777: Flags [R], seq 2946808305, win 0, length 0
23:16:24.816384 IP 164.177.146.87.50185 > 5.79.21.166.7777: Flags [R], seq 2946815545, win 0, length 0
23:16:24.816407 IP 164.177.146.87.50185 > 5.79.21.166.7777: Flags [R], seq 2946824595, win 0, length 0
23:16:24.816429 IP 164.177.146.87.50185 > 5.79.21.166.7777: Flags [R], seq 2946835455, win 0, length 0
23:16:24.816451 IP 164.177.146.87.50185 > 5.79.21.166.7777: Flags [R], seq 2946848125, win 0, length 0
23:16:24.816474 IP 164.177.146.87.50185 > 5.79.21.166.7777: Flags [R], seq 2946862605, win 0, length 0


We can see that as soon as the new byte generate 2 TCP segment the tcpkill learns about the SEQ and ACK numbers and generate its RST packet to desynchronize and kill the TCP session.
  1. killcx (active)
We start the 2 client and server sessions in a similar way. These are the command output showing what is happening on the wire this time.

server

root@server:~# tcpdump -i any -nn port 7777
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on any, link-type LINUX_SLL (Linux cooked), capture size 65535 bytes
23:46:06.228566 IP 164.177.146.87.50189 > 5.79.21.166.7777: Flags [S], seq 11257294, win 14600, options [mss 1460,sackOK,TS val 727439173 ecr 0,nop,wscale 2], length 0
23:46:06.228625 IP 5.79.21.166.7777 > 164.177.146.87.50189: Flags [S.], seq 1319656020, ack 11257295, win 14480, options [mss 1460,sackOK,TS val 351226499 ecr 727439173,nop,wscale 3], length 0
23:46:06.233328 IP 164.177.146.87.50189 > 5.79.21.166.7777: Flags [.], ack 1, win 3650, options [nop,nop,TS val 727439174 ecr 351226499], length 0
23:46:07.960259 IP 164.177.146.87.50189 > 5.79.21.166.7777: Flags [P.], seq 1:3, ack 1, win 3650, options [nop,nop,TS val 727439605 ecr 351226499], length 2
23:46:07.960322 IP 5.79.21.166.7777 > 164.177.146.87.50189: Flags [.], ack 3, win 1810, options [nop,nop,TS val 351226932 ecr 727439605], length 0
# at this moment the TCP is estabilished and the server received one byte

# we see this as soon as the killcx is started
23:46:32.411167 IP 164.177.146.87.50189 > 5.79.21.166.7777: Flags [S], seq 10, win 65535, length 0
23:46:32.411194 IP 5.79.21.166.7777 > 164.177.146.87.50189: Flags [.], ack 3, win 1810, options [nop,nop,TS val 351233045 ecr 727439605,nop,nop,sack 1 {4283710012:4283710013}], length 0
23:46:32.413315 IP 164.177.146.87.50189 > 5.79.21.166.7777: Flags [R], seq 11257297, win 65535, length 0
23:46:32.414945 IP 5.79.21.166.7777 > 164.177.146.87.50189: Flags [R], seq 1319656021, win 65535, length 0

client

root@client:~# tcpdump -i any -nn port 7777
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on any, link-type LINUX_SLL (Linux cooked), capture size 65535 bytes
23:50:45.365610 IP 164.177.146.87.50189 > 5.79.21.166.7777: Flags [S], seq 11257294, win 14600, options [mss 1460,sackOK,TS val 727439173 ecr 0,nop,wscale 2], length 0
23:50:45.370015 IP 5.79.21.166.7777 > 164.177.146.87.50189: Flags [S.], seq 1319656020, ack 11257295, win 14480, options [mss 1460,sackOK,TS val 351226499 ecr 727439173,nop,wscale 3], length 0
23:50:45.370059 IP 164.177.146.87.50189 > 5.79.21.166.7777: Flags [.], ack 1, win 3650, options [nop,nop,TS val 727439174 ecr 351226499], length 0
23:50:47.097391 IP 164.177.146.87.50189 > 5.79.21.166.7777: Flags [P.], seq 1:3, ack 1, win 3650, options [nop,nop,TS val 727439605 ecr 351226499], length 2
23:50:47.098888 IP 5.79.21.166.7777 > 164.177.146.87.50189: Flags [.], ack 3, win 1810, options [nop,nop,TS val 35122693221:20:14439605], length 0
# at this moment the TCP is estabilished and the client sent one byte

# we see this as soon as the killcx is started
23:51:11.550122 IP 5.79.21.166.7777 > 164.177.146.87.50189: Flags [.], ack 3, win 1810, options [nop,nop,TS val 351233045 ecr 727439605,nop,nop,sack 1 {4283710012:4283710013}], length 0
23:51:11.552854 IP 5.79.21.166.7777 > 164.177.146.87.50189: Flags [R], seq 1319656021, win 65535, length 0

killcx

root@server:~# netstat -tulpan | grep 7777
tcp        0      0 0.0.0.0:7777            0.0.0.0:*               LISTEN      10568/nc
tcp        0      0 5.79.21.166:7777        164.177.146.87:50189    ESTABLISHED 10568/nc

root@manage2:~# ./killcx.pl 164.177.146.87:50189
killcx v1.0.3 - (c)2009-2011 Jerome Bruandet - http://killcx.sourceforge.net/

[PARENT] checking connection with [164.177.146.87:50189]
[PARENT] found connection with [5.79.21.166:7777] (ESTABLISHED)
[PARENT] forking child
[CHILD]  interface not defined, will use [eth0]
[CHILD]  setting up filter to sniff ACK on [eth0] for 5 seconds
[PARENT] sending spoofed SYN to [5.79.21.166:7777] with bogus SeqNum
[CHILD]  hooked ACK from [5.79.21.166:7777]
[CHILD]  found AckNum [11257297] and SeqNum [1319656021]
[CHILD]  sending spoofed RST to [5.79.21.166:7777] with SeqNum [11257297]
[CHILD]  sending RST to remote host as well with SeqNum [1319656021]
[CHILD]  all done, sending USR1 signal to parent [10726] and exiting
[PARENT] received child signal, checking results...
         => success : connection has been closed !

As before the TCP session is established and idle waiting. As soon as we start the killcx it successfully spoofs a SYN packet on behalf of the client and sent it to the server. The server than replays with a valid TCP packet revealing the ACK and SEQ numbers. As soon as this this is on a wire the killcx sniffs this up and sent RST to kill the active session.

References
  1. http://killcx.sourceforge.net/
  2. http://www.cyberciti.biz/howto/question/linux/kill-tcp-connection-using-linux-netstat.php

  3. Further reading
  4. http://serverfault.com/questions/179702/is-it-possible-to-close-a-socket-with-a-shell-command
  5. http://superuser.com/questions/127863/manually-closing-a-port-from-commandline

No comments:

Post a Comment