神奇的Willem

万恶之源TCP

2025/11/30
26
0

传输控制协议(TCP,Transmission Control Protocol)是一种面向连接的、可靠的基于字节流的传输层通信协议,由IETF的RFC793定义 。

万恶开属于TCP,后面大多数应用层为了稳定传输和传输效率,都采用了长链接。但也存在了安全隐患。也带来很多好处,让我们了解一下吧!

TCP旨在适应支持多网络应用的分层协议层次结构,互连的计算机通信网络中成对的应用程序进程之间能够依靠TCP提供可靠的通信服务来传输字节流。TCP支持双向数据流,应用程序也可以仅单向发送数据。在主机之间,TCP使用端口号标识应用程序服务并且可以多路传输数据流。

TCP是一种面向广域网的通信协议,目的是在跨越多个网络通信时,为两个通信端点之间提供一条具有下列特点的通信方式:

(1)基于流的方式;

(2)面向连接;

(3)可靠通信方式;

(4)在网络状况不佳的时候尽量降低系统由于重传带来的带宽开销;

(5)通信连接维护是面向通信的两个端点的,而不考虑中间网段和节点。

为满足TCP协议的这些特点,TCP协议做了如下的规定:

①数据分片:在发送端对用户数据进行分片,在接收端进行重组,由TCP确定分片的大小并控制分片和重组;

②到达确认:接收端接收到分片数据时,根据分片数据序号向发送端发送一个确认;

③超时重传:发送方在发送一个分片时启动超时定时器,如果在定时器超时之后没有收到相应的确认,重发该分片;

④滑动窗口:TCP连接每一方的接收缓冲空间大小都是固定的,接收端只允许另一端发送接收端缓冲区所能接纳的数据,TCP通过滑动窗口机制提供流量控制,防止较快主机致使较慢主机的缓冲区溢出;

⑤失序处理:作为IP数据报来传输的TCP分片到达时可能会失序,TCP将对收到的数据进行重新排序,将收到的数据以正确的顺序交给应用层;

⑥重复处理:作为IP数据报来传输的TCP分片可能会发生重复传输,TCP的接收端必须丢弃重复的数据;

⑦数据校验:TCP将保持它首部和数据的检验和,这是一个端到端的检验和,目的是检测数据在传输过程中的任何变化。如果收到分片的检验和有差错,TCP将丢弃这个分片,且不确认收到此报文,从而导致发端超时并重发。

1. TCP三次握手详解

1.1 三次握手流程

客户端                    服务器
   |                        |
   |-------- SYN ---------->|  (1) 请求连接 (SYN=1, seq=x)
   |<--- SYN + ACK ---------|  (2) 确认连接 (SYN=1, ACK=1, seq=y, ack=x+1)
   |-------- ACK ---------->|  (3) 握手完成 (ACK=1, seq=x+1, ack=y+1)
   |                        |
   |     连接建立完成        |

详细步骤:

  1. 第一次握手 (SYN)

    • 客户端发送SYN包,SYN=1,seq=x(随机初始序列号)

    • 客户端进入SYN_SENT状态

    • 表示:"我想和你建立连接"

  2. 第二次握手 (SYN+ACK)

    • 服务器收到SYN,回复SYN+ACK包

    • SYN=1, ACK=1, seq=y(服务器的初始序列号), ack=x+1

    • 服务器进入SYN_RCVD状态

    • 表示:"我收到了你的请求,同意建立连接,这是我的序列号"

  3. 第三次握手 (ACK)

    • 客户端发送ACK包,ACK=1, seq=x+1, ack=y+1

    • 客户端和服务器都进入ESTABLISHED状态

    • 表示:"我收到了你的确认,连接建立完成"

1.2 为什么不能是两次握手?

核心问题:防止历史连接请求

假设场景:两次握手的问题
客户端                    服务器
   |                        |
   |--- 过期的SYN(seq=100) ->|  (1) 网络延迟,过期请求
   |--- 新的SYN(seq=200) --->|  (2) 正常的新请求  
   |<-- SYN+ACK(ack=101) ---|  (3) 服务器响应过期请求
   |<-- SYN+ACK(ack=201) ---|  (4) 服务器响应新请求

两次握手的问题:

  1. 无法区分新旧连接:服务器无法判断收到的SYN是否为历史请求

  2. 资源浪费:服务器会为过期请求分配资源

  3. 序列号混乱:无法确保双方序列号同步

三次握手解决方案:

  • 第三次握手让客户端确认服务器的响应

  • 客户端可以丢弃过期连接的响应

  • 只有完整的三次握手才建立连接

1.3 三次握手后可以携带数据吗?

答案:可以,但有条件

正常三次握手 + 数据传输:
客户端                    服务器
   |                        |
   |-------- SYN ---------->|  (1) SYN, seq=100
   |<--- SYN + ACK ---------|  (2) SYN+ACK, seq=200, ack=101
   |-- ACK + DATA --------->|  (3) ACK + 应用数据, seq=101, ack=201

可以携带数据的情况:

  1. 第三次握手包可以携带数据

  2. 连接已建立,开始数据传输阶段

  3. 需要正确设置序列号和确认号

不建议在前两次握手携带数据的原因:

  1. SYN包携带数据会增加SYN Flood攻击风险(不携带也可以攻击,效果没有带数据好,数据处理需要消耗算力和时间)

  2. 连接未完全建立时数据可能丢失

  3. 协议设计原则:握手和数据传输分离

2. TCP四次挥手详解

2.1 四次挥手流程

客户端                    服务器
   |                        |
   |------- FIN ----------->|  (1) 客户端关闭 (FIN=1, seq=u)
   |<------ ACK ------------|  (2) 服务器确认 (ACK=1, ack=u+1)
   |                        |
   |<------ FIN ------------|  (3) 服务器关闭 (FIN=1, seq=v)
   |------- ACK ----------->|  (4) 客户端确认 (ACK=1, ack=v+1)
   |                        |
   |     连接完全关闭        |

详细步骤:

  1. 第一次挥手 (FIN)

    • 客户端发送FIN包,表示不再发送数据

    • 客户端进入FIN_WAIT_1状态

  2. 第二次挥手 (ACK)

    • 服务器确认收到FIN包

    • 服务器进入CLOSE_WAIT状态

    • 客户端进入FIN_WAIT_2状态

  3. 第三次挥手 (FIN)

    • 服务器发送FIN包,表示准备关闭连接

    • 服务器进入LAST_ACK状态

  4. 第四次挥手 (ACK)

    • 客户端确认服务器的FIN包

    • 客户端进入TIME_WAIT状态

    • 服务器收到ACK后直接关闭连接

2.2 为什么不能是三次挥手?

核心原因:TCP是全双工通信

全双工连接示意:
客户端 ←→ 服务器
发送通道 ←--------- 需要单独关闭
接收通道 --------→ 需要单独关闭
双方都确认无数据丢失,传送完成才可关闭

四次挥手的必要性:

  1. 双向独立关闭:每个方向的数据流需要独立关闭

  2. 数据完整性:确保所有数据传输完成再关闭

  3. 优雅关闭:给应用层时间处理剩余数据

3.2 CLOSE_WAIT 状态

定义和特点:

  • 被动关闭方收到FIN后,发送ACK进入的状态

  • 等待应用层调用close()关闭连接

  • 如果应用层不关闭,会一直保持此状态

注意:在雷池嵌套雷池的时候:因为雷池前端长时间无数据传输,有超时主动切断TCP链接,直接切断,导致后面反向代理继续以为前站点出现502错误,造成了僵尸链接。(我是傻逼)

CLOSE_WAIT状态流程:
客户端                  服务器(被动关闭)
   |                        |
   |------- FIN ----------->|  客户端FIN
   |<------ ACK ------------|  服务器ACK (进入CLOSE_WAIT)
   |                        |
   |    服务器应用层处理    |
   |                        |
   |<------ FIN ------------|  服务器调用close()
   |------- ACK ----------->|  客户端最终ACK

CLOSE_WAIT过多的问题:

  1. 应用层BUG:忘记调用close()

  2. 资源泄露:文件描述符耗尽

  3. 连接堆积:大量僵尸连接

3.3 状态对比总结

状态

角色

触发条件

持续时间

主要作用

常见问题

TIME_WAIT

主动关闭方

发送最后ACK后

2MSL

确保可靠关闭

端口耗尽

CLOSE_WAIT

被动关闭方

收到FIN后

直到应用close()

等待应用关闭

应用层忘记关闭

4.TCP协议首部字段总览

我们要简单了解一下TCP的头部结构和报文,为什么???我们在优化后攻击中需要对目标端口及IP地址进行伪造。知己知彼百战百胜

4.1源端口与目标端口

通过使用wireShark工具对访问网络的连接进行抓包我们可以得到如下信息:

我们在优化后攻击中需要对目标端口及IP地址进行伪造,进行反射和放大一般来说,

IP层协议会存储源IP与目标IP

而TCP协议层做的事情则是存储源端口与目标端口,源IP、源端口、目标IP、目标端口唯一确定了TCP的一个四元组,一个四元组可以唯一确定一个标识,一台主机上的端口号最多有65536个,而端口号也分为好几种,有熟知端口号,端口范围为(0~1023),例如SSH连接的22端口号、HTTP连接的80端口号,已登记的端口号,端口范围为(1024~49151)等。

我们可以用NMAP扫描特定开放端口,分析手机信息。

4.2序列号(Sequence Number)

序列号 (Sequence Number) 的规律

• 早期的操作系统(如90年代): 初始序列号(ISN)通常基于时间线性增长(例如每秒增加64000)。攻击者极其容易预测下一个连接的序列号。

• 现代操作系统: 现在的OS都采用随机算法生成ISN(遵循RFC 1948),几乎没有规律,很难通过简单的数学推算来预测精确的序列号。

基本无法复现预测序列号

序列号的作用是确定发送数据包的先后顺序,同时在建立连接、防止发送失败等场景也有重要作用,当客户端向服务端发送一个数据包时,通过数据包对应的序列号与报文长度,服务端可以唯一确定发送的是哪一段数据

同时由于网络层(IP层)并不保证发送包的顺序,通过序列号TCP协议可以解决网络包乱序、重复的问题,以保证能够按照顺序接收网络包,例如如果发送方发送包的顺序是1,2,3,4,但是到达接收方的顺序为3,2,1,4,2,那么接收方就可以通过序列号剔除多余包并按照顺序进行接收组装

4.3确认号

注意:序号是本报文段发送的数据组的第一个字节的序号。在TCP传送的流中,每一个字节一个序号。e.g.一个报文段的序号为300,此报文段数据部分共有100字节,则下一个报文段的序号为400。所以序号确保了TCP传输的有序性。确认号,即ACK,指明下一个期待收到的字节序号,表明该序号之前的所有数据已经正确无误的收到。确认号只有当ACK标志为1时才有效。比如建立连接时,SYN报文的ACK标志位为0。

4.4位首部长度

4bits。由于首部可能含有可选项内容,因此TCP报头的长度是不确定的,报头不包含任何任选字段则长度为20字节,4位首部长度字段所能表示的最大值为1111,转化为10进制为15,15*32/8 = 60,故报头最大长度为60字节。首部长度也叫数据偏移,是因为首部长度实际上指示了数据区在报文段中的起始偏移值。

所以图上是32bytes,32个字节长度头部

4.5Flags

前六位都是0 保留

第七个:Urgent紧急指针标志,为1时表示紧急指针有效,为0则忽略紧急指针

下一个:ACK确认序号标志,为1时表示确认号有效,为0表示报文中不含确认信息,忽略确认号字段

下一个:push标志,为1表示是带有push标志的数据,指示接收方在接收到该报文段以后,应尽快将这个报文段交给应用程序,而不是在缓冲区排队

下一个:RST重置连接标志,用于重置由于主机崩溃或其他原因而出现错误的连接。或者用于拒绝非法的报文段和拒绝连接请求

下一个:finish标志,用于释放连接,为1时表示发送方已经没有数据发送了,即关闭本方数据流

4.6 窗口大小(重点)

用于表示窗口大小的位数只有16位(Window Size Value),换算一下也就是65536字节(64KB),这个大小对于当下计算机系统来说肯定是远远不够用的,因此TCP协议又引入了TCP窗口缩放这个选项作为窗口缩放的比例因子,比例因子的范围为0~14,即最小0为原来大小64KB,,窗口缩放值会在三次握手时指定,例如在用wireShark抓包后,展示的窗口大小为65536

