Done is better than perfect
实验目的
通过 packetdrill 测试在 TCP Time-Wait 状态下收到 RST 数据包会发生什么现象,本次构造模拟的是服务器端,主要为 Time-Wait 状态下收到新 SYN 请求下触发 ACK 进而产生 RST 的场景。
基础脚本
基础脚本为 TCP 四次挥手,构造模拟的是服务器端。
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 < . 1:1(0) ack 2 win 10000
+0.01 < F. 1:1(0) ack 2 win 10000
+0 > . 2:2(0) ack 2 <...>
实验测试
无时间戳场景
首先进行没有时间戳的场景,在之前的实验中,在 TW 状态下收到 SYN,如果新 SYN 的序列号比服务器端期望的下一个收到的序列号要小,服务器会响应一个 ACK(与之前连接第四次挥手 ACK 一样),之后客户端收到后发现并不是自己期望的 ACK num,就会回 RST 报文给服务器端。
修改脚本如下,增加服务器端响应 ACK 以及模拟注入客户端的 RST 数据包。
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 < . 1:1(0) ack 2 win 10000
+0.01 < F. 1:1(0) ack 2 win 10000
+0 > . 2:2(0) ack 2
+10 < S 1:1(0) win 10000 <mss 1460>
+0 > . 2:2(0) ack 2
+0.01 < R 2:2(0) win 0
+0 `sleep 100`
#
执行脚本,并观察 tcpdump 抓包结果,预期运行正常。
# packetdrill tcp_time_wait_009.pkt
#
# tcpdump -i any-nn port 8080
tcpdump: data link type LINUX_SLL2
tcpdump: verbose output suppressed, use -v[v]... forfull protocol decode
listening onany, link-type LINUX_SLL2 (Linux cooked v2), snapshot length 262144 bytes
10:01:52.247781 tun0 In IP 192.0.2.1.36323>192.168.110.31.8080: Flags [S], seq 0, win 10000, options [mss 1460], length 0
10:01:52.247815 tun0 Out IP 192.168.110.31.8080>192.0.2.1.36323: Flags [S.], seq 1365096224, ack 1, win 64240, options [mss 1460], length 0
10:01:52.257899 tun0 In IP 192.0.2.1.36323>192.168.110.31.8080: Flags [.], ack 1, win 10000, length 0
10:01:52.257946 tun0 Out IP 192.168.110.31.8080>192.0.2.1.36323: Flags [F.], seq 1, ack 1, win 64240, length 0
10:01:52.257992 tun0 In IP 192.0.2.1.36323>192.168.110.31.8080: Flags [.], ack 2, win 10000, length 0
10:01:52.268007 tun0 In IP 192.0.2.1.36323>192.168.110.31.8080: Flags [F.], seq 1, ack 2, win 10000, length 0
10:01:52.268025 tun0 Out IP 192.168.110.31.8080>192.0.2.1.36323: Flags [.], ack 2, win 64240, length 0
10:02:02.268113 tun0 In IP 192.0.2.1.36323>192.168.110.31.8080: Flags [S], seq 1, win 10000, options [mss 1460], length 0
10:02:02.268138 tun0 Out IP 192.168.110.31.8080>192.0.2.1.36323: Flags [.], ack 2, win 64240, length 0
10:02:02.278239 tun0 In IP 192.0.2.1.36323>192.168.110.31.8080: Flags [R], seq 2, win 0, length 0
^C
10 packets captured
11 packets received byfilter
0 packets dropped by kernel
#
通过 ss 检查连接状态时,发现原来处于 TW 状态的连接在收到 RST 数据包后,提前结束了 TW 状态并释放了连接。
# ss -anto | grep 8080
LISTEN 01192.168.110.31:80800.0.0.0:*
TIME-WAIT 00192.168.110.31:8080192.0.2.1:36323 timer:(timewait,57sec,0)
# ss -anto | grep 8080
LISTEN 01192.168.110.31:80800.0.0.0:*
TIME-WAIT 00192.168.110.31:8080192.0.2.1:36323 timer:(timewait,53sec,0)
# ss -anto | grep 8080
LISTEN 01192.168.110.31:80800.0.0.0:*
#
释放连接的动作,取决于以下 Linux 内核参数 net.ipv4.tcp_rfc1337
,默认情况为 0,即收到了 RST 后会提前结束 TIME_WAIT 状态,释放连接。
# sysctl -a|grep 1337
net.ipv4.tcp_rfc1337 = 0
#
如果修改 net.ipv4.tcp_rfc1337
值为 1 后,再次执行脚本观察连接状态,可以发现原来的连接仍然处于 TW 状态,说明在 TW 状态下的服务器端直接丢弃了 RST 数据包。
# sysctl -q net.ipv4.tcp_rfc1337=1
#
# packetdrill tcp_time_wait_009.pkt
#
# tcpdump -i any-nn port 8080
tcpdump: data link type LINUX_SLL2
tcpdump: verbose output suppressed, use -v[v]... forfull protocol decode
listening onany, link-type LINUX_SLL2 (Linux cooked v2), snapshot length 262144 bytes
10:08:41.327799 tun0 In IP 192.0.2.1.45473>192.168.222.161.8080: Flags [S], seq 0, win 10000, options [mss 1460], length 0
10:08:41.327836 tun0 Out IP 192.168.222.161.8080>192.0.2.1.45473: Flags [S.], seq 1064271788, ack 1, win 64240, options [mss 1460], length 0
10:08:41.337927 tun0 In IP 192.0.2.1.45473>192.168.222.161.8080: Flags [.], ack 1, win 10000, length 0
10:08:41.337986 tun0 Out IP 192.168.222.161.8080>192.0.2.1.45473: Flags [F.], seq 1, ack 1, win 64240, length 0
10:08:41.338039 tun0 In IP 192.0.2.1.45473>192.168.222.161.8080: Flags [.], ack 2, win 10000, length 0
10:08:41.348055 tun0 In IP 192.0.2.1.45473>192.168.222.161.8080: Flags [F.], seq 1, ack 2, win 10000, length 0
10:08:41.348081 tun0 Out IP 192.168.222.161.8080>192.0.2.1.45473: Flags [.], ack 2, win 64240, length 0
10:08:51.348184 tun0 In IP 192.0.2.1.45473>192.168.222.161.8080: Flags [S], seq 1, win 10000, options [mss 1460], length 0
10:08:51.348207 tun0 Out IP 192.168.222.161.8080>192.0.2.1.45473: Flags [.], ack 2, win 64240, length 0
10:08:51.358303 tun0 In IP 192.0.2.1.45473>192.168.222.161.8080: Flags [R], seq 2, win 0, length 0
^C
10 packets captured
11 packets received byfilter
0 packets dropped by kernel
#
# ss -anto | grep 8080
LISTEN 01192.168.222.161:80800.0.0.0:*
TIME-WAIT 00192.168.222.161:8080192.0.2.1:45473 timer:(timewait,57sec,0)
# ss -anto | grep 8080
LISTEN 01192.168.222.161:80800.0.0.0:*
TIME-WAIT 00192.168.222.161:8080192.0.2.1:45473 timer:(timewait,51sec,0)
# ss -anto | grep 8080
LISTEN 01192.168.222.161:80800.0.0.0:*
TIME-WAIT 00192.168.222.161:8080192.0.2.1:45473 timer:(timewait,48sec,0)
# ss -anto | grep 8080
LISTEN 01192.168.222.161:80800.0.0.0:*
TIME-WAIT 00192.168.222.161:8080192.0.2.1:45473 timer:(timewait,47sec,0)
#
有时间戳场景
再次进行有时间戳的场景,在之前的实验中,在 TW 状态下收到 SYN,有以下两种情况,服务器会响应一个 ACK(与之前连接第四次挥手 ACK 一样),之后客户端收到后发现并不是自己期望的 ACK num,就会回 RST 报文给服务器端。
- 新 SYN 的序列号比服务器端期望的下一个收到的序列号要大,并且新 SYN 的时间戳比服务器端最后收到的数据包的时间戳要小;
- 新 SYN 的序列号比服务器端期望的下一个收到的序列号要小,并且新 SYN 的时间戳比服务器端最后收到的数据包的时间戳要小。
因只是验证在收到 RST 情况下的处理,仅以第一个场景为例,修改脚本如下,增加服务器端响应 ACK 以及模拟注入客户端的 RST 数据包。
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,sackOK,TS val 100 ecr 0,nop,wscale 7>
+0 > S. 0:0(0) ack 1 <mss 1460,sackOK,TS val 105 ecr 100,nop,wscale 7>
+0.01 < . 1:1(0) ack 1 win 10000 <nop,nop,TS val 110 ecr 105>
+0 accept(3, ..., ...) = 4
+0 close(4) = 0
+0 > F. 1:1(0) ack 1 <nop,nop,TS val 115 ecr 110>
+0 < . 1:1(0) ack 2 win 10000 <nop,nop,TS val 120 ecr 115>
+0.01 < F. 1:1(0) ack 2 win 10000 <nop,nop,TS val 130 ecr 115>
+0 > . 2:2(0) ack 2 <nop,nop,TS val 135 ecr 130>
+10.015 < S 3:3(0) win 10000 <mss 1460,sackOK,TS val 100 ecr 0,nop,wscale 7>
+0 > . 2:2(0) ack 2 <nop,nop,TS val 10150 ecr 100>
+0.01 < R 2:2(0) win 0 <nop,nop,TS val 110 ecr 10150>
+0 `sleep 100`
#
执行脚本,并观察 tcpdump 抓包结果,预期运行正常。
# packetdrill tcp_time_wait_010.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
10:24:23.627778 tun0 In IP 192.0.2.1.36899 > 192.168.24.102.8080: Flags [S], seq 0, win 10000, options [mss 1460,sackOK,TS val100 ecr 0,nop,wscale 7], length 0
10:24:23.627810 tun0 Out IP 192.168.24.102.8080 > 192.0.2.1.36899: Flags [S.], seq 3963239093, ack 1, win 65160, options [mss 1460,sackOK,TS val2287356085 ecr 100,nop,wscale 7], length 0
10:24:23.637893 tun0 In IP 192.0.2.1.36899 > 192.168.24.102.8080: Flags [.], ack 1, win 10000, options [nop,nop,TS val110 ecr 2287356085], length 0
10:24:23.637948 tun0 Out IP 192.168.24.102.8080 > 192.0.2.1.36899: Flags [F.], seq 1, ack 1, win 510, options [nop,nop,TS val2287356095 ecr 110], length 0
10:24:23.637999 tun0 In IP 192.0.2.1.36899 > 192.168.24.102.8080: Flags [.], ack 2, win 10000, options [nop,nop,TS val120 ecr 2287356095], length 0
10:24:23.648014 tun0 In IP 192.0.2.1.36899 > 192.168.24.102.8080: Flags [F.], seq 1, ack 2, win 10000, options [nop,nop,TS val130 ecr 2287356095], length 0
10:24:23.648029 tun0 Out IP 192.168.24.102.8080 > 192.0.2.1.36899: Flags [.], ack 2, win 510, options [nop,nop,TS val2287356105 ecr 130], length 0
10:24:33.663136 tun0 In IP 192.0.2.1.36899 > 192.168.24.102.8080: Flags [S], seq 3, win 10000, options [mss 1460,sackOK,TS val100 ecr 0,nop,wscale 7], length 0
10:24:33.663166 tun0 Out IP 192.168.24.102.8080 > 192.0.2.1.36899: Flags [.], ack 2, win 510, options [nop,nop,TS val2287366120 ecr 130], length 0
10:24:33.673285 tun0 In IP 192.0.2.1.36899 > 192.168.24.102.8080: Flags [R], seq 2, win 0, options [nop,nop,TS val110 ecr 10150], length 0
^C
10 packets captured
11 packets received by filter
0 packets dropped by kernel
#
通过 ss 检查连接状态时,发现原来处于 TW 状态的连接在收到 RST 数据包后,仍然处于 TW 状态,说明在 TW 状态下的服务器端直接丢弃了 RST 数据包。
# ss -anto | grep 8080
LISTEN 01192.168.24.102:80800.0.0.0:*
TIME-WAIT 00192.168.24.102:8080192.0.2.1:36899 timer:(timewait,55sec,0)
# ss -anto | grep 8080
LISTEN 01192.168.24.102:80800.0.0.0:*
TIME-WAIT 00192.168.24.102:8080192.0.2.1:36899 timer:(timewait,50sec,0)
# ss -anto | grep 8080
LISTEN 01192.168.24.102:80800.0.0.0:*
TIME-WAIT 00192.168.24.102:8080192.0.2.1:36899 timer:(timewait,58sec,0)
# ss -anto | grep 8080
LISTEN 01192.168.24.102:80800.0.0.0:*
TIME-WAIT 00192.168.24.102:8080192.0.2.1:36899 timer:(timewait,54sec,0)
#
实验总结
以上的场景均是生产上可能碰到的案例,基于收到新 SYN 请求下产生,可能有以下几种情况:
无时间戳的场景,新 SYN 的序列号比服务器端期望的下一个收到的序列号要小,服务器会响应一个 ACK(与之前连接第四次挥手 ACK 一样),之后客户端收到后发现并不是自己期望的 ACK num,就会回 RST 报文给服务器端。
net.ipv4.tcp_rfc1337 为 0,服务器端收到了 RST 后会提前结束 TIME_WAIT 状态,释放连接; net.ipv4.tcp_rfc1337 为 1,服务器端直接丢弃 RST 数据包。
有时间戳的场景,新 SYN 的时间戳比服务器端最后收到的数据包的时间戳要小,服务器会响应一个 ACK(与之前连接第四次挥手 ACK 一样),之后客户端收到后发现并不是自己期望的 ACK num,就会回 RST 报文给服务器端。
服务器端直接丢弃 RST 数据包。
往期推荐
推荐站内搜索:最好用的开发软件、免费开源系统、渗透测试工具云盘下载、最新渗透测试资料、最新黑客工具下载……
还没有评论,来说两句吧...