TCP 利用 32比特的 Seq/Ack 序列号来确认每一个连接的可靠性. 此外, 这些32位的序列号还能保证服务器不会被会话劫持,伪造一个服务器发出的初始序列号(ISN) 是个难以实现的技术. 因为暴力破解的话需要穷举这个32比特的序列号,在一个千兆比特级别的网卡上也是很难实现的. 今天的文章将为大家带来是如何利用 TCP SYN Cookies (一项广泛使用的用来防止SYN-Flooding DOS攻击的防卫机制)来减少穷举ISN所需要花费的时间, SYN Cookies已经在Ubuntu 和 Debian各个发行版默认安装.
I. TCP基础知识复习
一个TCP连接是从三次握手开始的:
SYN: 客户端A发送一个SYN数据包给服务器,用来初始化一个连接. 这个SYN 包包含了客户端A所产生的一个初始序列号(ISN).
SYN-ACK: 服务器B应答了客户端A的这个连接请求. SYN-ACK包也包含了一个ISN,但是这个ISN是服务器所产生的. 除此之外,他还会应答上一个客户端发来的ISN,因此客户端A 能够从这个SYN-ACK包中确认到它上一次发的信息已经被服务器B所收获,而且通过ISN,还能验证是否由它所要求的服务器所发出,这样就验证了服务器的真伪。
ACK: 在三次握手的最后一个步骤,客户端A会发来一个ACK应答包,用来向服务器B确认客户端A已经收到服务器B 的ISN. 因此,服务器B就能确认客户端A实际上已经接收到了它发出的SYN-ACK 包,整个三次握手的过程如上图所示。
当三次握手完成之后TCP就建立了,这时,通信双方才可以真正开始传输数据给对方. 初始序列号不仅确定了对方可以接收到它所发出的数据包,而且也防止了IP欺骗,因为攻击者无法伪造初始序列号,也无法得知初始序列号是多少。
因为初始序列号是一个 32-bit 的值, 我们不可能盲目地穷举ISN来构造数据包. 如果我们需要发送3个数据包给服务器(一个 SYN 包用来初始化连接, 一个ACK 包用来结束三次握手和一个payload数据包), 我们平均需要发送3*2^32个数据包才可能成功做成一个会话欺骗. 如果我们以 300,000 个数据包一秒的速率来不断发包(能用一个千兆比特的高速网卡轻易实现), 发送完这些数据包需要12个小时.
TCP协议在设计上有一个广为人知的弱点,攻击者能够用数目巨大的SYN包来欺骗服务器. 服务器必须针对每一个SYN请求回送一个SYN-ACK 应答包,此时,服务器就必须保持一条半开放的连接,直到接收到一个对应的ACK应答包为止. 保持如此数目巨大的半开放连接,将会持续消耗掉服务器的资源,直到资源枯竭。服务器将会速度越来越慢,但是客户端的这种攻击却被视作是“合法”的,因为它遵守了TCP协议。这种“合法”的攻击叫做SYN Flooding(SYN泛洪攻击),它也是属于DOS攻击的一种。甚至攻击者只需一点点网络带宽就可以有效地攻陷一台普通的服务器。
II. 什么是SYN Cookie?
为了保护服务器不被SYN-Flooding攻击, Daniel J. Bernstein 在1996年发明了一项叫做TCP Syn Cookies 的技术.其核心技术是在TCP服务器收到TCP SYN包并返回TCP SYN+ACK包时,不分配一个专门的数据区,而是根据这个SYN包计算出一个cookie值。在收到TCP ACK包时,TCP服务器再根据那个cookie值检查这个TCP ACK包的合法性。如果合法,再分配专门的数据区进行处理未来的TCP连接。
服务器因此可以不用保持半开放的连接, Syn Cookies只会记住客户端发送的SYN 数据包的一些细节. 例如,当初始 SYN数据包包含了客户端发来的最大分段大小(MSS) 时, 服务器能够利用3个比特的标志位将MSS进行编码 (利用一张8位硬编码MSS值的表). 或是为了确认这个半开放的连接状态会在一段时间后过期,服务器会设置一串缓慢递增的字符计数器(一般一分钟递增一次). SYN数据包的其他选项一概不予理会。(最近 Linux 内核新增了对TCP中时间戳消息选项的支持 [1]). 当收到ACK 应答包时, Linux 内核会提取相应的SYN Cookie值来跟ACK包作对比。
Bernstein的这个经典的方法只重新设置了计数器和MSS值 [2] ,只利用了ISN的8 个比特,因此,让剩下的24个比特的信息可以被攻击者所伪造(非可以预测性)以用来做会话劫持。这导致SYN+ACK包能够在相当短的一段时间内被穷举,利用现在的网络硬件设备,这可以轻易做到。为了缓和此类攻击,, Bernstein建议: [3]:
#增加额外的cookie标志位:一个32比特长由私密函数生成的 a 32-bit 信息以防止攻击
这已经在最新的Lunix内核中被实现了,它确实保证了加密后的ISN信息比没有用加密函数的更加难以被穷举。但是,当我们更进一步地观察这个加密函数时,我们发现,攻击者也许不用穷举整个32比特的ISN。
下列的一系列函数给我们展示了基于 Linux Kernel 3.10.1 内核的SYN Cookies是如何工作的 (file net/ipv4/syncookies.c):
#define COOKIEBITS 24 /* Upper bits store count */
#define COOKI