多角度思考是为了拓宽认知边界
基于 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 8080tcpdump: data link type LINUX_SLL2tcpdump: verbose output suppressed, use -v[v]... forfull protocol decodelistening onany, link-type LINUX_SLL2 (Linux cooked v2), snapshot length 262144 bytes22: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 022: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 022:22:08.289818 tun0 Out IP 192.168.255.235.44400>192.0.2.1.8080: Flags [.], ack 1, win 65535, length 022: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^C4 packets captured5 packets received byfilter0 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 8080SKADDR C-PID C-COMM LADDR LPORT RADDR RPORT OLDSTATE -> NEWSTATE MSffff8a8205124600 445305 packetdril 192.168.255.2350192.0.2.18080CLOSE -> SYN_SENT 0.000ffff8a8205124600 445305 packetdril 192.168.255.23544400192.0.2.18080 SYN_SENT -> ESTABLISHED 10.157ffff8a8205124600 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 8080tcpdump: data link type LINUX_SLL2tcpdump: verbose output suppressed, use -v[v]... forfull protocol decodelistening onany, link-type LINUX_SLL2 (Linux cooked v2), snapshot length 262144 bytes21: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 021: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 021:13:46.541815 tun0 Out IP 192.168.75.45.47408>192.0.2.1.8080: Flags [.], ack 1, win 65535, length 021: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 8080SKADDR C-PID C-COMM LADDR LPORT RADDR RPORT OLDSTATE -> NEWSTATE MSffff8a8205121180 449283 packetdril 192.168.75.450192.0.2.18080CLOSE-> SYN_SENT 0.000ffff8a8205121180 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 8080tcpdump: data link type LINUX_SLL2tcpdump: verbose output suppressed, use -v[v]... forfull protocol decodelistening onany, link-type LINUX_SLL2 (Linux cooked v2), snapshot length 262144 bytes23: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 023: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 023:16:59.941817 tun0 Out IP 192.168.35.141.54932>192.0.2.1.8080: Flags [.], ack 1, win 65535, length 023: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 023: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 8080SKADDR C-PID C-COMM LADDR LPORT RADDR RPORT OLDSTATE -> NEWSTATE MSffff8a8202db4ec0 452678 packetdril 192.168.35.1410192.0.2.18080CLOSE-> SYN_SENT 0.000ffff8a8202db4ec0 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 8080tcpdump: data link type LINUX_SLL2tcpdump: verbose output suppressed, use -v[v]... forfull protocol decodelistening onany, link-type LINUX_SLL2 (Linux cooked v2), snapshot length 262144 bytes22: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 022: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 022:41:14.549872 tun0 Out IP 192.168.75.220.44578>192.0.2.1.8080: Flags [.], ack 1, win 65535, length 022: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^C4 packets captured5 packets received byfilter0 packets dropped by kernel## ./tcpstates.py -D 8080SKADDR C-PID C-COMM LADDR LPORT RADDR RPORT OLDSTATE -> NEWSTATE MSffff8a820e3fb480 452309 packetdril 192.168.75.2200192.0.2.18080CLOSE-> SYN_SENT 0.000ffff8a820e3fb480 452309 packetdril 192.168.75.22044578192.0.2.18080 SYN_SENT -> ESTABLISHED 10.166ffff8a820e3fb480 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 数据包的有效性校验。
往期推荐
推荐站内搜索:最好用的开发软件、免费开源系统、渗透测试工具云盘下载、最新渗透测试资料、最新黑客工具下载……




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