必须动用已有的知识去解释新知识
实验目的
通过 packetdrill 测试 TCP 基础之三次挥手,本次构造模拟的是服务器端场景。
基础脚本
基础脚本为 TCP 三次握手,构造模拟的是服务器端场景,相关脚本说明详见。
# cat tcp_3wh_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 三次挥手,实际上就是第二个 ACK 和第三个 FIN 数据包进行了合并。为什么第二个 ACK 会延迟发送,简单来说是在收到第一个 FIN 数据包后,接收端会进入 pingpong 模式,也就不满足 quickack 模式,综合判断下不会立马发送 ACK ,最终就会 Delayed ACK,和之后的第三个 FIN 数据包一并发送。
和 TCP 四次挥手一样,首先发起 FIN 的可以是客户端,也可以是服务器端,以下分别模拟相关两种场景。
客户端测试
首先模拟客户端先注入 FIN 数据包,观察服务器端协议栈响应。
# cat tcp_3wh_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.1 < F. 1:1(0) ack 1 win 10000
#
由于脚本最后的语句仅是注入 FIN ,所以脚本一执行完后就结束了,而 tcpdump 捕获数据包时未看到明显的服务器端响应,相关数据包说明如下。
# packetdrill cat tcp_3wh_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
22:50:50.514327 tun0 In IP 192.0.2.1.42457 > 192.168.29.218.8080: Flags [S], seq 0, win 10000, options [mss 1460], length 0
22:50:50.514355 tun0 Out IP 192.168.29.218.8080 > 192.0.2.1.42457: Flags [S.], seq 3091907294, ack 1, win 64240, options [mss 1460], length 0
22:50:50.524445 tun0 In IP 192.0.2.1.42457 > 192.168.29.218.8080: Flags [.], ack 1, win 10000, length 0
22:50:50.624530 tun0 In IP 192.0.2.1.42457 > 192.168.29.218.8080: Flags [F.], seq 1, ack 1, win 10000, length 0
22:50:50.624613 tun0 Out IP 192.168.29.218.8080 > 192.0.2.1.42457: Flags [F.], seq 1, ack 2, win 64239, length 0
22:50:50.624632 tun0 In IP 192.0.2.1.42457 > 192.168.29.218.8080: Flags [R.], seq 2, ack 1, win 10000, length 0
#
1. 前三个数据包为 TCP 三次握手数据包,略过。
2. 第四个数据包为模拟注入的客户端 FIN 数据包。
22:50:50.624530 tun0 In IP 192.0.2.1.42457 > 192.168.29.218.8080: Flags [F.], seq 1, ack 1, win 10000, length 0
// 对应于脚本 +0.1 < F. 1:1(0) ack 1 win 10000 ,与之前间隔 100ms 后 < 输入数据包,packetdrill 根据此行脚本各字段值构造的 FIN 数据包,注入到内核协议栈。
3. 最后两个数据包 FIN/ACK 和 RST/ACK
22:50:50.624613 tun0 Out IP 192.168.29.218.8080 > 192.0.2.1.42457: Flags [F.], seq 1, ack 2, win 64239, length 0
22:50:50.624632 tun0 In IP 192.0.2.1.42457 > 192.168.29.218.8080: Flags [R.], seq 2, ack 1, win 10000, length 0
// packetdrill 脚本之外产生的数据包,或者说预期之外的数据包。
// 根据个人判断,在 packetdrill 脚本执行完毕后,socket 被强行关闭,如果有accept()的情况下,内核会发送 FIN/ACK 结束连接,没有accept()的情况下,则不会发送 FIN/ACK.
// 最后一个为客户端所发送的 RST/ACK 结束连接,也像是 packetdrill 脚本默认构造,用于快速重置连接使用的。
// 因此这两个数据包一般与 packetdrill 构建的测试脚本无关,分析时可省略。
在以上脚本最后增加 sleep 语句,使得脚本执行完不要退出。
# cat tcp_3wh_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.1 < F. 1:1(0) ack 1 win 10000
+0 `sleep 100`
#
如下测试后,可观察到在客户端注入 FIN 数据包后,服务器端响应了 ACK 数据包。
注意,服务器端仅仅是响应了 ACK ,是因为这里并没有模拟调用 close(),所以就没有后续第三个 FIN 数据包的发送。
# packetdrill tcp_3wh_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
12:50:27.494334 tun0 In IP 192.0.2.1.34499 > 192.168.38.155.8080: Flags [S], seq 0, win 10000, options [mss 1460], length 0
12:50:27.494361 tun0 Out IP 192.168.38.155.8080 > 192.0.2.1.34499: Flags [S.], seq 346908529, ack 1, win 64240, options [mss 1460], length 0
12:50:27.504436 tun0 In IP 192.0.2.1.34499 > 192.168.38.155.8080: Flags [.], ack 1, win 10000, length 0
12:50:27.604513 tun0 In IP 192.0.2.1.34499 > 192.168.38.155.8080: Flags [F.], seq 1, ack 1, win 10000, length 0
12:50:27.610030 tun0 Out IP 192.168.38.155.8080 > 192.0.2.1.34499: Flags [.], ack 2, win 64239, length 0
#
通过 ss 查询 TCP 连接状态为 CLOSE-WAIT。
# ss -anto | grep 8080
CLOSE-WAIT 1 0 192.168.210.130:8080 192.0.2.1:60155
#
之后通过 CTRL+C 可终止脚本运行,如下:
# packetdrill tcp_3wh_001.pkt
^Ctcp_3wh_001.pkt:13: error executing `sleep 100` command: got signal 2 (Interrupt)
#
再次通过 ss 查询 TCP 连接状态已经变成 LAST-ACK。
# ss -anto | grep 8080
LAST-ACK 1 1 192.168.210.130:8080 192.0.2.1:60155 timer:(on,348ms,3)
#
抓包结果如下,仅出现一个服务器发送的 FIN 数据包,与上面的 TCP 连接 LAST-ACK 对应。
12:50:30.414159 ? Out IP 192.168.38.155.8080 > 192.0.2.1.34499: Flags [F.], seq 1, ack 2, win 64239, length 0
最后脚本增加服务器端 close 发送 FIN ,完成 TCP 三次挥手完整过程。
# cat tcp_3wh_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.1 < F. 1:1(0) ack 1 win 10000
+0 close(4) = 0
+0 > F. 1:1(0) ack 2
+0.01 < . 2:2(0) ack 2 win 10000
#
# packetdrill tcp_3wh_002.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
22:02:28.319674 tun0 In IP 192.0.2.1.46827 > 192.168.233.140.8080: Flags [S], seq 0, win 10000, options [mss 1460], length 0
22:02:28.319707 tun0 Out IP 192.168.233.140.8080 > 192.0.2.1.46827: Flags [S.], seq 3459722375, ack 1, win 65535, options [mss 1460], length 0
22:02:28.329813 tun0 In IP 192.0.2.1.46827 > 192.168.233.140.8080: Flags [.], ack 1, win 10000, length 0
22:02:28.429878 ? In IP 192.0.2.1.46827 > 192.168.233.140.8080: Flags [F.], seq 1, ack 1, win 10000, length 0
22:02:28.429915 ? Out IP 192.168.233.140.8080 > 192.0.2.1.46827: Flags [F.], seq 1, ack 2, win 65535, length 0
22:02:28.439993 ? In IP 192.0.2.1.46827 > 192.168.233.140.8080: Flags [.], ack 2, win 10000, length 0
22:02:28.440096 ? In IP 192.0.2.1.46827 > 192.168.233.140.8080: Flags [R.], seq 2, ack 2, win 10000, length 0
#
服务器端测试
通过 close ,服务器端先发送 FIN 数据包,之后注入客户端 FIN 数据包,最后完成服务器端 ACK 。
# cat tcp_3wh_003.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.01 < F. 1:1(0) ack 2 win 10000
+0 > . 2:2(0) ack 2 <..å.>
#
执行脚本,并通过 tcpdump 捕获数据包,可观察到 TCP 三次挥手完整过程。
# packetdrill tcp_3wh_003.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
22:05:45.899665 tun0 In IP 192.0.2.1.51661 > 192.168.18.58.8080: Flags [S], seq 0, win 10000, options [mss 1460], length 0
22:05:45.899693 tun0 Out IP 192.168.18.58.8080 > 192.0.2.1.51661: Flags [S.], seq 2001050617, ack 1, win 65535, options [mss 1460], length 0
22:05:45.909805 tun0 In IP 192.0.2.1.51661 > 192.168.18.58.8080: Flags [.], ack 1, win 10000, length 0
22:05:45.909913 tun0 Out IP 192.168.18.58.8080 > 192.0.2.1.51661: Flags [F.], seq 1, ack 1, win 65535, length 0
22:05:45.920001 tun0 In IP 192.0.2.1.51661 > 192.168.18.58.8080: Flags [F.], seq 1, ack 2, win 10000, length 0
22:05:45.920031 tun0 Out IP 192.168.18.58.8080 > 192.0.2.1.51661: Flags [.], ack 2, win 65535, length 0
22:05:45.920164 tun0 In IP 192.0.2.1.51661 > 192.168.18.58.8080: Flags [R.], seq 2, ack 2, win 10000, length 0
#
1. 前三个数据包为 TCP 三次握手数据包,略过。
2. 第四个数据包为服务器 close 所发出的 FIN 数据包。
22:05:45.909913 tun0 Out IP 192.168.18.58.8080 > 192.0.2.1.51661: Flags [F.], seq 1, ack 1, win 65535, length 0
// 对应于脚本 +0 > F. 1:1(0) ack 1 ,close 关闭连接,预期服务器协议栈发出 FIN 。
3. 第五个数据包为注入的客户端 FIN 数据包。
22:05:45.920001 tun0 In IP 192.0.2.1.51661 > 192.168.18.58.8080: Flags [F.], seq 1, ack 2, win 10000, length 0
// 对应于脚本 +0.01 < F. 1:1(0) ack 2 win 10000 ,为客户端关闭连接所发送的 FIN,此处模拟合并了第二个 ACK 。
4. 第六个数据包为服务器的 ACK 数据包。
22:05:45.920031 tun0 Out IP 192.168.18.58.8080 > 192.0.2.1.51661: Flags [.], ack 2, win 65535, length 0
// 对应于脚本 +0 > . 2:2(0) ack 2 <...>,预期服务器收到客户端的 FIN 所发送的 ACK 确认。
5. 最后一个数据包 RST/ACK
22:05:45.920164 tun0 In IP 192.0.2.1.51661 > 192.168.18.58.8080: Flags [R.], seq 2, ack 2, win 10000, length 0
// packetdrill 脚本之外产生的数据包,或者说预期之外的数据包。
// 客户端所发送的 RST/ACK 结束连接,如前所述是 packetdrill 脚本默认构造,用于快速重置连接使用的。
// 因此这个数据包一般与 packetdrill 构建的测试脚本无关,分析时可省略。
往期推荐
推荐站内搜索:最好用的开发软件、免费开源系统、渗透测试工具云盘下载、最新渗透测试资料、最新黑客工具下载……
还没有评论,来说两句吧...