在谈RST攻击前,必须先了解TCP:如何经过三次握手创建TCP链接、四次握手怎样把全双工的链接关闭掉、滑动窗口是怎么传输数据的、TCP的flag标志位里RST在哪些状况下出现。下面我会画一些尽可能简化的图来表达清楚上述几点,以后再了解下RST攻击是怎么回事。linux
TCP是在IP网络层之上的传输层协议,用于提供port到port面向链接的可靠的字节流传输。我来用土语解释下上面的几个关键字:golang
port到port:IP层只管数据包从一个IP到另外一个IP的传输,IP层之上的TCP层加上端口后,就是面向进程了,每一个port均可以对应到用户进程。windows
可靠:TCP会负责维护实际上子虚乌有的链接概念,包括收包后的确认包、丢包后的重发等来保证可靠性。因为带宽和不一样机器处理能力的不一样,TCP要能控制流量。缓存
字节流:TCP会把应用进程传来的字节流数据切割成许多个数据包,在网络上发送。IP包是会失去顺序或者产生重复的,TCP协议要能还原到字节流原本面目。服务器
从上面的TCP协议图能够看到,标志位共有六个,其中RST位就在TCP异常时出现,也是我这篇文章重点关注的地方。网络
下面我经过A向B创建TCP链接来讲明三次握手怎么完成的。架构
为了可以说清楚下面的RST攻击,须要结合上图说说:SYN标志位、序号、滑动窗口大小。学习
创建链接的请求中,标志位SYN都要置为1,在这种请求中会告知MSS段大小,就是本机但愿接收TCP包的最大大小。spa
发送的数据TCP包都有一个序号。它是这么得来的:最初发送SYN时,有一个初始序号,根据RFC的定义,各个操做系统的实现都是与系统时间相关的。以后,序号的值会不断的增长,好比原来的序号是100,若是这个TCP包的数据有10个字节,那么下次的TCP包序号会变成110。操作系统
滑动窗口用于加速传输,好比发了一个seq=100的包,理应收到这个包的确认ack=101后再继续发下一个包,但有了滑动窗口,只要新包的seq与没有获得确认的最小seq之差小于滑动窗口大小,就能够继续发。
滑动窗口毫无疑问是用来加速数据传输的。TCP要保证“可靠”,就须要对一个数据包进行ack确认表示接收端收到。有了滑动窗口,接收端就能够等收到许多包后只发一个ack包,确认以前已经收到过的多个数据包。有了滑动窗口,发送端在发送完一个数据包后不用等待它的ack,在滑动窗口大小内能够继续发送其余数据包。举个例子来看吧。
你们看上图,标志位为.表示全部的flag都为0。标志位P表示flag为PSH的TCP包,用于快速传输数据。
前三个包是三次握手,客户端表示本身的滑动窗口大小是65535(个人XP机器),服务器端表示滑动窗口是5840(屏幕宽了,没截出来)。从第四个包开始,客户端向服务器发送PSH包,数据长度是520字节,服务器发了ack确认包。注意此时win窗口大小发生了改变哈。以此类推。
倒数第2、三包,服务器在滑动窗口内连续向客户端发包,客户端发送的ack 124同时确认了以前的两个包。这就是滑动窗口的功能了。
若是谈到TCP攻击就须要注意,TCP的各类实现中,在滑动窗口以外的seq会被扔掉!下面会讲这个问题。
须要C/C++ Linux服务器架构师学习资料加群812855908(资料包括C/C++,Linux,golang技术,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK,ffmpeg等),免费分享
先画张简单的正常关闭链接状态变迁图。
FIN标志位也看到了,它用来表示正常关闭链接。图的左边是主动关闭链接方,右边是被动关闭链接方,用netstat命令能够看到标出的链接状态。
FIN是正常关闭,它会根据缓冲区的顺序来发的,就是说缓冲区FIN以前的包都发出去后再发FIN包,这与RST不一样。
RST表示复位,用来异常的关闭链接,在TCP的设计中它是不可或缺的。就像上面说的同样,发送RST包关闭链接时,没必要等缓冲区的包都发出去(不像上面的FIN包),直接就丢弃缓存区的包发送RST包。而接收端收到RST包后,也没必要发送ACK包来确认。
TCP处理程序会在本身认为的异常时刻发送RST包。例如,A向B发起链接,但B之上并未监听相应的端口,这时B操做系统上的TCP处理程序会发RST包。
又好比,AB正常创建链接了,正在通信时,A向B发送了FIN包要求关链接,B发送ACK后,网断了,A经过若干缘由放弃了这个链接(例如进程重启)。网通了后,B又开始发数据包,A收到后表示压力很大,不知道这野链接哪来的,就发了个RST包强制把链接关了,B收到后会出现connect reset by peer错误。
A和服务器B之间创建了TCP链接,此时C伪造了一个TCP包发给B,使B异常的断开了与A之间的TCP链接,就是RST攻击了。实际上从上面RST标志位的功能已经能够看出这种攻击如何达到效果了。
那么伪造什么样的TCP包能够达成目的呢?咱们至顶向下的看。
假定C假装成A发过去的包,这个包若是是RST包的话,毫无疑问,B将会丢弃与A的缓冲区上全部数据,强制关掉链接。
若是发过去的包是SYN包,那么,B会表示A已经发疯了(与OS的实现有关),正常链接时又来建新链接,B主动向A发个RST包,并在本身这端强制关掉链接。
这两种方式都可以达到复位攻击的效果。彷佛挺恐怖,然而关键是,如何能伪形成A发给B的包呢?这里有两个关键因素,源端口和序列号。
一个TCP链接都是四元组,由源IP、源端口、目标IP、目标端口惟一肯定一个链接。因此,若是C要伪造A发给B的包,要在上面提到的IP头和TCP头,把源IP、源端口、目标IP、目标端口都填对。这里B做为服务器,IP和端口是公开的,A是咱们要下手的目标,IP固然知道,但A的源端口就不清楚了,由于这多是A随机生成的。固然,若是可以对常见的OS如windows和linux找出生成source port规律的话,仍是能够搞定的。
序列号问题是与滑动窗口对应的,伪造的TCP包里须要填序列号,若是序列号的值不在A以前向B发送时B的滑动窗口内,B是会主动丢弃的。因此咱们要找到能落到当时的AB间滑动窗口的序列号。这个能够暴力解决,由于一个sequence长度是32位,取值范围0-4294967296,若是窗口大小像上图中我抓到的windows下的65535的话,只须要相除,就知道最多只须要发65537(4294967296/65535=65537)个包就能有一个序列号落到滑动窗口内。RST包是很小的,IP头+TCP头也才40字节,算算咱们的带宽就知道这实在只须要几秒钟就能搞定。
那么,序列号不是问题,源端口会麻烦点,若是各个操做系统不能彻底随机的生成源端口,或者黑客们能经过其余方式获取到source port,RST攻击易如反掌,后果很严重。