TCP攻击(通常指TCP序列号预测攻击、TCP劫持或RST复位攻击)必须落在“窗口”内,如果不在窗口内,接收方会直接把攻击包丢弃,攻击就无效了。但他是动态的浮动变化等。每次链接不一样。

 窗口大小 (Window Size) 的规律

窗口本身的大小是有一定规律的,但这取决于带宽延迟积 (BDP):

• 低速/低延迟网络: 窗口较小(例如64KB)。攻击者为了命中这个小范围,需要发送海量的伪造包(暴力碰撞),很容易被检测到。

• 高速/长肥管道(Long Fat Network): 为了跑满千兆/万兆带宽,现代TCP连接的窗口通常非常大(几MB甚至几GB)。

基本上不可能了,有脚本猜测,但是也很难,除非你知道他们连击速度很慢很卡CVE-2016-5696基本被修复。

但是如果你是中间人,那你就快乐了抓包吧孩子!

16位窗口大小(重要)

滑动窗口大小,用来告知发送端接受端的缓存大小,以此控制发送端发送数据的速率,从而达到流量控制。窗口大小是一个16bit字段,因而窗口大小最大为65535。

零窗口(ZeroWindow)
当发送方的发送速度大于接收方的处理速度,接收方的缓冲塞满后,就会告诉发送方当前窗口size=0,请停止发送,发送方此时停止发送数据

16位TCP校验和

奇偶校验,此校验和是对整个的 TCP 报文段,包括 TCP 头部和 TCP 数据,以 16 位字进行计算所得。由发送端计算和存储,并由接收端进行验证

16位紧急指针(没啥用)

Urgent只有当 URG 标志置 1 时紧急指针才有效。紧急指针是一个正的偏移量,和顺序号字段中的值相加表示紧急数据最后一个字节的序号。 TCP 的紧急方式是发送端向另一端发送紧急数据的一种方式

4.7MSS长度选项(若有)

最常见的可选字段是最长报文大小,又称为MSS(Maximum Segment Size),每个连接方通常都在通信的第一个报文段(为建立连接而设置SYN标志为1的那个段)中指明这个选项,它表示本端所能接受的最大报文段的长度。选项长度不一定是32位的整数倍,所以要加填充位,即在这个字段中加入额外的零,以保证TCP头是32的整数倍

数据

TCP 报文段中的数据部分是可选的。在一个连接建立和一个连接终止时,双方交换的报文段仅有 TCP 首部。如果一方没有数据要发送,也使用没有任何数据的首部来确认收到的数据。在处理超时的许多情况中,也会发送不带任何数据的报文段5. TCP拥塞控制详解

5.1 拥塞控制概述

目标:

  • 防止网络拥塞

  • 公平分配网络资源

  • 最大化网络利用率

核心机制:

发送速率 = min(拥塞窗口, 接收窗口) / RTT

5.2 拥塞控制算法

5.2.1 慢启动 (Slow Start)

拥塞窗口变化:
cwnd = 1 MSS                 # 初始值
每收到一个ACK: cwnd += 1     # 指数增长

特点:

  • 指数增长:1 → 2 → 4 → 8 → 16 ...

  • 快速探测可用带宽

  • 达到慢启动阈值(ssthresh)后进入拥塞避免

# 慢启动示例
def slow_start():
    cwnd = 1  # 初始拥塞窗口
    ssthresh = 16  # 慢启动阈值
    
    while cwnd < ssthresh:
        print(f"发送 {cwnd} 个段")
        # 假设收到 cwnd 个ACK
        cwnd *= 2  # 每个RTT翻倍
        
    print("进入拥塞避免阶段")

5.2.2 拥塞避免 (Congestion Avoidance)

拥塞窗口变化:
每收到一个ACK: cwnd += 1/cwnd    # 线性增长
每个RTT: cwnd += 1

特点:

  • 线性增长,谨慎探测

  • AIMD算法:Additive Increase, Multiplicative Decrease

5.2.3 快速重传 (Fast Retransmit)(重点)

触发条件:收到3个重复ACK
动作:立即重传丢失的数据包/丢包
快速重传示例:
发送方                     接收方
   |                         |
   |------ Seg1 ------------>|  收到,ACK1
   |------ Seg2 -----------X | 丢失
   |------ Seg3 ------------>|  期望Seg2,发送ACK1
   |------ Seg4 ------------>|  期望Seg2,发送ACK1
   |------ Seg5 ------------>|  期望Seg2,发送ACK1
   |<----- ACK1 x3 ----------|  收到3个重复ACK
   |------ Seg2 ------------>|  快速重传

5.2.4 快速恢复 (Fast Recovery)(基本上不在使用)

进入条件:检测到丢包(3个重复ACK)
动作:
1. ssthresh = cwnd / 2      # 阈值减半
2. cwnd = ssthresh + 3      # 拥塞窗口设为阈值+3
3. 每收到重复ACK: cwnd++   # 继续增长
4. 收到新ACK: cwnd = ssthresh  # 恢复正常

5.3 现代拥塞控制算法

5.3.1 BBR (Bottleneck Bandwidth and RTT)

核心思想:

  • 不依赖丢包检测拥塞

  • 基于带宽和RTT测量

  • 四个状态:Startup, Drain, ProbeBW, ProbeRTT

BBR算法:
发送速率 = 测量带宽 × RTT(TCP往返时间) / 数据量

Startup(起步阶段)—— 类似“地板油”(慢启动,一点点给到最大)

• 动作: 刚开始不知道路况(网络带宽),于是大脚踩油门。发送速率呈指数级增长。(如BBRplus)

• 目的: 快速填满管道,探测出网络的最大带宽(BtlBw)。

• 结束条件: 当加速了,但发现车速(吞吐量)不再提升,说明管道满了,再发就只会堵在路由器里。这时候进入下一阶段。

表现为延迟升高或丢包

2. Drain(排空阶段)—— 到达瓶颈会维持速录,而不是TCP那种直接砍半。

• 动作: 因为刚才“地板油”踩猛了,肯定有一些包堵在了路由器的缓存(Buffer)里。BBR 会迅速降低发送速率(比正常带宽低)。

• 目的: 把刚才那个阶段多塞进去的数据“排空”,让管道里的数据量回归到理想状态(即 BDP,带宽 x 延迟)。

3. ProbeBW(探测带宽)—— 避免拥堵算法

这是 BBR 绝大部分时间(98%)所处的状态。

它不再像你刚才的图那样“丢包才减速”,而是主动地“调皮”一下:

• 加速 (1.25x): 突然多发一点点。问网络:还能再快点吗?

• 如果网络回话:能!-> 更新最大带宽记录。

• 如果网络回话:不能(延迟变大了)!-> 没关系,进入下一步。

• 减速 (0.75x): 刚才试探多发了点,现在少发点,把多出来的排掉。

• 平稳 (1.0x): 保持刚才测出来的最佳速度跑一会儿。

不断尝试极限,和丢包延迟取决平衡,如果出现丢包,自动触发快速充传。

4. ProbeRTT(探测延迟)—— 避免拥堵算法

• 触发: 如果连续 10 秒钟,网络一直很忙,BBR 担心测出来的延迟(RTT)不准(因为一直在排队)。

• 动作: 强制把发送窗口降到极低(只发 4 个包),几乎是因为自己原因导致拥堵,几乎暂停发送约 200ms。

• 目的: 让路上的车都跑光,测一下在没有车流排队的情况下RTT,空跑一趟到底要多久(物理延迟 RTprop)。这保证了 BBR 的模型基准是准确的。

BBR 的聪明之处就在于,它 98% 的时间在跑高速(测带宽),只用 2% 的时间减速清场(测延迟),用这微小的牺牲换来了整体的极致性能

如图所示:

6. TCP vs UDP 详细对比

6.1 核心差异对比

特性

TCP

UDP

连接性

面向连接

无连接

可靠性

可靠传输

不可靠传输

有序性

保证有序

不保证有序

流量控制

拥塞控制

头部开销

20-60字节

8字节

速度

相对较慢

快速

适用场景

可靠性要求高

实时性要求高

6.2性能特征

TCP性能开销:

连接建立:3次握手 (1.5 RTT)
数据传输:确认 + 重传 + 流控 + 拥塞控制
连接关闭:4次挥手 (2 RTT)
内存占用:连接状态 + 发送/接收缓冲区

UDP性能优势:

无连接开销:立即发送数据
无确认开销:单向传输
无状态开销:最小内存占用
无拥塞控制:最大发送速率

6.3 应用场景选择

6.3.1 TCP适用场景

1. Web应用 (HTTP/HTTPS)
   - 要求数据完整性
   - 可以容忍延迟
   
2. 文件传输 (FTP, SFTP)
   - 数据不能丢失
   - 顺序很重要
   
3. 邮件传输 (SMTP, IMAP)
   - 可靠性至关重要
   
4. 远程登录 (SSH, Telnet)
   - 命令不能丢失
   
5. 数据库连接
   - 事务完整性

6.3.2 UDP适用场景

1. 视频直播/会议
   - 实时性重要
   - 丢失部分数据可接受
   
2. 在线游戏
   - 低延迟需求
   - 位置更新频繁
   
3. DNS查询
   - 简单请求-响应
   - 可以重试
   
4. DHCP
   - 广播性质
   - 简单配置
   
5. 音频流
   - 实时传输
   - 缓冲处理

7. TCP网络攻击与安全隐患

7.1 TCP协议层攻击

7.1.1 SYN Flood 攻击(强者)

攻击原理:

攻击者                   目标服务器
   |                        |
   |------ SYN(伪造IP) ---->|  大量SYN请求
   |------ SYN(伪造IP) ---->|  服务器分配资源
   |------ SYN(伪造IP) ---->|  等待ACK(永远不来)
   |                        |
   |    服务器资源耗尽      |

攻击效果:

  • 服务器半连接队列满

  • 无法处理正常连接请求

  • 拒绝服务攻击(DoS)

防护措施:

# SYN Flood防护
class SYNFloodProtection:
    def __init__(self):
        self.syn_cookies = True      # 启用SYN Cookies
        self.syn_backlog = 1024      # 调整半连接队列大小
        self.syn_retries = 3         # 限制SYN重试次数
        
    def handle_syn(self, syn_packet):
        if self.is_syn_flood():
            # 使用SYN Cookies而非分配连接状态
            cookie = self.generate_syn_cookie(syn_packet)
            return self.send_syn_ack_with_cookie(cookie)
        else:
            # 正常处理
            return self.normal_syn_handling(syn_packet)

# 启用SYN Cookies

# 调整半连接队列大小

# 限制SYN重试次数

#快速回收(队列满了直接拒绝)

挂CDN并开启人机验证,认证后颁发Cookise,有效防止劫持和,SYN洪水

7.1.2 TCP RST 攻击(需要中间人,盲猜基本无缘CVE-2016-5696)

攻击原理:

攻击者监听网络流量,伪造RST包强制断开连接
 
正常通信:
客户端 <--------数据流-------> 服务器
   ↑                            ↑
   |                            |
   |------ RST (伪造) ---------> |
   |                            |
   连接被强制断开

攻击条件:

  • 知道四元组(源IP、源端口、目标IP、目标端口)

  • 序列号在接收窗口内

  • 网络可达

