如果一件事情你不能讲清楚,十有八九你还没有完全理解。
实验目的
基础脚本
# cat tcp_si_close_000.pkt0 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3+0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0+0 bind(3, ..., ...) = 0+0 listen(3, 1) = 0+0 < S 0:0(0) win 10000 <mss 1460>+0 > S. 0:0(0) ack 1 <...>+0.01 < . 1:1(0) ack 1 win 10000+0 accept(3, ..., ...) = 4
TCP Simultaneous Close
实验测试
修改脚本如下,执行。
# cat tcp_si_close_001.pkt0 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3+0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0+0 bind(3, ..., ...) = 0+0 listen(3, 1) = 0+0 < S 0:0(0) win 10000 <mss 1460>+0 > S. 0:0(0) ack 1 <...>+0.01 < . 1:1(0) ack 1 win 10000+0 accept(3, ..., ...) = 4+0 close(4) = 0+0 > F. 1:1(0) ack 1+0 < F. 1:1(0) ack 1 win 10000+0 > . 2:2(0) ack 2+0.01 < . 2:2(0) ack 2 win 10000#
执行脚本,并观察 tcpdump 抓包结果,预期运行正常。
# packetdrill tcp_si_close_001.pkt### tcpdump -i any -nn port 8080tcpdump: data link type LINUX_SLL2tcpdump: verbose output suppressed, use -v[v]... for full protocol decodelistening on any, link-type LINUX_SLL2 (Linux cooked v2), snapshot length 262144 bytes15:48:39.919678 tun0 In IP 192.0.2.1.36845 > 192.168.135.169.8080: Flags [S], seq 0, win 10000, options [mss 1460], length 015:48:39.919709 tun0 Out IP 192.168.135.169.8080 > 192.0.2.1.36845: Flags [S.], seq 2919419743, ack 1, win 65535, options [mss 1460], length 015:48:39.929804 tun0 In IP 192.0.2.1.36845 > 192.168.135.169.8080: Flags [.], ack 1, win 10000, length 015:48:39.929888 tun0 Out IP 192.168.135.169.8080 > 192.0.2.1.36845: Flags [F.], seq 1, ack 1, win 65535, length 015:48:39.929955 tun0 In IP 192.0.2.1.36845 > 192.168.135.169.8080: Flags [F.], seq 1, ack 1, win 10000, length 015:48:39.929962 tun0 Out IP 192.168.135.169.8080 > 192.0.2.1.36845: Flags [.], ack 2, win 65535, length 015:48:39.940026 ? In IP 192.0.2.1.36845 > 192.168.135.169.8080: Flags [.], ack 2, win 10000, length 015:48:39.940120 ? In IP 192.0.2.1.36845 > 192.168.135.169.8080: Flags [R.], seq 2, ack 2, win 10000, length 0#1. 前三个数据包为 TCP 三次握手数据包,略过。2. 第四个数据包为服务器端 close 所发出的 FIN 数据包。15:48:39.919678 tun0 In IP 192.0.2.1.36845 > 192.168.135.169.8080: Flags [S], seq 0, win 10000, options [mss 1460], length 0// 对应于脚本 +0 > F. 1:1(0) ack 1 ,close 关闭连接,预期服务器端协议栈发出 FIN 。3. 第五个数据包为模拟注入的客户端 FIN 数据包。15:48:39.929955 tun0 In IP 192.0.2.1.36845 > 192.168.135.169.8080: Flags [F.], seq 1, ack 1, win 10000, length 0// 对应于脚本 +0 < F. 1:1(0) ack 1 win 10000 ,无时间间隔,为模拟客户端同时发起的 FIN 。4. 第六个数据包为服务器端收到客户端 FIN 所响应的 ACK 数据包。15:48:39.929962 tun0 Out IP 192.168.135.169.8080 > 192.0.2.1.36845: Flags [.], ack 2, win 65535, length 0// 对应于脚本 +0 > . 2:2(0) ack 2 ,为服务器端收到 FIN 预期自动所响应的 ACK 。5. 第七个数据包为模拟客户端收到服务器端 FIN 所响应的 ACK 数据包。15:48:39.940026 ? In IP 192.0.2.1.36845 > 192.168.135.169.8080: Flags [.], ack 2, win 10000, length 0// 对应于脚本 +0.01 < . 2:2(0) ack 2 win 10000 ,为模拟客户端收到 FIN 后所响应的 ACK 。6. 最后一个数据包 RST/ACK15:48:39.940120 ? In IP 192.0.2.1.36845 > 192.168.135.169.8080: Flags [R.], seq 2, ack 2, win 10000, length 0// packetdrill 脚本之外产生的数据包,或者说预期之外的数据包。// 客户端所发送的 RST/ACK 结束连接,如前所述是 packetdrill 脚本默认构造,用于快速重置连接使用的。// 因此这个数据包一般与 packetdrill 构建的测试脚本无关,分析时可省略。
如何确认同时关闭连接时的 TCP 状态变化过程,可以通过 BCC 工具 tcpstate 进行检查如下。
# packetdrill tcp_si_close_001.pkt## ./tcpstates.py -L 8080SKADDR C-PID C-COMM LADDR LPORT RADDR RPORT OLDSTATE -> NEWSTATE MSffff8a820397abc0 362994 packetdril 192.168.124.5 8080 0.0.0.0 0 CLOSE -> LISTEN 0.000ffff8a820397f1c0 362994 packetdril 192.168.124.5 8080 0.0.0.0 0 LISTEN -> SYN_RECV 0.000ffff8a820397f1c0 362994 packetdril 192.168.124.5 8080 192.0.2.1 39231 SYN_RECV -> ESTABLISHED 0.009ffff8a820397f1c0 362994 packetdril 192.168.124.5 8080 192.0.2.1 39231 ESTABLISHED -> FIN_WAIT1 0.019ffff8a820397f1c0 362994 packetdril 192.168.124.5 8080 192.0.2.1 39231 FIN_WAIT1 -> CLOSING 0.052ffff8a820397f1c0 362994 packetdril 192.168.124.5 8080 192.0.2.1 39231 CLOSING -> CLOSE 10.052ffff8a820397abc0 362994 packetdril 192.168.124.5 8080 0.0.0.0 0 LISTEN -> CLOSE 20.341#
从上述 TCP 状态变化,可以看到服务器端 TCP 状态变化由 ESTABLISHED -> FIN_WAIT1 -> CLOSING -> CLOSE。但相较 RFC 标准,少了一个 TIME_WAIT 状态,起初以为是脚本直接结束,最后一个 RST 直接结束了 TW 状态,但仔细一想也不对,既然是完整状态切换,理应也要经过 TW 状态,最后再变为 CLOSE 状态。
为了进一步验证,修改脚本增加 sleep ,也就是不结束脚本,尝试看下 tcpstate 是否有 TW 状态变化。
# cat tcp_si_close_002.pkt0 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3+0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0+0 bind(3, ..., ...) = 0+0 listen(3, 1) = 0+0 < S 0:0(0) win 10000 <mss 1460>+0 > S. 0:0(0) ack 1 <...>+0.01 < . 1:1(0) ack 1 win 10000+0 accept(3, ..., ...) = 4+0 close(4) = 0+0 > F. 1:1(0) ack 1+0 < F. 1:1(0) ack 1 win 10000+0 > . 2:2(0) ack 2+0.01 < . 2:2(0) ack 2 win 10000+0 `sleep 100`#
可以看到 BCC tcpstate 所捕获的状态变化,仍没有 TW,而且从 CLOSING -> CLOSE 状态变化的时间,间隔 10ms ,这也符合脚本中最后一个间隔 10ms 后模拟注入的 ACK 数据包,收到 ACK 后状态发生变化,理论应为 CLOSING -> TIME_WAIT 。
# packetdrill tcp_si_close_002.pkt## ./tcpstates.py -L 8080SKADDR C-PID C-COMM LADDR LPORT RADDR RPORT OLDSTATE -> NEWSTATE MSffff8a8205123d40 380850 packetdril 192.168.204.46 8080 0.0.0.0 0 CLOSE -> LISTEN 0.000ffff8a82051208c0 380850 packetdril 192.168.204.46 8080 0.0.0.0 0 LISTEN -> SYN_RECV 0.000ffff8a82051208c0 380850 packetdril 192.168.204.46 8080 192.0.2.1 44937 SYN_RECV -> ESTABLISHED 0.010ffff8a82051208c0 380850 packetdril 192.168.204.46 8080 192.0.2.1 44937 ESTABLISHED -> FIN_WAIT1 0.021ffff8a82051208c0 380850 packetdril 192.168.204.46 8080 192.0.2.1 44937 FIN_WAIT1 -> CLOSING 0.070ffff8a82051208c0 380850 packetdril 192.168.204.46 8080 192.0.2.1 44937 CLOSING -> CLOSE 10.085
因为 TW 状态有一个 2MSL 也就是 60 秒的超时时间,因此同样的脚本可以用 ss 进一步检查如下,可以看到确实有 TW 状态的存在。
# ss -anto | grep 8080LISTEN 0 1 192.168.195.67:8080 0.0.0.0:*TIME-WAIT 0 0 192.168.195.67:8080 192.0.2.1:49321 timer:(timewait,57sec,0)## ss -anto | grep 8080LISTEN 0 1 192.168.195.67:8080 0.0.0.0:*TIME-WAIT 0 0 192.168.195.67:8080 192.0.2.1:49321 timer:(timewait,36sec,0)#
通过 telnet ip 端口的方式,仍然使用 BCC tcpstate ,再简单验证了正常 TCP 四次挥手的一个状态变化,如下,ESTABLISHED -> FIN_WAIT_1 -> FIN_WAIT_2 -> CLOSED,相较于标准 TCP 四次挥手的状态变化 ESTABLISHED -> FIN_WAIT_1 -> FIN_WAIT_2 -> TIME_WAIT -> CLOSED,仍然是少了一个 TW 状态的变化。
sudo ./tcpstates.py -L 22SKADDR C-PID C-COMM LADDR LPORT RADDR RPORT OLDSTATE -> NEWSTATE MSffff99674acd4ec0 13 ksoftirqd/ 0.0.0.0 22 0.0.0.0 0 LISTEN -> SYN_RECV 0.000ffff99674acd4ec0 13 ksoftirqd/ 10.1.1.2 22 10.1.1.1 46722 SYN_RECV -> ESTABLISHED 0.010ffff99674acd4ec0 2067 sshd 10.1.1.2 22 10.1.1.1 46722 ESTABLISHED -> FIN_WAIT1 2175.966ffff99674acd4ec0 0 swapper/0 10.1.1.2 22 10.1.1.1 46722 FIN_WAIT1 -> FIN_WAIT2 40.115ffff99674acd4ec0 0 swapper/0 10.1.1.2 22 10.1.1.1 46722 FIN_WAIT2 -> CLOSE 0.069
实验总结
通过上述实验测试结果,TCP 挥手之连接同时关闭的情况,虽然 BCC tcpstate 未完整捕获包括 TW 状态的变化(限于能力,暂时未深入分析),但也仅仅是一个工具的问题,TCP 状态确实为 ESTABLISHED -> FIN_WAIT_1 -> CLOSING -> TIME_WAIT -> CLOSED 的变化过程,packetdrill 脚本测试目的达到。😎
往期推荐
推荐站内搜索:最好用的开发软件、免费开源系统、渗透测试工具云盘下载、最新渗透测试资料、最新黑客工具下载……




还没有评论,来说两句吧...