├── COPYING ├── README.md └── tcp-connection-hijack-reset.py /COPYING: -------------------------------------------------------------------------------- 1 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 2 | Version 2, December 2004 3 | 4 | Copyright (C) 2012 Mike Kazantsev 5 | 6 | Everyone is permitted to copy and distribute verbatim or modified 7 | copies of this license document, and changing it is allowed as long 8 | as the name is changed. 9 | 10 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 11 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 12 | 13 | 0. You just DO WHAT THE FUCK YOU WANT TO. 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | tcp-connection-hijack-reset 2 | -------------------- 3 | 4 | Simple [scapy](http://www.secdev.org/projects/scapy/) + iptables/ipsets + nflog 5 | tool to hijack and reset existing TCP connections (for both ends), established 6 | from other pids. 7 | 8 | Purpose is not some malicious DoS attacks but rather kicking hung state-machines 9 | in otherwise nice software, while making the whole thing look like a random net 10 | hiccup, which most apps are designed to handle. 11 | 12 | If NFLOG is used (to get packets that should not pass netfilter, for instance), 13 | requires [scapy-nflog-capture](https://github.com/mk-fg/scapy-nflog-capture). 14 | 15 | 16 | Usage 17 | -------------------- 18 | 19 | - Create "conn_cutter" ipset: `ipset create conn_cutter hash:ip,port` 20 | 21 | - Create "conn_cutter" chain (some lines wrapped): 22 | 23 | ``` 24 | -A conn_cutter ! -p tcp -j RETURN 25 | -A conn_cutter -m set ! --match-set conn_cutter src,src -j RETURN 26 | -A conn_cutter -p tcp -m recent --set --name conn_cutter --rsource 27 | -A conn_cutter -p tcp -m recent ! --rcheck --seconds 20\ 28 | --hitcount 2 --name conn_cutter --rsource -j NFLOG 29 | -A conn_cutter -p tcp -m recent ! --rcheck --seconds 20\ 30 | --hitcount 2 --name conn_cutter --rsource -j REJECT --reject-with tcp-reset 31 | ``` 32 | 33 | Note that due to one global "recent" netfilter tag used above, only one 34 | connection can be cut in 20 seconds (others will pass through this chain 35 | unharmed). 36 | 37 | This is done in case of rare pids which may bind() outgoing socket to a 38 | constant port, so that packets of the reconnection attempt from the same port 39 | won't get matched and pass. 40 | 41 | - Update "OUTPUT" chain: 42 | 43 | ``` 44 | -I OUTPUT -j conn_cutter 45 | ``` 46 | 47 | That should be strictly *before* rules like `--state RELATED,ESTABLISHED -j 48 | ACCEPT`. 49 | 50 | - Run: `tcp-connection-hijack-reset.py conn_cutter --pid 1234 --debug` 51 | 52 | Will pick single TCP connection of a specified pid (or raise error if there's 53 | more than one) and cut it, with a lots of noise about what it's doing (due to 54 | "--debug"). 55 | 56 | - Result: both endpoints should reliably get single RST packet and connection 57 | closed promptly. 58 | 59 | See [this 60 | post](http://blog.fraggod.net/2013/04/08/tcp-hijacking-for-the-greater-good.html) 61 | on more details about what it all means and why it's there. 62 | 63 | 64 | Similar tools 65 | -------------------- 66 | 67 | - [dsniff](http://www.monkey.org/~dugsong/dsniff/) - has "tcpkill" binary that 68 | does very similar thing. 69 | 70 | - [tcpkill](https://github.com/chartbeat/tcpkill) - standalone tcpkill tool from 71 | dsniff. 72 | 73 | - [cutter](http://www.digitage.co.uk/digitage/software/cutter) - aims to solve 74 | similar problem, but on a router box (seem to work with conntrack tables 75 | only), and with some strange methods (generating noise on connection to get 76 | seq, which doesn't seem to work at all). 77 | -------------------------------------------------------------------------------- /tcp-connection-hijack-reset.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from __future__ import print_function 3 | 4 | from scapy.all import * 5 | 6 | import itertools as it, operator as op, functools as ft 7 | from subprocess import Popen, PIPE 8 | from os.path import join 9 | import os, sys, signal, re 10 | 11 | 12 | class ConnMultipleMatches(Exception): pass 13 | class ConnNotFound(Exception): pass 14 | 15 | 16 | def get_endpoints(pid=None, port_local=None, port_remote=None): 17 | if pid: matches = list(get_endpoints_by_pid(pid)) 18 | else: 19 | matches = list(get_endpoints_by_port( 20 | port_local=None, port_remote=None )) 21 | 22 | if not matches: raise ConnNotFound() 23 | if len(matches) > 1: raise ConnMultipleMatches(matches) 24 | 25 | local, remote = matches[0] 26 | return (inet_ntoa(struct.pack(' {1[0]}:{1[1]}'.format(local, remote)) 203 | 204 | ipset_update('add', optz.ipset_name, *local) 205 | try: TCPBreaker(port_local=local[1], port_remote=remote[1], timeout=True).run() 206 | finally: ipset_update('del', optz.ipset_name, *local) 207 | 208 | 209 | if __name__ == '__main__': sys.exit(main()) 210 | --------------------------------------------------------------------------------