多角度思考是为了拓宽认知边界
基于 packetdrill TCP 三次握手脚本,测试 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
众所周知,客户端如果向一个未打开的服务端口进行连接请求,正常会收到 RST 中止连接请求。
譬如尝试发起一次连接至本地不存在的一个服务端口 81,可以看到连接直接被拒。
# telnet 127.0.0.181
Trying 127.0.0.1...
telnet: Unable toconnectto remote host: Connection refused
#
# tcpdump -i any-nn port 81
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
18:32:21.032663 lo In IP 127.0.0.1.53338>127.0.0.1.81: Flags [S], seq 1286378737, win 65535, options [mss 65495,sackOK,TS val 46298351 ecr 0,nop,wscale 8], length 0
18:32:21.032676 lo In IP 127.0.0.1.81>127.0.0.1.53338: Flags [R.], seq 0, ack 1286378738, win 0, length 0
#
# ./tcpstates.py -D 81
SKADDR C-PID C-COMM LADDR LPORT RADDR RPORT OLDSTATE -> NEWSTATE MS
ffff8a8205120000 444090 telnet 127.0.0.10127.0.0.181CLOSE-> SYN_SENT 0.000
ffff8a8205120000 444090 telnet 127.0.0.153338127.0.0.181 SYN_SENT ->CLOSE0.214
而以数据包的角度来看,发出 SYN 后直接收到了 RST/ACK 数据包,其中RST 数据包中的 ACK Num 为 1286378738 ,也就是对应 SYN 中 Seq Num 1286378737 + 1。
需要注意的是,一个 RST 数据包如果要被客户端所接收(也就是中止连接请求,不会再继续进行 SYN 重传),需要保证 ACK 标志位置 1 且 ACK Num 落在有效的窗口范围内,否则会认为是一个无效 RST 数据包,这样的情况客户端并不会接收,而会继续进行 SYN 重传。
实验测试
首先模拟一次正常的 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 < R. 0:0(0) ack 1 win 10000 <mss 1000>
+0 `sleep 100`
#
执行脚本,并观察 tcpdump 抓包结果,预期运行正常。
# packetdrill tcp_rst_001.pkt
^Ctcp_rst_001.pkt:9: error executing `sleep 100` command: got signal 2 (Interrupt)
#
由于收到RST数据包,连接请求直接终止,因此也手动中止了脚本。
#
# 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
18:46:56.231681 tun0 Out IP 192.168.133.15.45818>192.0.2.1.8080: Flags [S], seq 426701422, win 65535, options [mss 1460,sackOK,TS val 3262398999 ecr 0,nop,wscale 8], length 0
18:46:56.241775 tun0 In IP 192.0.2.1.8080>192.168.133.15.45818: Flags [R.], seq 0, ack 426701423, win 10000, options [mss 1000], length 0
#
1. 第一个数据包为模拟客户端发起连接所发送的 SYN 数据包。
18:46:56.231681 tun0 Out IP 192.168.133.15.45818>192.0.2.1.8080: Flags [S], seq 426701422, win 65535, options [mss 1460,sackOK,TS val 3262398999 ecr 0,nop,wscale 8], length 0
// 对应于脚本 +0> S 0:0(0) <...>,预期客户端协议栈发出 SYN 。
2. 第二个数据包为模拟注入的服务器所发送的 RST 数据包。
18:46:56.241775 tun0 In IP 192.0.2.1.8080>192.168.133.15.45818: Flags [R.], seq 0, ack 426701423, win 10000, options [mss 1000], length 0
// 对应于脚本 +0.01< R. 0:0(0) ack 1 win 10000<mss 1000>,模拟注入服务器端的 RST 数据包。
同时 TCP 状态变化如下,与真实环境测试结果一致。
# ./tcpstates.py -D 8080
SKADDR C-PID C-COMM LADDR LPORT RADDR RPORT OLDSTATE -> NEWSTATE MS
ffff8a820e3fc600 444166 packetdril 192.168.133.150192.0.2.18080CLOSE -> SYN_SENT 0.000
ffff8a820e3fc600 444166 packetdril 192.168.133.1545818192.0.2.18080 SYN_SENT -> CLOSE10.135
继续模拟测试,修改脚本如下,唯一的变化的就是 RST 数据包中的 ACK Num 随机了一个数值,这样使得客户端在收到 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 < R. 0:0(0) ack 38571495 win 10000 <mss 1000>
+0 `sleep 150`
#
执行脚本,并观察 tcpdump 抓包结果,可以看到客户端在收到 RST 数据包后,并没有中止连接请求,由于 SYN 没有得到响应,所以之后进行的是 SYN 超时重传,尝试 6 次后才结束连接。
以数据包分析原因来看,是客户端发出 SYN ,在收到服务器端响应的 RST/ACK后进行校验,因 ACK Num 没有落在有效的窗口范围内,所以丢弃了该 RST 数据包。
# packetdrill tcp_rst_002.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
20:08:40.136942 tun0 Out IP 192.168.203.91.43336>192.0.2.1.8080: Flags [S], seq 2179694398, win 65535, options [mss 1460,sackOK,TS val 147782763 ecr 0,nop,wscale 8], length 0
20:08:40.147115 tun0 In IP 192.0.2.1.8080>192.168.203.91.43336: Flags [R.], seq 0, ack 2218265893, win 10000, options [mss 1000], length 0
20:08:40.156601 tun0 Out IP 192.168.203.91.43336>192.0.2.1.8080: Flags [S], seq 2179694398, win 65535, options [mss 1460,sackOK,TS val 147782783 ecr 0,nop,wscale 8], length 0
20:08:42.180598 tun0 Out IP 192.168.203.91.43336>192.0.2.1.8080: Flags [S], seq 2179694398, win 65535, options [mss 1460,sackOK,TS val 147784807 ecr 0,nop,wscale 8], length 0
20:08:46.276616 tun0 Out IP 192.168.203.91.43336>192.0.2.1.8080: Flags [S], seq 2179694398, win 65535, options [mss 1460,sackOK,TS val 147788903 ecr 0,nop,wscale 8], length 0
20:08:54.468585 tun0 Out IP 192.168.203.91.43336>192.0.2.1.8080: Flags [S], seq 2179694398, win 65535, options [mss 1460,sackOK,TS val 147797095 ecr 0,nop,wscale 8], length 0
20:09:10.596606 tun0 Out IP 192.168.203.91.43336>192.0.2.1.8080: Flags [S], seq 2179694398, win 65535, options [mss 1460,sackOK,TS val 147813223 ecr 0,nop,wscale 8], length 0
20:09:42.852586 tun0 Out IP 192.168.203.91.43336>192.0.2.1.8080: Flags [S], seq 2179694398, win 65535, options [mss 1460,sackOK,TS val 147845479 ecr 0,nop,wscale 8], length 0
20:11:10.154914 tun0 In IP 192.0.2.1.8080>192.168.203.91.43336: Flags [R.], seq 0, ack 1, win 10000, length 0
#
TCP 状态变化以上述结果一致,只不过是因 SYN 超时重传的时间较长,因此最后放弃请求,从 SYN_SENT 变成 CLOSED 状态约 127 秒。
# ./tcpstates.py -D 8080
SKADDR C-PID C-COMM LADDR LPORT RADDR RPORT OLDSTATE -> NEWSTATE MS
ffff92592fade040 168185 packetdril 192.168.203.910192.0.2.18080CLOSE -> SYN_SENT 0.000
ffff92592fade040 0 swapper/0192.168.203.9143336192.0.2.18080 SYN_SENT -> CLOSE128251.675
#
最后再简单验证下,是否纯 RST 能生效中止连接,也就是 ACK 位置 0 。
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 < R 0:0(0) win 10000 <mss 1000>
+0 `sleep 150`
#
执行脚本,并观察 tcpdump 抓包结果,可以看到客户端在收到纯 RST 数据包后,也没有中止连接请求,仍然是 SYN 超时重传尝试 6 次后才结束连接,同样是因为纯 RST 经有效性验证,不满足条件后丢弃。
# packetdrill tcp_rst_003.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
20:16:27.524993 tun0 Out IP 192.168.39.233.57226>192.0.2.1.8080: Flags [S], seq 3557827583, win 65535, options [mss 1460,sackOK,TS val 3289295779 ecr 0,nop,wscale 8], length 0
20:16:27.535188 tun0 In IP 192.0.2.1.8080>192.168.39.233.57226: Flags [R], seq 0, win 10000, options [mss 1000], length 0
20:16:28.548645 tun0 Out IP 192.168.39.233.57226>192.0.2.1.8080: Flags [S], seq 3557827583, win 65535, options [mss 1460,sackOK,TS val 3289296803 ecr 0,nop,wscale 8], length 0
20:16:30.564632 tun0 Out IP 192.168.39.233.57226>192.0.2.1.8080: Flags [S], seq 3557827583, win 65535, options [mss 1460,sackOK,TS val 3289298819 ecr 0,nop,wscale 8], length 0
20:16:34.756638 tun0 Out IP 192.168.39.233.57226>192.0.2.1.8080: Flags [S], seq 3557827583, win 65535, options [mss 1460,sackOK,TS val 3289303011 ecr 0,nop,wscale 8], length 0
20:16:42.948645 tun0 Out IP 192.168.39.233.57226>192.0.2.1.8080: Flags [S], seq 3557827583, win 65535, options [mss 1460,sackOK,TS val 3289311203 ecr 0,nop,wscale 8], length 0
20:16:59.076642 tun0 Out IP 192.168.39.233.57226>192.0.2.1.8080: Flags [S], seq 3557827583, win 65535, options [mss 1460,sackOK,TS val 3289327331 ecr 0,nop,wscale 8], length 0
20:17:31.844617 tun0 Out IP 192.168.39.233.57226>192.0.2.1.8080: Flags [S], seq 3557827583, win 65535, options [mss 1460,sackOK,TS val 3289360099 ecr 0,nop,wscale 8], length 0
20:18:57.543656 ? In IP 192.0.2.1.8080>192.168.39.233.57226: Flags [R.], seq 0, ack 3557827584, win 0, length 0
#
实验总结
客户端 TCP 连接不存在的服务端口引起的 RST ,却不一定会中止连接请求,因为会校验 RST 数据包的有效性,避免可能简单的 RST 攻击。当然这也只是实验测试,不用过多探讨。
往期推荐
推荐站内搜索:最好用的开发软件、免费开源系统、渗透测试工具云盘下载、最新渗透测试资料、最新黑客工具下载……
还没有评论,来说两句吧...