一、TCP三次握手、四次分手原理
在kali下运行:tcpdump -nn -i eth0 port 80
然后另起一个窗口运行:curl www.baidu.com,抓取的整个过程如下(中文部分都是自己加的理解和注释):
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
-----------------------------------------三次握手----------------------------------------------------------------------
20:01:12.656037 IP 192.168.40.130.51840 > 14.215.177.39.80: Flags [S], seq 649061102, win 64240, options [mss 1460,sackOK,TS val 3289670858 ecr 0,nop,wscale 7], length 0 -->客户端主动给服务器打招呼,希望建立连接;同时带上seq,便于识别本次会话的顺序,避免错续和乱序;length=0,这个包没数据,只有头;
20:01:12.696893 IP 14.215.177.39.80 > 192.168.40.130.51840: Flags [S.], seq 1494708435, ack 649061103, win 64240, options [mss 1460], length 0 -->ack=649061103,是客户端上个包的seq+1,让客户端知道自己收到他的请求;
20:01:12.696960 IP 192.168.40.130.51840 > 14.215.177.39.80: Flags [.], ack 1, win 64240, length 0 -->建立连接完毕
-----------------------------------------开始传输数据--------------------------------------------------------------------
20:01:12.697280 IP 192.168.40.130.51840 > 14.215.177.39.80: Flags [P.], seq 1:78, ack 1, win 64240, length 77: HTTP: GET / HTTP/1.1 --> P是push,立即发送,不要等;77是http头; 这里发送http请求;
20:01:12.697509 IP 14.215.177.39.80 > 192.168.40.130.51840: Flags [.], ack 78, win 64240, length 0 --> 这个包没有任何数据,ack=78=上个包的seq+1,就是服务器让客户端知道自己已经收到数据请求了,即将发送数据,请准备好接受;
20:01:12.740915 IP 14.215.177.39.80 > 192.168.40.130.51840: Flags [P.], seq 1:2782, ack 78, win 64240, length 2781: HTTP: HTTP/1.1 200 OK -->seq:服务器从这里开始传输http内容,seq和length共同说明了数据长度
20:01:12.740936 IP 192.168.40.130.51840 > 14.215.177.39.80: Flags [.], ack 2782, win 62780, length 0 -->客户端回应收到服务端2782字节的内容,让服务器知道传输内容是完整的
----------------------------------------四次分手,双方释放资源------------------------------------------------------------------------
20:01:12.741227 IP 192.168.40.130.51840 > 14.215.177.39.80: Flags [F.], seq 78, ack 2782, win 62780, length 0 -->我要分手:seq=78,就是客户端上次发的http数据请求报头+1,表明这个是基于这次http数据的请求基础上的;ack:2782,表明服务器上个包已经收到;
20:01:12.741499 IP 14.215.177.39.80 > 192.168.40.130.51840: Flags [.], ack 79, win 64239, length 0 -->ack-79,收到了客户端上个包
20:01:12.781491 IP 14.215.177.39.80 > 192.168.40.130.51840: Flags [FP.], seq 2782, ack 79, win 64239, length 0 -->真的要分手?再确认一下:seq=2782,我上次发的http内容都收到了么?
20:01:12.781534 IP 192.168.40.130.51840 > 14.215.177.39.80: Flags [.], ack 2783, win 62780, length 0 -->是的,要分手,都把资源释放了呗;
整个过程很精妙,总结如下:
1、seq标明自己包的顺序,首次发包都是随机生成0~2^32内的数据;
2、如果后续自己每次发包都和自己上次的包有关系(也就是接着发, 或者说我方的数据流就从这里开始),那么这次包的seq都在上次发包的基础上+1,用于避免因为网络或其他原因导致的错序和乱序;
如果后续自己每次发包都和自己上次的包没关系,则不带上seq字段,仅通过ack响应对方上个包;
3、ack=对方上个包的sep+1,标明回复的是对方的哪个包;
4、所谓的可靠,就是依托序列号及确认号机制让TCP的传输过程中即使出现丢包也会重传
5、所谓的连接,本质上就是通信双方投入自己的资源,为接下来的收发数据做准备
二、基于TCP协议的反射DOS攻击
很多网络协议都有一个“致命”弱点:只发包通信,不会(可能也没法)验证对方身份,这就导致了网络中可能存在大量“虚假欺骗”的流量,比如最典型的当属ARP协议:对方的MAC对方说了算,自己完全没法验证;还有就是TCP协议,设计的初衷是为了保证数据输出可靠,说白了就是我发的数据对方100%能收到,也没有考虑数据包的“真伪”,就给DOS攻击留下了可乘之机;
1、基于TCP的反射型DOS攻击
如上图所示:
(1)小坏蛋本身的地址是10.1.0.2,但是伪造了一个TCP的SYN包,把源地址改成受害人的10.1.0.4:80,发给“帮凶”10.1.0.3;
(2)“帮凶” 拆开包一看,发现源地址是10.1.0.3,还真以为是受害人发的,于是按照TCP三次握手的约定,马上给10.1.0.3发送包,具体的包类型有以下几种情况:
- “帮凶”本次收到包的seq比首个SYN的seq+自己的win还大,比如第一个SYN的seq=1000000,通过SYN+ACK告诉受害者自己的win=29200;受害者从5个包接着发送seq=1000000+win+1的包,但从一开始,所有包的len都等于0,没有任何应用层的数据传输,seq怎么能用1029201开始了? 于是“帮凶”不服,在第6个包给受害则回复:从ACK来看,只“承认收到”首个SYN包;
anyway,不管怎样,“帮凶”在收到包后,都会根据不同的情况给受害人发包SYN+ACK或单独的ACK包;不同的seq会对应不同的回复包,总结如下:
真实场景下的攻击流量如下:destination是受害者,在不到13ms的时间内,收到了15个攻击包;
(3) 受害人10.1.0.4收到 “帮凶” 发来的SYN+ACK或SYN包,一脸懵逼:“兄dei,我没给你发SYN包呀,你为啥发我SYN+ACK了?”
如果“帮凶”数量不多,受害人收到的SYN+ACK包不多,现有的硬件资源尚可应对这些无意义的包;可一旦小坏蛋找了大量的“帮凶”,然后伪造SYN包(把src ip改成受害人)发给各个“帮凶”,这些“帮凶”同时响应,给受害人发送SYN+ACK包,造成受害人短时间疲于接受和分析这些无意义的包,无法回应正常的流量,小坏蛋的目的就达到了;
2、还有一种稍微简单一点的SYN Flood攻击,不要找“帮凶”,自己就能完成,如下:3次握手中小坏蛋第一次发送SYN包,有些服务器会专门开辟一个队列记录这种“半连接”的状态;小坏蛋短时间内伪造大量src是假ip的SYN包发给受害者,让其队列排满,无法继续记录正常合理的SYN流量,达到攻击的目的;
三、总结如下:
四、DDOS实战
1、对于普通人来说,要DOS攻击服务器,肯定不可能用常规的flood,原因很简单:自己的带宽肯定拼不过服务器,自损八百后可能还无法杀敌一千,得不偿失;“投机取巧”有这么几种方式:
- 找到大量有TCP反射的服务器,利用它们放大流量,典型如memcache攻击,流量可放大5~6万倍
- 或则用木马钓大量肉鸡,利用这些肉鸡DDOS攻击
以上两种方式都要利用大量的第三方的“帮凶”帮忙,短时间内塞满对方地带宽,相当于“使蛮力”;如果短时间内找不到第三方“帮凶”该怎么办了?
- tear drop:源端发送序列号错乱的畸形tcp包,服务器收到包后重组老是出错,就不停地消耗算力重组,直至算力耗尽;或则数据包对于end和offset故意乱标识,导致服务器计算包长度时变成负数(溢出),覆盖内核的重要代码或数据,导致崩溃宕机;(这里多说一句:近期著名的CVE-2020-0796漏洞,虽说是应用层的漏洞,但基本原理也是服务器接受数据后没检查,导致内存溢出,小坏蛋可利用该漏洞提权或让服务器蓝屏)
- sockStress:tcp建立连接不是要3次握手么?小坏蛋将第三次握手地ACK包中的windwo大小修改为0,然后不停地向服务器发这种包。在服务器看来就是客户端暂时没有空间来接受数据流量,所以服务器一直处于等待状态,且不会释放资源;但是小坏蛋只需要发完window=0的这种包就行,不需要维持这么一个连接,所以对于自身不会消耗巨量的CPU内存资源,达到以小博大地目的;
2、偶然间发现一个Discuz的小黄站,ip在海外;
废话少说,直接用kali上sockStress:
大约1分钟后,网站已经打不开了,浏览器一片空白:
参考: