多角度思考是为了拓宽认知边界
基于 packetdrill TCP 三次握手脚本,测试应用主动 RST 连接现象,此次构造模拟的是客户端场景。
基础脚本
0 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
+0 fcntl(3, F_SETFL, O_RDWR|O_NONBLOCK) = 0
+0 setsockopt(3, SOL_TCP, TCP_NODELAY, [1], 4) = 0
+0 connect(3, ..., ...) = -1 EINPROGRESS (Operation now in progress)
+0 > S 0:0(0) <...>
+0.01 < S. 0:0(0) ack 1 win 10000 <mss 1000>
+0 > . 1:1(0) ack 1
#
TCP RST
应用可以直接主动 Reset 一个 TCP 连接,并且不需要得到确认,这种情况下的连接中断过程称为 abortive release,而正常 TCP 四次挥手通过 FIN 终止连接的过程称为 orderly release 。
也就是说 orderly release 是一种优雅的四次挥手过程,通过 FIN 和 ACK 包的交换确保双方都完成数据传输并安全关闭连接;而 abortive release 则是通过发送 RST 包来强制立即终止连接的方式,它会丢弃所有未发送的数据并跳过正常的关闭流程,通常用于处理错误情况或需要紧急断开连接的场景。
实验测试
模拟 TCP 正常三次握手连接后,服务器端直接主动 RST ,修改脚本如下。
0 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
+0 fcntl(3, F_SETFL, O_RDWR|O_NONBLOCK) = 0
+0 setsockopt(3, SOL_TCP, TCP_NODELAY, [1], 4) = 0
+0 connect(3, ..., ...) = -1 EINPROGRESS (Operation now in progress)
+0 > S 0:0(0) <...>
+0.01 < S. 0:0(0) ack 1 win 10000 <mss 1000>
+0 > . 1:1(0) ack 1
+0.01 < R. 1:1(0) ack 1 win 10000
+0 `sleep 100`
#
执行脚本,并观察 tcpdump 抓包结果,预期运行正常,客户端在收到 RST 后中止连接,且不会响应 ACK。
# packetdrill tcp_rst_004.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
22:22:08.279667 tun0 Out IP 192.168.255.235.44400>192.0.2.1.8080: Flags [S], seq 1696918032, win 65535, options [mss 1460,sackOK,TS val 475291566 ecr 0,nop,wscale 8], length 0
22:22:08.289787 tun0 In IP 192.0.2.1.8080>192.168.255.235.44400: Flags [S.], seq 0, ack 1696918033, win 10000, options [mss 1000], length 0
22:22:08.289818 tun0 Out IP 192.168.255.235.44400>192.0.2.1.8080: Flags [.], ack 1, win 65535, length 0
22:22:08.299884 tun0 In IP 192.0.2.1.8080>192.168.255.235.44400: Flags [R.], seq 1, ack 1, win 10000, length 0
^C
4 packets captured
5 packets received byfilter
0 packets dropped by kernel
#
1. 前三个数据包为 TCP 三次握手数据包,略过。
2. 第四个数据包为模拟注入的服务器所发送的 RST 数据包。
22:22:08.299884 tun0 In IP 192.0.2.1.8080>192.168.255.235.44400: Flags [R.], seq 1, ack 1, win 10000, length 0
// 对应于脚本 +0.01< R. 1:1(0) ack 1 win 10000,模拟注入服务器端的 RST 数据包。
同时 TCP 状态变化如下,CLOSED -> SYN_SENT -> ESTABLISHED -> CLOSED,ESTABLISHED 状态下收到 RST 后直接变为 CLOSED 状态。
# ./tcpstates.py -D 8080
SKADDR C-PID C-COMM LADDR LPORT RADDR RPORT OLDSTATE -> NEWSTATE MS
ffff8a8205124600 445305 packetdril 192.168.255.2350192.0.2.18080CLOSE -> SYN_SENT 0.000
ffff8a8205124600 445305 packetdril 192.168.255.23544400192.0.2.18080 SYN_SENT -> ESTABLISHED 10.157
ffff8a8205124600 445305 packetdril 192.168.255.23544400192.0.2.18080 ESTABLISHED -> CLOSE10.092
#
考虑到可模拟注入 RST 数据包的不同,以下计划分为四次测试,验证 RST 的有效性:
- 修改 RST 数据包的 Seq Num,落在有效窗口之外;
- 修改 RST 数据包的 Seq Num,落在有效窗口之内,但不是所期望的 Seq Num;
- 修改 RST 数据包的 Ack Num;
- 修改 RST 数据包的 ACK 置位为 0。
第一次修改 RST 数据包 Seq Num 落在有效的窗口范围之外,脚本如下。
0 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
+0 fcntl(3, F_SETFL, O_RDWR|O_NONBLOCK) = 0
+0 setsockopt(3, SOL_TCP, TCP_NODELAY, [1], 4) = 0
+0 connect(3, ..., ...) = -1 EINPROGRESS (Operation now in progress)
+0 > S 0:0(0) <...>
+0.01 < S. 0:0(0) ack 1 win 10000 <mss 1000>
+0 > . 1:1(0) ack 1
+0.01 < R. 1432556:1432556(0) ack 1 win 10000
+0 `sleep 100`
#
执行脚本,并观察 tcpdump 抓包结果,可以看到客户端在收到 RST 数据包后,没有任何响应,直接丢弃了该数据包,因为 RST 数据包的 Seq Num 落在有效窗口之外。
# packetdrill tcp_rst_005.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
21:13:46.531684 tun0 Out IP 192.168.75.45.47408>192.0.2.1.8080: Flags [S], seq 1148305542, win 65535, options [mss 1460,sackOK,TS val 609947092 ecr 0,nop,wscale 8], length 0
21:13:46.541773 tun0 In IP 192.0.2.1.8080>192.168.75.45.47408: Flags [S.], seq 0, ack 1148305543, win 10000, options [mss 1000], length 0
21:13:46.541815 tun0 Out IP 192.168.75.45.47408>192.0.2.1.8080: Flags [.], ack 1, win 65535, length 0
21:13:46.551907 tun0 In IP 192.0.2.1.8080>192.168.75.45.47408: Flags [R.], seq 1432556, ack 1, win 10000, length 0
#
# ./tcpstates.py -D 8080
SKADDR C-PID C-COMM LADDR LPORT RADDR RPORT OLDSTATE -> NEWSTATE MS
ffff8a8205121180 449283 packetdril 192.168.75.450192.0.2.18080CLOSE-> SYN_SENT 0.000
ffff8a8205121180 449283 packetdril 192.168.75.4547408192.0.2.18080 SYN_SENT -> ESTABLISHED 10.132
#
第二次修改 RST 数据包 Seq Num 落在有效的窗口范围之内,但不是期望的 Seq Num,脚本如下。
0 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
+0 fcntl(3, F_SETFL, O_RDWR|O_NONBLOCK) = 0
+0 setsockopt(3, SOL_TCP, TCP_NODELAY, [1], 4) = 0
+0 connect(3, ..., ...) = -1 EINPROGRESS (Operation now in progress)
+0 > S 0:0(0) <...>
+0.01 < S. 0:0(0) ack 1 win 10000 <mss 1000>
+0 > . 1:1(0) ack 1
+0.01 < R. 2:2(0) ack 1 win 10000
+0 `sleep 100`
#
执行脚本,并观察 tcpdump 抓包结果,可以看到客户端在收到 RST 数据包后,响应了 ACK(challenge_ack),因为 RST 数据包的 Seq Num 落在有效窗口之内,但不是期望的 Seq Num,并且没有中止连接。
# packetdrill tcp_rst_006.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
23:16:59.931671 tun0 Out IP 192.168.35.141.54932>192.0.2.1.8080: Flags [S], seq 3073198600, win 65535, options [mss 1460,sackOK,TS val 974672773 ecr 0,nop,wscale 8], length 0
23:16:59.941781 tun0 In IP 192.0.2.1.8080>192.168.35.141.54932: Flags [S.], seq 0, ack 3073198601, win 10000, options [mss 1000], length 0
23:16:59.941817 tun0 Out IP 192.168.35.141.54932>192.0.2.1.8080: Flags [.], ack 1, win 65535, length 0
23:16:59.951923 tun0 In IP 192.0.2.1.8080>192.168.35.141.54932: Flags [R.], seq 2, ack 1, win 10000, length 0
23:16:59.951941 tun0 Out IP 192.168.35.141.54932>192.0.2.1.8080: Flags [.], ack 1, win 65535, length 0
#
# ./tcpstates.py -D 8080
SKADDR C-PID C-COMM LADDR LPORT RADDR RPORT OLDSTATE -> NEWSTATE MS
ffff8a8202db4ec0 452678 packetdril 192.168.35.1410192.0.2.18080CLOSE-> SYN_SENT 0.000
ffff8a8202db4ec0 452678 packetdril 192.168.35.14154932192.0.2.18080 SYN_SENT -> ESTABLISHED 10.156
#
第三次修改 RST 数据包 Ack Num 落在有效的窗口范围之外,脚本如下。
0 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
+0 fcntl(3, F_SETFL, O_RDWR|O_NONBLOCK) = 0
+0 setsockopt(3, SOL_TCP, TCP_NODELAY, [1], 4) = 0
+0 connect(3, ..., ...) = -1 EINPROGRESS (Operation now in progress)
+0 > S 0:0(0) <...>
+0.01 < S. 0:0(0) ack 1 win 10000 <mss 1000>
+0 > . 1:1(0) ack 1
+0.01 < R. 1:1(0) ack 3432556789 win 10000
+0 `sleep 100`
#
执行脚本,并观察 tcpdump 抓包结果,可以看到客户端在收到 RST 数据包后确实中止了连接,这是因为 RST 数据包的 Ack Num 并不在有效性检测之内。
# packetdrill tcp_rst_007.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
22:41:14.539709 tun0 Out IP 192.168.75.220.44578>192.0.2.1.8080: Flags [S], seq 3187046630, win 65535, options [mss 1460,sackOK,TS val 3416042552 ecr 0,nop,wscale 8], length 0
22:41:14.549812 tun0 In IP 192.0.2.1.8080>192.168.75.220.44578: Flags [S.], seq 0, ack 3187046631, win 10000, options [mss 1000], length 0
22:41:14.549872 tun0 Out IP 192.168.75.220.44578>192.0.2.1.8080: Flags [.], ack 1, win 65535, length 0
22:41:14.559949 tun0 In IP 192.0.2.1.8080>192.168.75.220.44578: Flags [R.], seq 1, ack 3432556789, win 10000, length 0
^C
4 packets captured
5 packets received byfilter
0 packets dropped by kernel
#
# ./tcpstates.py -D 8080
SKADDR C-PID C-COMM LADDR LPORT RADDR RPORT OLDSTATE -> NEWSTATE MS
ffff8a820e3fb480 452309 packetdril 192.168.75.2200192.0.2.18080CLOSE-> SYN_SENT 0.000
ffff8a820e3fb480 452309 packetdril 192.168.75.22044578192.0.2.18080 SYN_SENT -> ESTABLISHED 10.166
ffff8a820e3fb480 452309 packetdril 192.168.75.22044578192.0.2.18080 ESTABLISHED ->CLOSE10.133
#
第四次修改 RST 数据包的 ACK 置位为 0,也就是不包含 ACK,脚本如下。
0 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
+0 fcntl(3, F_SETFL, O_RDWR|O_NONBLOCK) = 0
+0 setsockopt(3, SOL_TCP, TCP_NODELAY, [1], 4) = 0
+0 connect(3, ..., ...) = -1 EINPROGRESS (Operation now in progress)
+0 > S 0:0(0) <...>
+0.01 < S. 0:0(0) ack 1 win 10000 <mss 1000>
+0 > . 1:1(0) ack 1
+0.01 < R. 1:1(0) ack 1 win 10000
+0 `sleep 100`
#
0
执行脚本,并观察 tcpdump 抓包结果,可以看到客户端在收到 RST 数据包后也中止了连接,这是因为 RST 数据包的 Ack 标志位也不在有效性检测之内。
0 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
+0 fcntl(3, F_SETFL, O_RDWR|O_NONBLOCK) = 0
+0 setsockopt(3, SOL_TCP, TCP_NODELAY, [1], 4) = 0
+0 connect(3, ..., ...) = -1 EINPROGRESS (Operation now in progress)
+0 > S 0:0(0) <...>
+0.01 < S. 0:0(0) ack 1 win 10000 <mss 1000>
+0 > . 1:1(0) ack 1
+0.01 < R. 1:1(0) ack 1 win 10000
+0 `sleep 100`
#
1
实验总结
通过模拟服务器不同的 RST 数据包,使得客户端在收到 RST 时有可能会中止连接,是否中止连接还要取决于 RST 数据包的有效性校验。
往期推荐
推荐站内搜索:最好用的开发软件、免费开源系统、渗透测试工具云盘下载、最新渗透测试资料、最新黑客工具下载……
还没有评论,来说两句吧...