Today we started discussing the algorithmic aspects of TCP. We will continue on Tuesday. =======================[ Readings ]======================== Continue reading textbook chapters about TCP. Note that TCP gets 5 chapters in the book, Ch. 12--16, whereas all other protocols get at most one plus a few mentions in others. Ch. 12, previously assigned, gets you the overview of TCP. It mentions TCP options that you see in our actual pcaps. These options are further discussed in Ch. 13.3.1--4. Read 13.5.1, which explains the states that a TCP connection can be in. This state machine is a part of the standard, and answers the question of what TCP behavior is expected in response to certain packets in each state. Read 15.1--5 about the delayed ACK, Nagle's algorithm, and their interaction. Read http://www.stuartcheshire.org/papers/nagledelayedack/ about a real-life example of Nagle's algorithm conflicting with the delayed ACK (cf. https://en.wikipedia.org/wiki/Nagle%27s_algorithm). Read 14.1--4 about retransmission timeouts. I found a succinct set of slides that summarizes these issues: http://www.cs.uah.edu/~gcox/670docs/670s08lec4-tcpimplementation.pdf -------------------------[ Tarpits ]------------------------- Tarpits weaponize the use of TCP window set to 0. This causes a spammer's machine to be stuck in a connection to the "tarpit" machine, rather than moving on to connecting to actual production machines. https://en.wikipedia.org/wiki/Tarpit_%28networking%29#The_original_tarpit_idea Of course, an attacker aware of the tarpit trick can program their TCP stack to get unstuck, but that requires the attacker to understand and reprogram TCP internals. -------------------------[ Scapy ]----------------------- Scapy is an extremely versatile Python environment for packet crafting. Scapy encodes, in fairly readable Python code, a lot of protocol RFC knowledge. See http://www.cs.dartmouth.edu/~sergey/cs60/misc-tools/scapy-log.txt For Scapy cheatsheets: https://blogs.sans.org/pen-testing/files/2016/04/ScapyCheatSheet_v0.2.pdf http://packetlife.net/media/library/36/scapy.pdf http://www.packetlevel.ch/html/scapy/docs/scapy_sans.pdf Scapy tutorial (skip to Ch. 3 for examples; Ch. 1 is general info, and Ch. 2 is about installation on different systems. A simple "apt-get install python-scapy" on Linux, "port install scapy" worked for me on MacPorts. Scapy is already installed in your VMs): http://www.dirk-loss.de/scapy-doc/Scapy.pdf -------------------------[ Scapy startup bug ]----------------------- I managed to find the reason for Scapy failing to start on my Mac in class (and, possibly, for some of your Scapy installations crashing, too). Some scapy initialization routines are confused by the vboxnet0 virtual interface being up. They fail to parse the information about it, which leads to a crash. However, bringing this interface down allows Scapy to start up with no problem. This is a bug in Scapy, due to Scapy being too aggressive about finding out which interfaces it can listen on and inject through. I will file a bug report; in the meantime, bring vboxnet0 down before you start scapy at your MacOS rootshell; then you can bring it up again: # scapy Traceback (most recent call last): File "/opt/local/bin/scapy", line 25, in <module> interact() File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/scapy/main.py", line 278, in interact scapy_builtins = __import__("all",globals(),locals(),".").__dict__ <crash skipped> # ifconfig vboxnet0 down # scapy INFO: Can't import PyX. Won't be able to use psdump() or pdfdump(). WARNING: No route found for IPv6 destination :: (no default route?) Welcome to Scapy (2.3.1) >>> a = IP() >>> a.show() ###[ IP ]### version= 4 ihl= None tos= 0x0 len= None id= 1 <skipped> # ifconfig vboxnet0 up -----------------------[ Naive TCP handshake responder ]----------------------- We saw a very naive approach to responding to a TCP handshake today in class. It is posted, with some additions, in http://www.cs.dartmouth.edu/~sergey/cs60/lab4/tcp-raw-responder.c This code falls *far* short of a real TCP implementation, but it shows several things: 1) You really do need a well-defined state machine to make sure your code reacts right to incoming packets in each situation. A collection of if -- if else -- else would simply not do! The states are enumerated in /usr/include/netinet/tcp.h (on Linux) /usr/include/netinet/tcp_fsm.h (on MacOS/Darwin). See 13.5.1, http://www.tcpipguide.com/free/t_TCPConnectionEstablishmentSequenceNumberSynchroniz-2.htm, and http://tcpipguide.com/free/t_TCPOperationalOverviewandtheTCPFiniteStateMachineF-2.htm 2) TCP checksum is a nuisance, because it involves a "pseudo-header" (http://www.tcpipguide.com/free/t_TCPChecksumCalculationandtheTCPPseudoHeader-2.htm) that does not occur in any packet received or sent. My code collects the bytes over which the TCP checksum is run by copying them into a buffer, but this is hugely wasteful: a TCP/IP stack has better things to do than wasting CPU cycles on copying bytes around! In fact, modern TCP/IP stacks are "zero-copy": they never copy the bytes received from the network device until these bytes are given to a userland application (and thus much be copied from kernel space into userland space). Examples of a zero-copy checksum are many; e.g., http://minirighi.sourceforge.net/html/tcp_8c-source.html 3) You can pre-compute the TCP packets to send out for a valid response to an HTTP request for a static message or file. My code breaks the "file" into three lines and sends them one after another, but one can precompute an entire packet-level response to an HTTP request: http://dunkels.com/adam/miniweb/ Then the only remaining thing is to change the destination IP and port in these stored packets, recompute the checksums, and send. This, it turns out, can be done in 30 bytes of binary code. This is a weird example that doesn't belong in production, but it shows that without its algorithms TCP is really simple. In a VM, running # ./tcp-raw-responder In a host window: $ nc 192.168.56.100 80 Hello TCP Great to meet you! Nice weather we are having, but they say it will rain on Monday. ^D In another host window as root: # tcpdump -vx -i vboxnet0 -n tcp // SYN as sent by nc: # tcpdump -vx -i vboxnet0 -n tcp tcpdump: listening on vboxnet0, link-type EN10MB (Ethernet), capture size 65535 bytes 01:35:59.849805 IP (tos 0x0, ttl 64, id 23344, offset 0, flags [DF], proto TCP (6), length 64) 192.168.56.1.62438 > 192.168.56.100.80: Flags [S], cksum 0x844d (correct), seq 255257674, win 65535, options [mss 1460,nop,wscale 5,nop,nop,TS val 387824164 ecr 0,sackOK,eol], length 0 0x0000: 4500 0040 5b30 4000 4006 edd1 c0a8 3801 0x0010: c0a8 3864 f3e6 0050 0f36 ec4a 0000 0000 0x0020: b002 ffff 844d 0000 0204 05b4 0103 0305 0x0030: 0101 080a 171d ba24 0000 0000 0402 0000 // My faked SYN+ACK. Sending RSTs from the VM is blocked by an IPtables rule // (see previous lecture notes). 01:35:59.850215 IP (tos 0x0, ttl 64, id 23344, offset 0, flags [DF], proto TCP (6), length 64) 192.168.56.100.80 > 192.168.56.1.62438: Flags [S.], cksum 0x1dd6 (correct), seq 2863315899, ack 255257675, win 65535, options [mss 1460,nop,wscale 5,nop,nop,TS val 387824164 ecr 0,sackOK,eol], length 0 0x0000: 4500 0040 5b30 4000 4006 edd1 c0a8 3864 0x0010: c0a8 3801 0050 f3e6 aaaa bbbb 0f36 ec4b 0x0020: b012 ffff 1dd6 0000 0204 05b4 0103 0305 0x0030: 0101 080a 171d ba24 0000 0000 0402 0000 // My SYN+ACK passes muster. I get an ACK back, completing the handshake: 01:35:59.850249 IP (tos 0x0, ttl 64, id 24559, offset 0, flags [DF], proto TCP (6), length 52) 192.168.56.1.62438 > 192.168.56.100.80: Flags [.], cksum 0x7c4e (correct), ack 1, win 4117, options [nop,nop,TS val 387824164 ecr 387824164], length 0 0x0000: 4500 0034 5fef 4000 4006 e91e c0a8 3801 0x0010: c0a8 3864 f3e6 0050 0f36 ec4b aaaa bbbc 0x0020: 8010 1015 7c4e 0000 0101 080a 171d ba24 0x0030: 171d ba24 // Now I send my "Hello TCP\n\0" payload (NOTE: seq 1:12) 01:35:59.850415 IP (tos 0x0, ttl 64, id 24559, offset 0, flags [DF], proto TCP (6), length 63) 192.168.56.100.80 > 192.168.56.1.62438: Flags [.], cksum 0xb403 (correct), seq 1:12, ack 1, win 4117, options [nop,nop,TS val 387824164 ecr 387824164], length 11 0x0000: 4500 003f 5fef 4000 4006 e913 c0a8 3864 0x0010: c0a8 3801 0050 f3e6 aaaa bbbc 0f36 ec4b 0x0020: 8010 1015 b403 0000 0101 080a 171d ba24 0x0030: 171d ba24 4865 6c6c 6f20 5443 500a 00 // ...and my next payload... (NOTE: seq 12:32) 01:35:59.850494 IP (tos 0x0, ttl 64, id 24559, offset 0, flags [DF], proto TCP (6), length 72) 192.168.56.100.80 > 192.168.56.1.62438: Flags [.], cksum 0xf447 (correct), seq 12:32, ack 1, win 4117, options [nop,nop,TS val 387824164 ecr 387824164], length 20 0x0000: 4500 0048 5fef 4000 4006 e90a c0a8 3864 0x0010: c0a8 3801 0050 f3e6 aaaa bbc7 0f36 ec4b 0x0020: 8010 1015 f447 0000 0101 080a 171d ba24 0x0030: 171d ba24 4772 6561 7420 746f 206d 6565 0x0040: 7420 796f 7521 0a00 // ...both are ACK-ed, cumulatively (NOTE: ack 32) // (This cumulative ACK rather than two separate ACKs are due to the delayed ACK.) // (Remember: every second segment is still ACK-ed immediately when the delayed ACK) // ( is in force.) 01:35:59.850520 IP (tos 0x0, ttl 64, id 61262, offset 0, flags [DF], proto TCP (6), length 52) 192.168.56.1.62438 > 192.168.56.100.80: Flags [.], cksum 0x7c30 (correct), ack 32, win 4116, options [nop,nop,TS val 387824164 ecr 387824164], length 0 0x0000: 4500 0034 ef4e 4000 4006 59bf c0a8 3801 0x0010: c0a8 3864 f3e6 0050 0f36 ec4b aaaa bbdb 0x0020: 8010 1014 7c30 0000 0101 080a 171d ba24 0x0030: 171d ba24 // ...and there is my third segment (NOTE: seq 32:98): 01:35:59.850593 IP (tos 0x0, ttl 64, id 24559, offset 0, flags [DF], proto TCP (6), length 118) 192.168.56.100.80 > 192.168.56.1.62438: Flags [.], cksum 0x2375 (correct), seq 32:98, ack 1, win 4117, options [nop,nop,TS val 387824164 ecr 387824164], length 66 0x0000: 4500 0076 5fef 4000 4006 e8dc c0a8 3864 0x0010: c0a8 3801 0050 f3e6 aaaa bbdb 0f36 ec4b 0x0020: 8010 1015 2375 0000 0101 080a 171d ba24 0x0030: 171d ba24 4e69 6365 2077 6561 7468 6572 0x0040: 2077 6520 6172 6520 6861 7669 6e67 2c20 0x0050: 6275 7420 7468 6579 2073 6179 2069 7420 0x0060: 7769 6c6c 2072 6169 6e20 6f6e 204d 6f6e 0x0070: 6461 792e 0a00 // ...and an ACK for it: (NOTE: ack 98) 01:35:59.951239 IP (tos 0x0, ttl 64, id 22892, offset 0, flags [DF], proto TCP (6), length 52) 192.168.56.1.62438 > 192.168.56.100.80: Flags [.], cksum 0x7b8b (correct), ack 98, win 4114, options [nop,nop,TS val 387824265 ecr 387824164], length 0 0x0000: 4500 0034 596c 4000 4006 efa1 c0a8 3801 0x0010: c0a8 3864 f3e6 0050 0f36 ec4b aaaa bc1d 0x0020: 8010 1012 7b8b 0000 0101 080a 171d ba89 0x0030: 171d ba24 ^C 8 packets captured 12 packets received by filter 0 packets dropped by kernel Note that this will break in interesting ways if 192.168.56.1 attempts sending any data. That data will not be ACK-ed, because my naive code does not do it (except for the FIN+ACK); it will, however, be echoed back, because my code blindly copies the incoming packet and only swaps (srcIP, dstIP, srcPort, dstPort) around, and adds the payload. This lazy way works for the initial handshake, but not for the actual data transmission! There are so many things wrong with this "implementation", but it fools netcat into receiving the payload strings. Closing this connection cleanly is not hard. First, you'd need to send a FIN packet indicating there will be no more payloads; then you'll want to ACK the FIN from the host side. See http://www.tcpipguide.com/free/t_TCPConnectionTermination-2.htm