分类:
2007-05-01 19:28:55
虽然也算是把 TCP/IP Illustrated
三卷大致翻看过一遍,但现在回想起来读的真是很不认真,往往只是对自己感兴趣的地方仔细研究,麻烦的地方点到为止。这次因为那个号称 TCP
原理上存在的漏洞,又涨了一些经验值,原来发送 SYN 包也是可以导致重置 TCP 连接的。
具体漏洞描述可参考 Vulnerabilities in TCP,因为已经看了一些公司里老大写的内部文档,我就不好对其多做评论了,呵呵
其原理大概是通过监听、猜测或尝试,向被攻击连接的一方机器发送一个序列号在已建立连接 TCP 窗口中的 SYN 包,导致接收端以为连接无效而重置连接,并向另一端发送 RST 包切断连接。
这比传统的向两端发送多个 RST 包试图切断 TCP 连接的方式要优雅得多。因为 TCP 连接在 ESTABLISHED 状态时收到 RST 包后,直接清理队列并删除 TCB,连接进入 CLOSED 状态。
以下为引用:
second check the RST bit,
...
ESTABLISHED
FIN-WAIT-1
FIN-WAIT-2
CLOSE-WAIT
If the RST bit is set then, any outstanding RECEIVEs and SEND
should receive "reset" responses. All segment queues should be
flushed. Users should also receive an unsolicited general
"connection reset" signal. Enter the CLOSED state, delete the
TCB, and return.
如果切断不够及时(通常情况下如此),会导致接收到切断用 RST 包后,又在 CLOSED 状态收到数据(下图中第二行的 B)或对方发来的 RST 包(下图中第四行的 A),而这种异常情况可以被检测软件发现 :P
以下为引用:
C RST --> B (ESTABLISHED)
(ESTABLISHED) A DATA --> B (CLOSED)
(ESTABLISHED) A <-- RST C
(CLOSED) A <-- RST B (CLOSED)
RFC 793 中对 CLOSED 状态的处理流程描述如下:
以下为引用:
If the state is CLOSED (i.e., TCB does not exist) then
all data in the incoming segment is discarded. An incoming
segment containing a RST is discarded. An incoming segment not
containing a RST causes a RST to be sent in response. The
acknowledgment and sequence field values are selected to make the
reset sequence acceptable to the TCP that sent the offending
segment.
简单的说就是在 CLOSED 状态收到数据包则回应 RST 包(有的操作系统屏蔽了此功能);收到 RST 包直接丢弃。
而通过 SYN 包则较难检测并且至少在一端不存在此问题(可以选择) :D
对上述漏洞所处的 ESTABLISHED 状态处理 SYN 包的流程,RFC 793 中是如下描述的(rfc793.txt:4394):
以下为引用:
fourth, check the SYN bit,
SYN-RECEIVED
ESTABLISHED STATE
FIN-WAIT STATE-1
FIN-WAIT STATE-2
CLOSE-WAIT STATE
CLOSING STATE
LAST-ACK STATE
TIME-WAIT STATE
If the SYN is in the window it is an error, send a reset, any
outstanding RECEIVEs and SEND should receive "reset" responses,
all segment queues should be flushed, the user should also
receive an unsolicited general "connection reset" signal, enter
the CLOSED state, delete the TCB, and return.
If the SYN is not in the window this step would not be reached
and an ack would have been sent in the first step (sequence
number check).
如果在 ESTABLISHED 状态收到一个在窗口内的 SYN 包则是一个错误,需要发送 RST 包响应并且清空所有队列。而用户将收到一个主动的“连接已重置”的信号,并进入 CLOSED 状态,TCB 被删除后返回。
翻阅了一下FreeBSD 5/Linux 2.4.26 和NT 4的TCP部分代码,在FreeBSD中很容易就找到了这种情况的处理代码(sys etinet cp_input.c:1697):
以下为引用:
/*
* If a SYN is in the window, then this is an
* error and we send an RST and drop the connection.
*/
if (thflags & TH_SYN) {
tp = tcp_drop(tp, ECONNRESET);
rstreason = BANDLIM_UNLIMITED;
goto drop;
}
注释里面也说的很清楚,如果一个 SYN 包在 TCP 窗口中,这就是一个需要发送 RST 包并丢弃连接的错误。
在 Linux 下的处理 ESTABLISHED 状态的 tcp_rcv_established 函数(netipv4 cp_input.c:3589)中也对 SYN 包做了相同的处理:
以下为引用:
int tcp_rcv_established(...)
{
// ...
if(th->rst) {
tcp_reset(sk);
goto discard;
}
tcp_replace_ts_recent(tp, TCP_SKB_CB(skb)->seq);
if (th->syn && !before(TCP_SKB_CB(skb)->seq, tp->rcv_nxt)) {
TCP_INC_STATS_BH(TcpInErrs);
NET_INC_STATS_BH(TCPAbortOnSyn);
tcp_reset(sk);
return 1;
}
// ...
}
而 NT 下也有类似的处理代码(ntos di cpip cp cp.rcv.c:2829):
以下为引用:
// At this point, the segment is in our window and does not overlap
// on either end. If it's the next sequence number we expect, we can
// handle the data now. Otherwise we'll queue it for later. In either
// case we'll handle RST and ACK information right now.
// 按 RFC 793,首先检查 RST 位
// ...
// 接着检查 SYN 位
if (RcvInfo.tri_flags & TCP_FLAG_SYN)
{
// ...
SendRSTFromHeader(TCPH, Size, Src, Dest, OptInfo); // 发送 RST 包切断连接
// ...
return IP_SUCCESS;
}
可以看到 FreeBSD, Linux 和 NT 的上述代码都是完全符合 RFC 要求的。虽然这个漏洞目前很难被利用,但通过构造 SYN 包切断 TCP 连接的思路还是非常值得借鉴的 :P
而实际上通过阅读 RFC 和 FreeBSD/NT 相关源码,发现可能引起重置 TCP 连接的原因远远不止直接收到 RST 包或上述这种对
SYN 包的特殊处理。通过直接发送 RST 包只是主动重置的步骤,而还有很多情况可能引发被动重置。RFC 793 的 Reset
Generation (rfc793.txt:2308) 中有非常详细的描述。但可惜的是这些重置发生的状态,都不是最容易被利用的
ESTABLISHED 状态,呵呵。
btw: 匆匆忙忙写完,估计问题挺多,呵呵,欢迎指正 :D