防护措施:

  • 使用IPSec/TLS加密(有效防止VPN被劫持,推荐SSLVPN)

  • 序列号随机化RFC 5961(缺点: 只能防盲猜,防不了中间人(因为中间人看得到你的精确序列号如途径防火墙和路由)

重点:中国的GFW怎么切断网站链接的,就用这个方法。

当你访问某个被屏蔽的网站时,防火墙检测到敏感关键词(HTTP头或SNI),它不会直接拦截你的包(那样太累)如果你一直发包会让防火墙很大的压力,而是伪装成服务器给你发个 RST,同时伪装成你给服务器发个 RST。双向掐断。

表现: 浏览器显示“连接被重置 (Connection Reset)”。

7.1.3 TCP Hijacking (会话劫持)(基本放弃)

攻击流程:

1. 攻击者嗅探网络流量

2. 获取TCP连接状态信息

中间人攻击 (Man-in-the-Middle, MitM):

• 攻击者在你的网线/Wi-Fi 路径上。他能看到所有的包,所以他能精准知道当前的序列号。这是最容易成功的劫持。

4. 注入恶意数据包

5. 控制TCP会话

防护措施:

  • 序列号强随机化

  • TLS/SSL加密

  • 入侵检测系统IDS

由于现在随机序列号,和跨站防护很好,以及很难遇到劫持攻击。

7.1.4反射放大攻击 (UDP Reflection/Amplification) —— 目前的主流

这是 DDoS 攻击中的霸主。因为它不需要建立 TCP 连接(无握手),且利用 UDP 的“无状态”特性,攻击者可以伪造受害者的 IP 去请求公网上的服务器。

• 原理:

1. 攻击者伪造受害者的 IP (1.2.3.4)。

2. 向公网上开放的 DNS、NTP、SSDP、Memcached 服务器发送一个小请求(比如问“你是谁?”)。

3. 这些服务器会向 1.2.3.4 回复一个巨大的包(比如几千倍大小的回复)。

常用协议:

• DNS 放大: 请求根域名服务器,返回大量记录。

• NTP 放大 (Monlist): 请求时间服务器列表。

• Memcached 放大: 这是最恐怖的,放大倍数可达 5万倍。

• LDAP / CLDAP 放大: 利用企业的目录服务端口。

现状: 非常好用,成本极低,流量极大。通过 UDP 直接把受害者的带宽塞满。

7.2 应用层攻击

7.2.1 HTTP Slowloris 攻击

攻击原理:

# Slowloris攻击模拟
class SlowlorisAttack:
    def __init__(self, target_host, target_port):
        self.target = (target_host, target_port)
        self.sockets = []
        
    def start_attack(self):
        # 建立大量TCP连接
        for i in range(1000):
            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            sock.connect(self.target)
            
            # 发送不完整的HTTP请求
            sock.send(b"GET / HTTP/1.1\r\n")
            sock.send(b"Host: target.com\r\n")
            # 故意不发送\r\n\r\n结束标志
            
            self.sockets.append(sock)
            
        # 定期发送额外的头部,保持连接
        while True:
            for sock in self.sockets:
                try:
                    sock.send(b"X-Custom-Header: value\r\n")
                    time.sleep(10)
                except:
                    # 连接断开,重新建立
                    self.sockets.remove(sock)
                    self.establish_new_connection()

2. 技术原理

这个最恶心TMD。HTTP 协议规定,一个完整的请求头(Headers)必须以两个连续的换行符 (\r\n\r\n) 结尾,代表“头发送完毕,服务器你可以开始处理了”。

Slowloris 的攻击逻辑:

1. 建立连接: 完成 TCP 三次握手。

2. 发送部分头部: 发送 GET / HTTP/1.1。

3. 无限续命: 只要连接快超时了,就发送一个无意义的头,比如 X-Ignore: 1,然后继续保持沉默。

4. 死不结尾: 永远不发送 那个关键的 \r\n\r\n。

这样,Web 服务器(特别是 Apache 1.x/2.x 这种基于线程的)会认为客户端网速慢,还在努力发数据,所以会一直保留这个连接线程,直到达到服务器的最大并发连接数上限。

3.解决方法

调整 Nginx 超时配置

原理: 既然攻击者想拖时间,我就规定超时丢弃。

在 Nginx 配置文件 (nginx.conf) 的 http 或 server 块中添加:

http {
    # 1. 限制请求头接收时间 (防 Slowloris)
    # 默认可能是 60s,改成 5s。
    # 正常人谁发个 HTTP 头要 5 秒?肯定是攻击。
    client_header_timeout 5s;

    # 2. 限制请求体接收时间 (防 Slow POST)
    # 如果两个连续的数据包之间间隔超过 10s,就断开。
    client_body_timeout 10s;

    # 3. 限制连接总时长 (可选)
    # 无论你多慢,这个连接最多只能活 60秒。
    keepalive_timeout 60s;
}

7.2.2 慢速POST攻击

攻击原理:

1. 发送POST请求头,声明大量数据
2. 极其缓慢地发送POST数据
3. 占用服务器连接资源
4. 导致正常用户无法访问

2. 技术原理

和上面一个逼样!

服务器(特别是传统的 Apache,或者未优化的 Nginx)处理连接的线程数/进程数是有限的(比如默认 1024 个)。

1. 发送 Header: 攻击者发送一个 POST 请求,关键在于 Header 里带上 Content-Length: 10000000(声明有一个很大的数据包要传)。

2. 正文慢传: 服务器收到 Header 后,打开大门准备接收 Body。攻击者以极慢的速度(例如每分钟 1 个字节)发送数据。

3. 维持连接: 每次发送 1 个字节,都能重置服务器的“空闲超时计时器”,让服务器觉得连接还是活的。

解决方法:

限制缓冲区大小

原理: 既然攻击者利用大 Content-Length 来骗资源,我就限制最大允许上传的大小。(尤其是图床Willem被揍过)

1. Nginx 层面: 调小 client_header_timeout 和 client_body_timeout,对慢速连接零容忍。

2. 架构层面: 只要套上 Cloudflare(开启小黄云),慢速攻击对你就是无效的。(套CDN)

http {
    # 限制上传文件大小。博客传图片一般 10MB 够了。
    # 攻击者如果声称要传 1GB,直接拒绝。
    client_max_body_size 10m;

    # 限制请求头缓冲区
    large_client_header_buffers 4 8k;
}

7.2.3 TCP Middlebox Reflection (TCP 中间盒反射) —— 较新的手法(强者)

这是近几年被学术界和黑产挖掘出来的,利用防火墙(Middlebox)的特性来做 DDoS。

• 原理:

• 世界上有很多防火墙(比如某些国家的审查防火墙、企业防火墙),当你发送某种违规请求时,它们会试图拦截并给你发一个“拦截页面”或 RST 包。

• 攻击者伪造受害者的 IP,向这些防火墙发送触发拦截的包。

• 防火墙向受害者发送大量的拦截响应数据。

  • 利用构造IP层/TCP层/http头或处罚注入语句造成反射。

  • 优势: 这是 TCP 协议的反射,比 UDP 更难防,因为看起来像是合法的 TCP 回包。

难以防御

• 看起来像正常流量: 受害者收到的是正规的 HTTP 响应或者是标准的 TCP 包。

• 利用基础设施: 攻击者不需要控制僵尸网络,只需要利用公网上的防火墙设备。这些设备带宽极大,性能极强。

• 没有握手: 受害者会莫名其妙收到大量带数据的 PSH+ACK 包,但本地根本没有这个连接记录,导致系统资源消耗

7.3 网络层相关攻击

7.3.1 IP欺骗攻击

攻击场景:

真实客户端 IP: 192.168.1.100
攻击者伪造源IP发送TCP包
 
伪造的TCP包:
源IP: 192.168.1.100 (伪造)
目标IP: 192.168.1.1
源端口: 随机
目标端口: 80
# IP欺骗防护
class AntiSpoofing:
    def __init__(self):
        self.trusted_networks = ['192.168.1.0/24']
        
    def validate_source_ip(self, packet):
        source_ip = packet.src_ip
        
        # 1. 检查源IP是否在合理范围
        if self.is_bogon_ip(source_ip):
            return False
            
        # 2. 反向路径验证
        if not self.reverse_path_forwarding_check(source_ip):
            return False
            
        # 3. 地理位置验证
        if not self.geo_location_check(source_ip):
            return False
            
        return True

8 防护策略

8.1多层防护体系

时代不一样了,随着技术的更新,会话劫持,RST拒绝攻击随着Linux内核升级,以及SSl普及变得难度跟高,除非在路径中间者获取完整数据包才可以。

SYN洪水,在个别局域网可以尝试,大部分的路由器有防止SYN和Vlan划分。(还是可以操作的)

Slowloris & Slow POST 还算可以这个是真恶心。但是可以很轻缓解。套了CDN基本上就不可能了。

总结

TCP作为互联网的核心协议,其设计体现了网络通信的复杂性和可靠性要求:

  1. 三次握手和四次挥手确保连接的可靠建立和优雅关闭

  2. 复杂的状态管理(如TIME_WAIT、CLOSE_WAIT)保证网络的稳定性

  3. 精心设计的头部结构支持各种高级功能

  4. 拥塞控制机制保证网络的公平性和效率

  5. 与UDP的差异体现了可靠性与性能的权衡

  6. 安全隐患和防护需要多层次的安全策略

理解TCP的这些机制对于网络编程、系统优化和安全防护都至关重要。在实际应用中,需要根据具体场景选择合适的协议和优化策