如果一件事情你不能讲清楚,十有八九你还没有完全理解。
实验目的
基础脚本
# cat tcp_si_close_000.pkt
0 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.pkt
0 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 8080
tcpdump: data link type LINUX_SLL2
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on any, link-type LINUX_SLL2 (Linux cooked v2), snapshot length 262144 bytes
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
15: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 0
15:48:39.929804 tun0 In IP 192.0.2.1.36845 > 192.168.135.169.8080: Flags [.], ack 1, win 10000, length 0
15: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 0
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
15:48:39.929962 tun0 Out IP 192.168.135.169.8080 > 192.0.2.1.36845: Flags [.], ack 2, win 65535, length 0
15:48:39.940026 ? In IP 192.0.2.1.36845 > 192.168.135.169.8080: Flags [.], ack 2, win 10000, length 0
15: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/ACK
15: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 8080
SKADDR C-PID C-COMM LADDR LPORT RADDR RPORT OLDSTATE -> NEWSTATE MS
ffff8a820397abc0 362994 packetdril 192.168.124.5 8080 0.0.0.0 0 CLOSE -> LISTEN 0.000
ffff8a820397f1c0 362994 packetdril 192.168.124.5 8080 0.0.0.0 0 LISTEN -> SYN_RECV 0.000
ffff8a820397f1c0 362994 packetdril 192.168.124.5 8080 192.0.2.1 39231 SYN_RECV -> ESTABLISHED 0.009
ffff8a820397f1c0 362994 packetdril 192.168.124.5 8080 192.0.2.1 39231 ESTABLISHED -> FIN_WAIT1 0.019
ffff8a820397f1c0 362994 packetdril 192.168.124.5 8080 192.0.2.1 39231 FIN_WAIT1 -> CLOSING 0.052
ffff8a820397f1c0 362994 packetdril 192.168.124.5 8080 192.0.2.1 39231 CLOSING -> CLOSE 10.052
ffff8a820397abc0 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.pkt
0 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 8080
SKADDR C-PID C-COMM LADDR LPORT RADDR RPORT OLDSTATE -> NEWSTATE MS
ffff8a8205123d40 380850 packetdril 192.168.204.46 8080 0.0.0.0 0 CLOSE -> LISTEN 0.000
ffff8a82051208c0 380850 packetdril 192.168.204.46 8080 0.0.0.0 0 LISTEN -> SYN_RECV 0.000
ffff8a82051208c0 380850 packetdril 192.168.204.46 8080 192.0.2.1 44937 SYN_RECV -> ESTABLISHED 0.010
ffff8a82051208c0 380850 packetdril 192.168.204.46 8080 192.0.2.1 44937 ESTABLISHED -> FIN_WAIT1 0.021
ffff8a82051208c0 380850 packetdril 192.168.204.46 8080 192.0.2.1 44937 FIN_WAIT1 -> CLOSING 0.070
ffff8a82051208c0 380850 packetdril 192.168.204.46 8080 192.0.2.1 44937 CLOSING -> CLOSE 10.085
因为 TW 状态有一个 2MSL 也就是 60 秒的超时时间,因此同样的脚本可以用 ss 进一步检查如下,可以看到确实有 TW 状态的存在。
# ss -anto | grep 8080
LISTEN 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 8080
LISTEN 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 22
SKADDR C-PID C-COMM LADDR LPORT RADDR RPORT OLDSTATE -> NEWSTATE MS
ffff99674acd4ec0 13 ksoftirqd/ 0.0.0.0 22 0.0.0.0 0 LISTEN -> SYN_RECV 0.000
ffff99674acd4ec0 13 ksoftirqd/ 10.1.1.2 22 10.1.1.1 46722 SYN_RECV -> ESTABLISHED 0.010
ffff99674acd4ec0 2067 sshd 10.1.1.2 22 10.1.1.1 46722 ESTABLISHED -> FIN_WAIT1 2175.966
ffff99674acd4ec0 0 swapper/0 10.1.1.2 22 10.1.1.1 46722 FIN_WAIT1 -> FIN_WAIT2 40.115
ffff99674acd4ec0 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 脚本测试目的达到。😎
往期推荐
推荐站内搜索:最好用的开发软件、免费开源系统、渗透测试工具云盘下载、最新渗透测试资料、最新黑客工具下载……
还没有评论,来说两句吧...