TCP 收到一个 out of window 报文后会立即回复一个 ack,这是 RFC793 中 SEGMENT ARRIVES 段的要求。但这是为什么?难道不是默默丢弃才对吗?
对 oow 报文回复 ack,岂不是把正确的 ack 号回过去了吗,这样攻击者盲打一番就能拿到正确的 seq(至少 in-order) 实施数据劫持篡改。所以为 oow 报文回复 ack 目的是什么?
别扯 Keepalive,原始 TCP 规范没有 Keepalive。Keepalive 本身非标,它或许歪打正着利用了 TCP 的该原始漏洞:为 oow 报文回复 ack。
如果一开始 TCP 规范根本不对 oow 报文回复 ack,Keepalive 就必须想别的办法了,再也无法以 seq = max_seq - 1 作为序列号了。
无论如何,安全考虑,不与陌生人说话。
说完针对 receiver 的恶意 oow 报文,再看针对 sender 的恶意 in-window 报文。如果 sender 收到一个恶意 in-window 报文,该报文 ack > snd.una 会怎样?sender 会清理掉 ack - snd.una 之间的数据,如果这部分数据有丢失,丢失的数据将永远无法被重传。
如果 sender 只是单向发送,从不接收数据,通告一个 zero window 是高尚的。又或者 sender 只是接收少量数据,不 care 吞吐,就别通告太大的窗口。家里阳台越大,被烟花误窜的概率越大。
下面详细解释该 case。
oow(out-of-window) 不更新 receiver 的 una,这保护了 receiver 的 send queue 不会被 blind attacker 轻易篡改推进,而我们知道,una 被篡改推进会导致丢失的数据永远不会被重传。
但反过来呢?in window 但 out-of-order 岂不是可以随意更新 una?是的。但这会带来下面的问题:
不想同时作为 receiver 的 sender 如果打开一个过大的 rwin,就更容易被 blind attacker 构造的 in window 报文篡改推进 una,造成丢失的数据无法被重传,最终 receiver 的 hole 永远不会被填充,耗尽 rwin,跌入 zero window 万劫不复,一条连接就这样被打死。
如果不想接收数据,一定注意将 rwin 缩小甚至关闭。
TCP 是全双工连接,但 ack 方向可统一在反向 data 报头被捎带,两个方向之间的影响不容忽视。
无论如何,安全考虑,别把大门敞太大。
总之,对于 receiver,抵制随意的 out-of-window 报文,保护 rcv queue 数据,对于 sender,抵制随意的 in-window 报文,保护 rtx queue 数据。
再提一下 Keepalive,如果 TCP 可扩展,Keepalive 何必使用这种 max_seq - 1 如此怪异的 oow 报文来探测,单独一个 probe request flag 更自然,receiver 只需立即回复携带 probe response flag 但不设置 A flag 的 ack 报文。
标准并不一定一开始就正确,但标准在无伤大雅的情况下很难发生变化。
此类安全问题到底应不应该由 TCP 负责?在我看来数据篡改问题不是 TCP 的职责,应用程序发觉后直接报错断开即可,但 una 被篡改造成丢包不能重传就是 TCP 的问题了,它足以形成一种新的 DDoS,并且对于应用程序,在发现这种情况之前,浪费了很多时间。
此外,状态防火墙要是丢掉 oow,Keepalive 就用不了。Linux nf_conntrack 采用另一种宽松的方式判定 tcp_in_window 足以支持 Keepalive,挺好。
周中帮忙看了一个关于 Keepalive 的问题,其实我是一直觉得 Keepalive 本就是恰巧擦边生效的机制,若不是对 oow 报文回复一个不合理的 ack,Keepalive 根本就不会得到回应。而从 Linux 4.x 开始,对于 oow 报文的 ack 就是可回可不回的,取决于 oow 报文到达的 rate,这意味着对标准理解的松动,在我看来,与其不 care,不如 MUST NOT,不要对 oow 报文进行任何响应,连计数器都不更新!这篇短文给出相关的安全建议。
浙江温州皮鞋湿,下雨进水不会胖。
版权归原作者 dog250 所有, 如有侵权,请联系我们删除。