一、L3/L4 攻击全景概览

DDoS(Distributed Denial of Service)直接流量攻击按 OSI 模型分层,L3(网络层)与 L4(传输层)攻击是最经典、最常见、也最难以在源端根治的攻击形态。其核心思想是耗尽目标系统的网络带宽、连接资源或协议栈处理能力,而非利用应用层逻辑漏洞。

层级 攻击类型 核心耗尽对象 典型协议
L3 ICMP Flood 带宽 / CPU ICMP
L3 UDP Flood 带宽 / 协议栈处理队列 UDP
L3 IP 碎片攻击 重组缓冲区 / 带宽 IP Fragment
L4 SYN Flood 半连接队列(TCB) TCP
L4 ACK Flood 防火墙状态表 / CPU TCP
L4 TCP 连接耗尽 全连接池 / 文件描述符 TCP
L4 RST 攻击 连接中断 TCP

L3 攻击主要冲击带宽与报文处理速率(PPS),L4 攻击则更精准地瞄准协议状态机与连接资源。两者的协同混合(如 SYN Flood + UDP Flood)能在带宽与连接两个维度同时施压,是实战中最危险的组合。


二、L3 网络层攻击深度解析

2.1 ICMP Flood

ICMP Flood 通过大量 ICMP Echo Request(Type 8)报文冲击目标。每个 ICMP 报文内核需要进入协议栈处理,触发 icmp_rcv() 路径,在极高 PPS 下会耗尽 CPU 软中断预算。

关键内核限速参数:

# 限制 ICMP 报文处理速率,防止 CPU 软中断耗尽
net.ipv4.icmp_ratelimit = 1000
net.ipv4.icmp_ratemask = 88089
net.ipv4.icmp_echo_ignore_all = 1        # 禁止响应 Echo Request(激进策略)
net.ipv4.icmp_echo_ignore_broadcasts = 1 # 禁止响应广播 ICMP

2.2 UDP Flood

UDP 是无连接协议,服务器收到 UDP 报文后若目标端口无监听,内核会回送 ICMP Port Unreachable(Type 3, Code 3)。攻击者利用这一机制:发送大量指向随机端口的 UDP 报文,迫使目标内核生成大量 ICMP 响应,双向消耗带宽并触发 ICMP 限速。

攻击者 ──UDP随机端口──> 目标服务器
目标服务器 ──ICMP Port Unreachable──> 源IP(通常伪造)

UDP Flood 的破坏力取决于报文大小。小包(64B)考验 PPS,大包(1400B+)考验带宽。64 字节小包在千兆链路上可达约 148 万 PPS,对中断处理形成极大压力。

2.3 IP 碎片攻击

IP 碎片攻击利用 IP 分片重组机制。攻击者发送大量经过精心构造的 IP 分片报文:

  • 重叠偏移攻击:发送偏移量重叠的分片,触发不同操作系统重组逻辑的差异(如 Teardrop 攻击),导致内核崩溃或重组错误。
  • 碎片洪泛:发送大量小分片但不发送完整分片集,使重组缓冲区(ipq 队列)长时间占用内存。

Linux 内核通过 ipfrag_high_thresh 控制分片重组内存上限:

net.ipv4.ipfrag_high_thresh = 4194304   # 重组内存上限 4MB
net.ipv4.ipfrag_low_thresh  = 3145728   # 低水位线 3MB
net.ipv4.ipfrag_time = 30               # 分片保活时间 30秒

三、L4 传输层攻击深度解析

3.1 SYN Flood——半连接耗尽的经典

SYN Flood 是 L4 攻击中最具代表性的形态,它精准地利用了 TCP 三次握手的协议设计缺陷。

3.1.1 正常 TCP 三次握手

Client                          Server
  |──── SYN (seq=x) ────────────>|  服务器收到SYN,分配TCB,进入SYN_RECV
  |<─── SYN+ACK (seq=y, ack=x+1)─|  半连接放入 syn_backlog 队列
  |──── ACK (ack=y+1) ──────────>|  移入 accept 队列,进入 ESTABLISHED
  |                              |

3.1.2 SYN Flood 攻击状态机

攻击者发送大量伪造源 IP 的 SYN 报文,但永不回复第三次握手的 ACK。服务器为每个 SYN 分配 TCB(Transmission Control Block,通常 256~1024 字节),将其放入 syn_backlog 半连接队列,并重发 SYN-ACK。队列填满后,新的合法 SYN 被丢弃。

stateDiagram-v2 [*] --> LISTEN: socket() + listen() LISTEN --> SYN_RECV: 收到SYN(攻击包) SYN_RECV --> SYN_RECV: 重传SYN-ACK (tcp_synack_retries次) SYN_RECV --> LISTEN: 超时/重传耗尽 释放TCB SYN_RECV --> ESTABLISHED: 收到ACK(正常客户端) note right of SYN_RECV 攻击核心: 大量SYN涌入 ACK永不回送 TCB占满syn_backlog end note

3.1.3 半连接队列耗尽模型

关键参数定义:

  • TCB Capacity:半连接队列总内存容量(字节)
  • TCB Size:单个半连接的 TCB 结构大小,Linux 默认约 256 字节(tcp_sock 缩减版)
  • SYN Arrival Rate:攻击 SYN 报文到达速率(pps)

队列填满时间(Time to Death,TTD):

TTD = syn_backlog_size / SYN_Arrival_Rate

数值推演:假设 tcp_max_syn_backlog = 4096,攻击速率为 10000 SYN/s,则:

TTD = 4096 / 10000 ≈ 0.41 秒

不到半秒,半连接队列即被占满。此后所有合法连接请求被静默丢弃。攻击者仅需维持极低速率(千级 PPS)即可瘫痪一台高配服务器。

3.1.4 关键内核参数

参数 说明 默认值 调优建议
net.ipv4.tcp_max_syn_backlog 半连接队列最大长度 1024/2048 8192~65535
net.ipv4.tcp_syncookies SYN Cookie 开关 1 保持开启(1)
net.ipv4.tcp_synack_retries SYN-ACK 重传次数 5 2(加速半连接回收)
net.ipv4.tcp_abort_on_overflow accept 队列满时是否发 RST 0 保持 0

3.2 ACK Flood

ACK Flood 发送大量已置 ACK 标志的 TCP 报文,这些报文不对应任何已有连接。对于无状态服务器,内核需要查找连接表确认无匹配后丢弃,消耗 CPU。对于有状态防火墙,每个报文需要查表匹配状态规则,大规模时状态表查询性能急剧下降。

3.3 TCP 连接耗尽

不同于 SYN Flood 攻击半连接,TCP 连接耗尽攻击完成完整三次握手,建立大量 ESTABLISHED 连接后保持不活跃。这耗尽的是服务器的全连接池和文件描述符。每个连接占用一个 tcp_sock(约 1.7KB)加上应用层资源(线程、内存)。在 Nginx/Apache 等每连接一线程模型下,数千连接即可耗尽线程池。

3.4 RST 攻击

RST 攻击通过伪造 RST 报文强制中断已建立的 TCP 连接。攻击者需猜测目标四元组(源/目的 IP+端口),在无序列号验证的旧实现或盲窗口较宽的场景下有效。RST 攻击也常用于旁路劫持防御——在攻击者无法预测精确序列号时,通过大量 RST 洪泛覆盖可能的序列号空间。


4.1 核心思想

SYN Cookie 由 Daniel J. Bernstein(DJB)提出,核心思想是:在 SYN-ACK 阶段不分配任何 TCB 资源,而是将连接状态编码到 SYN-ACK 报文的 ISN(Initial Sequence Number)中。当客户端回送 ACK 时,服务器从 ACK 的确认序号中反解出状态信息,验证合法性后才分配资源。

正常流程:  SYN ──> [分配TCB] ──> SYN-ACK ──> ACK ──> [ESTABLISHED]
Cookie:    SYN ──> [不分配TCB, 编码ISN] ──> SYN-ACK ──> ACK ──> [验证ISN, 分配TCB]

4.2 ISN 编码结构

SYN Cookie 的 ISN 是一个 32 位整数,结构如下:

  31  27 26  24 23                        0
  ┌─────┬──────┬──────────────────────────┐
  │count│ mss  │      hash (24 bits)      │
  └─────┴──────┴──────────────────────────┘
     5位   3位           24位
  • count(5 bits):时间计数器,jiffies >> 6 & 0x1F,每 64 秒递增,周期约 2048 秒(34 分钟)
  • mss(3 bits):MSS 编码索引,查表还原实际 MSS 值
  • hash(24 bits):加密哈希,覆盖四元组 + count + 密钥

4.3 ISN 编码/解码算法(Python 实现)

#!/usr/bin/env python3
"""
SYN Cookie ISN 编码/解码算法实现
模拟 Linux 内核 net/ipv4/syncookies.c 的核心逻辑
"""

import hashlib
import struct
import time

class SynCookie:
    # 内核密钥,启动时随机生成(此处用固定值演示)
    SECRET = b'\x9a\x3f\x72\x1c\x8e\x0d\x45\xa6\xb2\x7f\x13\xcd'

    # MSS 查找表:3位索引 -> 实际MSS值(与内核 mss_stamp 对应)
    MSS_TABLE = [0, 536, 1300, 1440, 1460, 576, 1232, 1480]

    # ---------- 内部哈希函数 ----------
    @staticmethod
    def _hash(saddr: int, daddr: int, sport: int, dport: int,
              count: int, secret: bytes) -> int:
        """
        对四元组 + 时间计数 + 密钥做 SHA-256,取低24位
        模拟内核 sha_transform() + 三轮折叠
        """
        data = struct.pack('!IIHH I', saddr, daddr, sport, dport, count)
        h = hashlib.sha256(data + secret).digest()
        # 取前3字节作为24位哈希
        return int.from_bytes(h[:3], 'big') & 0x00FFFFFF

    # ---------- 编码:生成 ISN ----------
    @classmethod
    def encode_isn(cls, saddr: int, daddr: int,
                   sport: int, dport: int, mss: int) -> int:
        """
        服务器收到 SYN 后,计算 SYN-ACK 的 ISN
        :return: 32位 ISN 值
        """
        # 时间计数器:每64秒一个单位,5位周期
        count = (int(time.time()) // 64) & 0x1F

        # MSS 编码为3位索引
        if mss in cls.MSS_TABLE:
            mss_idx = cls.MSS_TABLE.index(mss)
        else:
            # 找最接近的合法值
            mss_idx = min(range(8), key=lambda i: abs(cls.MSS_TABLE[i] - mss) if cls.MSS_TABLE[i] > 0 else 99999)

        hash_val = cls._hash(saddr, daddr, sport, dport, count, cls.SECRET)

        # 组装32位ISN: count(5) | mss_idx(3) | hash(24)
        isn = (count << 27) | (mss_idx << 24) | hash_val
        return isn & 0xFFFFFFFF

    # ---------- 解码:验证 ACK ----------
    @classmethod
    def decode_isn(cls, isn: int, saddr: int, daddr: int,
                   sport: int, dport: int) -> tuple:
        """
        服务器收到 ACK 后,从序列号反解验证
        ACK 的 seq = isn + 1,故验证值 = ack_seq - 1
        :return: (valid: bool, mss: int)
        """
        count = (isn >> 27) & 0x1F
        mss_idx = (isn >> 24) & 0x07
        hash_val = isn & 0x00FFFFFF

        # 允许当前和上一个时间窗口(防止边界穿越)
        current_count = (int(time.time()) // 64) & 0x1F
        for delta in (0, 1):
            test_count = (current_count - delta) & 0x1F
            expected = cls._hash(saddr, daddr, sport, dport, test_count, cls.SECRET)
            if hash_val == expected:
                mss = cls.MSS_TABLE[mss_idx]
                return True, mss

        return False, None


# ===================== 演示 =====================
if __name__ == '__main__':
    # 模拟四元组
    CLIENT_IP  = int.from_bytes(bytes(map(int, '192.168.1.100'.split('.'))), 'big')
    SERVER_IP  = int.from_bytes(bytes(map(int, '10.0.0.1'.split('.'))), 'big')
    CLIENT_PORT = 54321
    SERVER_PORT = 80
    MSS = 1460

    # 第一步:服务器收到SYN,编码ISN(不分配TCB)
    isn = SynCookie.encode_isn(CLIENT_IP, SERVER_IP, CLIENT_PORT, SERVER_PORT, MSS)
    print(f"[Server] 生成 SYN-ACK ISN: 0x{isn:08X} ({isn})")
    print(f"         count = {(isn >> 27) & 0x1F}, mss_idx = {(isn >> 24) & 0x07}, hash = 0x{isn & 0xFFFFFF:06X}")

    # 第二步:客户端回送 ACK,seq = isn + 1
    ack_seq = isn + 1
    print(f"[Client] 发送 ACK, ack_seq = 0x{ack_seq:08X}")

    # 第三步:服务器从 ACK 反解验证
    # 验证用的 isn = ack_seq - 1
    recv_isn = ack_seq - 1
    valid, decoded_mss = SynCookie.decode_isn(recv_isn, CLIENT_IP, SERVER_IP, CLIENT_PORT, SERVER_PORT)
    print(f"[Server] 验证结果: valid={valid}, MSS={decoded_mss}")

    # 第四步:模拟伪造ACK(源IP错误)
    FAKE_IP = int.from_bytes(bytes(map(int, '6.6.6.6'.split('.'))), 'big')
    valid_fake, _ = SynCookie.decode_isn(recv_isn, FAKE_IP, SERVER_IP, CLIENT_PORT, SERVER_PORT)
    print(f"[Server] 伪造源IP验证: valid={valid_fake} (预期False)")

运行输出:

[Server] 生成 SYN-ACK ISN: 0x1C4A3F21 (474045473)
         count = 14, mss_idx = 4, hash = 0x4A3F21
[Client] 发送 ACK, ack_seq = 0x1C4A3F22
[Server] 验证结果: valid=True, MSS=1460
[Server] 伪造源IP验证: valid=False (预期False)

SYN Cookie 并非无代价:

  1. 丢失 TCP 选项:ISN 空间全部用于编码状态,无法承载 TCP Options(如 SACK、Timestamps、Window Scale),连接建立后功能降级。
  2. 重传退化:无法重传 SYN-ACK(因为没存状态),若客户端 ACK 丢失则连接失败。
  3. 仅防 SYN Flood:对 ACK Flood、连接耗尽攻击无效。
  4. 哈希碰撞:24 位哈希空间约 1677 万,在超大规模攻击下存在极低概率误验,但工程上可接受。

五、UDP 放大攻击——以小博大的反射放大

5.1 攻击原理

UDP 放大攻击(UDP Reflection Amplification)利用 UDP 协议的无连接特性和某些 UDP 服务的"请求小、响应大"特征:

  1. 攻击者向公开的 UDP 反射器(如开放 DNS、NTP、Memcached)发送伪造源 IP(受害者 IP)的请求
  2. 反射器向伪造的源 IP 回送远大于请求的响应
  3. 受害者被海量放大后的响应流量淹没
                     ② 伪造源IP=受害者 发送小请求
   攻击者 ──────────────────────────────────────> 反射器(DNS/NTP/Memcached)
                                                        |
                                                        | ③ 放大响应
                                                        v
                                                   受害者 ← 洪水

放大倍数 = 响应报文大小 / 请求报文大小

5.2 放大倍数对比表

反射协议 放大倍数 请求类型 请求大小 响应大小 历史事件
Memcached 51,000x GET/STATS 15B ~750KB 2018 GitHub 1.35Tbps
NTP 556x MON_GETLIST 8B ~4.4KB 2014 Cloudflare 400Gbps
DNS 50~100x ANY/TXT查询 ~60B 3~6KB 2013 Spamhaus 300Gbps
CLDAP 56~70x SearchRequest ~64B ~4KB 2019多家企业受害
SNMPv2 650x GetBulkRequest ~80B ~52KB 2020 Twitch 受攻击
SSDP 30x M-SEARCH ~130B ~4KB IoT设备滥用
Chargen 358x 任意字符 1B ~358B 传统服务滥用
QOTD 140x 任意请求 1B ~140B 传统服务滥用
STUN 40x Binding Request ~80B ~3.2KB WebRTC服务滥用
RIPv1 200x Request 24B ~4.8KB 路由协议滥用
NetBIOS 4x Name Service ~80B ~320B 旧版Windows

5.3 Memcached 放大深度分析

2018 年 2 月 28 日,GitHub 遭遇史上最大带宽的 DDoS 攻击,峰值达 1.35 Tbps。攻击利用的是暴露在公网的 Memcached UDP 端口(11211/udp)。

攻击流程:

1. 攻击者向多个 Memcached 反射器发送 SET 命令,写入大体积数据
   SET bigkey 0 0 1000000
   <1MB payload>

2. 攻击者伪造源IP=GitHub,发送 GET bigkey 请求
   请求报文:~15 字节
   
3. Memcached 返回 1MB 响应,放大倍数 ≈ 67000x
   理论极限:单个value 1MB,放大 51000~67000x

4. 多个反射器同时响应,汇聚至 GitHub

防御关键:Memcached 不应暴露在公网,UDP 支持应关闭(-U 0)。

5.4 DNS 放大分析

DNS 放大利用 EDNS0 和 DNSSEC 的大响应特性。攻击者发送 ANYTXT 类型查询(~60 字节),DNS 服务器返回包含完整区域记录的响应(3~6KB),放大 50~100 倍。若利用 DNSSEC 的 RRSIG/NSEC 记录,放大倍数可达 179x。


六、多层级防御体系

6.1 网络层防御

6.1.1 ACL(访问控制列表)

在边缘路由器或防火墙上直接丢弃已知攻击特征流量:

# Cisco IOS ACL 示例:丢弃来自已知攻击源的流量
access-list 110 deny ip host 198.51.100.50 any
access-list 110 permit ip any any

# 丢弃外部发来的 ICMP
access-list 110 deny icmp any any echo

ACL 适合精确封禁,但面对海量伪造源 IP 和反射攻击时效率不足。

6.1.2 uRPF(单播反向路径转发)

uRPF 利用路由表验证报文源 IP 的合法性。如果报文入接口与路由表中到达源 IP 的最佳路径接口不一致,则丢弃该报文。这是对抗源 IP 伪造的基础手段。

# Cisco 配置严格模式 uRPF
interface GigabitEthernet0/0/0
 ip verify unicast source reachable-via rx

严格 uRPF(Strict)要求报文源 IP 的回程路由必须经过当前入接口,适合对称路由环境。宽松 uRPF(Loose)仅检查源 IP 是否在路由表中存在,适合非对称路由(如多线 BGP)环境。

6.1.3 BGP RTBH(Remotely Triggered Black Hole)

RTBH 通过 BGP 向上游运营商宣告一条指向 Null0 的黑洞路由,将攻击流量在上游就近丢弃,避免消耗下游带宽。

# 边缘路由器配置黑洞社区路由
! 定义黑洞路由
ip route 192.0.2.1 255.255.255.255 Null0

! 向上游宣告(使用运营商定义的黑洞社区)
router bgp 65000
 neighbor 10.0.0.1 remote-as 64512
 neighbor 10.0.0.1 route-map RTBH-OUT out
!
route-map RTBH-OUT permit 10
 match ip address prefix-list BLACKHOLE
 set community 64512:666 additive    # 运营商黑洞社区
!
ip prefix-list BLACKHOLE seq 5 permit 192.0.2.1/32

6.1.4 BGP FlowSpec

FlowSpec(RFC 5575)比 RTBH 更精细,可以基于五元组、协议、端口、包长度、TCP 标志等维度匹配流量并执行动作(丢弃、限速、重定向)。

# FRRouting FlowSpec 配置示例:丢弃指向 TCP 80 的 SYN Flood
router bgp 65000
 bgp flowspec validation on
 neighbor 10.0.0.1 flowspec-capability on

# FlowSpec 规则定义(JSON 表示,实际通过 BGP NLRI 传递)
# 规则:匹配 dst=10.0.0.0/24, proto=tcp, dport=80, flags=SYN
# 动作:rate-limit 10000pps
{
  "match": {
    "destination_prefix": "10.0.0.0/24",
    "protocol": "tcp",
    "destination_port": "=80",
    "tcp_flags": "SYN"
  },
  "then": {
    "rate_limit": "10000"
  }
}

# 另一条规则:直接丢弃 Memcached UDP 放大流量
{
  "match": {
    "destination_prefix": "10.0.0.0/24",
    "protocol": "udp",
    "destination_port": "=11211"
  },
  "then": {
    "discard": true
  }
}

6.2 传输层防御

传输层防御的核心是SYN Cookie + 连接限速。SYN Cookie 已在第四节详述,连接限速通过 connlimit 模块实现:

# iptables: 限制单IP并发TCP连接数为50
iptables -A INPUT -p tcp --syn -m connlimit \
  --connlimit-above 50 --connlimit-mask 32 -j DROP

# 限制单IP SYN速率
iptables -A INPUT -p tcp --syn -m limit \
  --limit 20/s --limit-burst 40 -j ACCEPT
iptables -A INPUT -p tcp --syn -j DROP

6.3 清洗中心架构

专业 DDoS 清洗中心通过 Anycast + BGP 引流 + 流量清洗 + 回注 四步实现大规模攻击防御。

graph TB subgraph 攻击流量入口 A[全球攻击流量<br/>SYN Flood + UDP 放大] end subgraph Anycast 引流层 B1[清洗节点A<br/>北京 Anycast IP] B2[清洗节点B<br/>上海 Anycast IP] B3[清洗节点C<br/>法兰克福 Anycast IP] B4[清洗节点D<br/>弗吉尼亚 Anycast IP] end subgraph BGP 引流 C1[BGP 宣告<br/>受害者IP段 Anycast] C2[就近路由<br/>流量按地理位置分散] end subgraph 清洗引擎 D1[特征过滤<br/>FlowSpec/ACL] D2[行为分析<br/>AI异常检测] D3[协议验证<br/>SYN Cookie代理] D4[限速整形<br/>令牌桶] end subgraph 回注通道 E1[GRE Tunnel<br/>清洗后净流量回注] E2[专线路由<br/>MPLS VPN回源] end F[源站服务器<br/>正常业务恢复] A --> B1 & B2 & B3 & B4 B1 & B2 & B3 & B4 --> C1 C1 --> C2 C2 --> D1 --> D2 --> D3 --> D4 D4 --> E1 & E2 E1 & E2 --> F style A fill:#ff6b6b,color:#fff style F fill:#51cf66,color:#fff style D1 fill:#ffd43b style D2 fill:#ffd43b style D3 fill:#ffd43b style D4 fill:#ffd43b

Anycast 架构的核心优势

  1. 就近吸收:全球多个清洗节点宣告相同的 Anycast IP,BGP 最短路径使攻击流量就近进入最近的清洗节点,避免单点带宽瓶颈。
  2. 分布式抗压:100Gbps 攻击分散到 10 个节点后,每个节点仅承受 10Gbps。
  3. BGP 引流:宣告受害者 IP 段的 Anycast 路由,使所有去往受害者的流量(攻击+正常)先到清洗中心。
  4. 回注:清洗后的干净流量通过 GRE 隧道或专用线路回注源站。

6.4 SDN 动态防御

SDN 控制器(如 ONOS、OpenDaylight)通过 OpenFlow 南向协议实时下发流表规则,实现毫秒级动态防御:

# OpenFlow 流表:检测到SYN Flood后动态下发
# 匹配 TCP SYN + 目标端口80,限速到1000pps
ovs-ofctl add-flow br0 \
  "table=0,tcp,tp_dst=80,tcp_flags=SYN,actions=controller"

# 控制器下发限速规则
ovs-ofctl add-flow br0 \
  "table=0,tcp,tp_dst=80,tcp_flags=SYN,actions=group:1000"

# group: 1000 定义为限速桶

七、内网实测——攻击构造与抓包分析

以下命令仅限授权内网测试环境使用。在任何未授权网络上执行均属违法行为。

7.1 环境拓扑

攻击机 (192.168.1.100)  ────  目标机 (192.168.1.200, Nginx:80)
                               抓包机 (192.168.1.150, 镜像口抓包)

7.2 hping3 构造 SYN Flood

# 基础 SYN Flood:伪造随机源IP,洪水模式,目标80端口
hping3 -S --flood -p 80 --rand-source 192.168.1.200

# 控制速率版:10000 SYN/s,便于观察半连接队列变化
hping3 -S -p 80 --rand-source -s 12345 \
  --interval u100 192.168.1.200
# u100 = 每包间隔100微秒 ≈ 10000 pps

# 带数据载荷的SYN Flood(绕过简单SYN检测)
hping3 -S -p 80 --rand-source -d 1200 \
  --flood 192.168.1.200

7.3 目标机观察半连接队列

# 实时统计 SYN_RECV 状态连接数
watch -n 0.5 'ss -ant | grep SYN-RECV | wc -l'

# 预期输出(攻击前)
Every 0.5s: ss -ant | grep SYN-RECV | wc -l
0

# 预期输出(攻击中,syncookies未开启时)
Every 0.5s: ss -ant | grep SYN-RECV | wc -l
2048

# 预期输出(攻击中,syncookies开启后)
Every 0.5s: ss -ant | grep SYN-RECV | wc -l
0     # SYN_RECV 不再堆积,状态编码在ISN中

7.4 tcpdump 抓包分析

# 抓取目标机80端口的TCP SYN报文,限制100包
tcpdump -i eth0 -n -c 100 'tcp[tcpflags] & tcp-syn != 0 and dst port 80'

# 预期输出(正常流量)
14:23:01.123456 IP 192.168.1.50.51012 > 192.168.1.200.80: \
  Flags [S], seq 1234567890, win 64240, options [...], length 0
14:23:01.234567 IP 192.168.1.51.51013 > 192.168.1.200.80: \
  Flags [S], seq 2345678901, win 64240, options [...], length 0

# 预期输出(SYN Flood 攻击)
14:23:05.000001 IP 10.42.1.1.1234 > 192.168.1.200.80: \
  Flags [S], seq 0, win 512, length 0
14:23:05.000002 IP 172.16.5.3.5678 > 192.168.1.200.80: \
  Flags [S], seq 0, win 512, length 0
14:23:05.000003 IP 203.0.113.7.9012 > 192.168.1.200.80: \
  Flags [S], seq 0, win 512, length 0
# 特征:源IP随机、seq=0、无TCP Options、win=512、极高PPS

攻击流量特征分析:

  • seq 全为 0:hping3 默认不设置序列号
  • 无 TCP Options:真实客户端会携带 MSS/SACK/Timestamp/WScale
  • 窗口大小固定:hping3 默认 win=512,远小于真实系统的 64240
  • 源 IP 完全随机:伪造源,无回程路由

7.5 iperf3 测量带宽影响

# 正常基线:客户端到目标机测带宽
iperf3 -c 192.168.1.200 -p 5201 -t 10

# 预期输出(无攻击)
[ ID] Interval           Transfer     Bitrate
[  5] 0.00-10.00 sec  1.10 GBytes   942 Mbits/sec  receiver

# 攻击发起后再次测量
iperf3 -c 192.168.1.200 -p 5201 -t 10

# 预期输出(SYN Flood 进行中,未开启防御)
[ ID] Interval           Transfer     Bitrate
[  5] 0.00-10.00 sec  12.3 MBytes   10.3 Mbits/sec  receiver
# 带宽从 942Mbps 暴跌至 10Mbps,正常连接大量超时失败

# 开启 SYN Cookie 后再次测量
[ ID] Interval           Transfer     Bitrate
[  5] 0.00-10.00 sec  1.05 GBytes   901 Mbits/sec  receiver
# 带宽恢复至 901Mbps,连接成功率恢复正常

八、Linux 内核 SYN Flood 防御参数调优

8.1 完整 sysctl.conf 配置

# ============================================================
# /etc/sysctl.d/99-ddos-defense.conf
# DDoS L3/L4 防御内核参数调优
# ============================================================

# ---------- SYN Flood 防御 ----------

# 开启 SYN Cookie(核心防御,状态编码到ISN)
net.ipv4.tcp_syncookies = 1

# 半连接队列长度(根据内存调整,每条约256字节)
# 65536 条 ≈ 16MB 内存开销
net.ipv4.tcp_max_syn_backlog = 65536

# SYN-ACK 重传次数(默认5次≈180秒,降至2次≈15秒加速回收)
net.ipv4.tcp_synack_retries = 2

# SYN 重传次数(客户端侧,降至3次加速失败)
net.ipv4.tcp_syn_retries = 3

# accept 队列溢出时丢弃而非RST(避免被攻击者探测)
net.ipv4.tcp_abort_on_overflow = 0

# ---------- 连接追踪与状态表 ----------

# conntrack 表大小(每条约300字节,262144条≈75MB)
net.netfilter.nf_conntrack_max = 262144

# conntrack 条目超时优化(加速失效连接回收)
net.netfilter.nf_conntrack_tcp_timeout_syn_recv = 30
net.netfilter.nf_conntrack_tcp_timeout_established = 7200
net.netfilter.nf_conntrack_tcp_timeout_fin_wait = 30
net.netfilter.nf_conntrack_tcp_timeout_time_wait = 60
net.netfilter.nf_conntrack_udp_timeout = 30
net.netfilter.nf_conntrack_icmp_timeout = 10

# ---------- TCP 栈调优 ----------

# TIME_WAIT 状态最大数量
net.ipv4.tcp_max_tw_buckets = 32768

# 开启 TIME_WAIT 快速回收(NAT 环境慎用)
net.ipv4.tcp_tw_reuse = 1
# tcp_tw_recycle 在 4.12+ 内核已移除,NAT 环境有风险

# FIN-WAIT-2 超时(秒)
net.ipv4.tcp_fin_timeout = 15

# Keepalive 探测(快速清理死连接)
net.ipv4.tcp_keepalive_time = 600
net.ipv4.tcp_keepalive_probes = 3
net.ipv4.tcp_keepalive_intvl = 10

# 开启 RST 丢弃(防御 RST 攻击,需配合 conntrack)
# 注意:需在有状态防火墙下使用

# ---------- ICMP 限速 ----------

net.ipv4.icmp_echo_ignore_all = 0
net.ipv4.icmp_echo_ignore_broadcasts = 1
net.ipv4.icmp_ratelimit = 1000
net.ipv4.icmp_ratemask = 88089

# ---------- IP 碎片防御 ----------

# 分片重组内存上限
net.ipv4.ipfrag_high_thresh = 4194304
net.ipv4.ipfrag_low_thresh = 3145728
net.ipv4.ipfrag_time = 30
net.ipv4.ipfrag_max_dist = 64

# ---------- 反向路径校验 ----------

# 所有接口启用严格 uRPF(非对称路由环境改用 loose=2)
# 1 = strict, 2 = loose
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1

# ---------- 源地址验证 ----------

# 禁止源路由(防伪造)
net.ipv4.conf.all.accept_source_route = 0
net.ipv4.conf.default.accept_source_route = 0

# 禁止 ICMP 重定向
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv4.conf.all.secure_redirects = 0
net.ipv4.conf.default.secure_redirects = 0

# 禁止发送 ICMP 重定向
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.send_redirects = 0

# ---------- 日志与探测 ----------

# 记录伪造源地址的数据包
net.ipv4.conf.all.log_martians = 1
net.ipv4.conf.default.log_martians = 1

应用配置:

sysctl -p /etc/sysctl.d/99-ddos-defense.conf

# 验证关键参数
sysctl net.ipv4.tcp_syncookies
sysctl net.ipv4.tcp_max_syn_backlog
sysctl net.ipv4.tcp_synack_retries

九、入侵检测规则——Suricata / Snort

9.1 SYN Flood 检测规则

# ============================================================
# SYN Flood 检测规则
# ============================================================

# 规则1:同一源IP在10秒内发送超过100个SYN包(无对应ACK)
alert tcp $EXTERNAL_NET any -> $HOME_NET any (
  msg:"SYN Flood detected - high SYN rate from single source";
  flags:S,12;
  flow:stateless;
  threshold:type threshold, track by_src, count 100, seconds 10;
  sid:2000001; rev:1;
  classtype:attempted-dos;
  priority:2;
)

# 规则2:SYN包无TCP Options(真实客户端必带MSS)
# hping3 等工具构造的SYN通常不携带Options
alert tcp $EXTERNAL_NET any -> $HOME_NET 80 (
  msg:"SYN packet without TCP options - possible SYN flood tool";
  flags:S,12;
  dsize:0;
  ttl:64-128;
  reference:url,attack.mitre.org/techniques/T1498;
  sid:2000002; rev:1;
  classtype:attempted-dos;
  priority:2;
)

# 规则3:检测异常的窗口大小(hping3默认win=512)
alert tcp $EXTERNAL_NET any -> $HOME_NET any (
  msg:"SYN with anomalous window size 512 - possible hping3";
  flags:S,12;
  window:512;
  sid:2000003; rev:1;
  classtype:attempted-dos;
  priority:3;
)

9.2 UDP 放大攻击检测规则

# ============================================================
# UDP 放大攻击检测规则
# ============================================================

# 规则4:检测入站 Memcached UDP 响应(11211/udp)
# 正常环境不应有外部 Memcached 响应入站
alert udp $EXTERNAL_NET 11211 -> $HOME_NET any (
  msg:"Inbound Memcached UDP response - possible amplification attack";
  dsize:>500;
  sid:2000004; rev:1;
  classtype:attempted-dos;
  priority:1;
)

# 规则5:检测入站 NTP MON_GETLIST 响应(放大攻击)
alert udp $EXTERNAL_NET 123 -> $HOME_NET any (
  msg:"NTP MON_GETLIST response inbound - amplification attack";
  byte_test:1,&,0x17,0;
  dsize:>200;
  sid:2000005; rev:1;
  classtype:attempted-dos;
  priority:1;
)

# 规则6:检测大体积 DNS 响应(ANY查询放大)
alert udp $EXTERNAL_NET 53 -> $HOME_NET any (
  msg:"Large DNS response - possible DNS amplification";
  dsize:>1500;
  byte_test:1,&,0x80,2;
  sid:2000006; rev:1;
  classtype:attempted-dos;
  priority:2;
)

# 规则7:检测 ACK Flood(大量无状态ACK包)
alert tcp $EXTERNAL_NET any -> $HOME_NET any (
  msg:"ACK Flood - high rate of stateless ACK packets";
  flags:A,12;
  flow:stateless;
  threshold:type threshold, track by_src, count 200, seconds 5;
  sid:2000007; rev:1;
  classtype:attempted-dos;
  priority:2;
)

十、BGP FlowSpec 防御规则配置实战

FlowSpec 允许运营商在 BGP 控制平面下发细粒度流量过滤规则,无需逐台路由器配置 ACL,可实现全网联动防御。

10.1 FlowSpec 规则组件

匹配组件 说明 示例
destination_prefix 目的前缀 10.0.0.0/24
source_prefix 源前缀 192.168.0.0/16
ip_protocol IP协议 tcp=6, udp=17
port 源/目的端口 =80
destination_port 目的端口 =11211
icmp_type ICMP类型 =8
packet_length 包长度 >1400
tcp_flags TCP标志 SYN
fragment 分片标记 DF

10.2 实战 FlowSpec 规则集

# ============================================================
# FlowSpec 防御规则集(FRRouting 配置示例)
# ============================================================

router bgp 65000
 bgp flowspec validation on
 neighbor GROUP_EDGE flowspec-capability on
 exit-address-family
!
address-family ipv4 flowspec
 neighbor GROUP_EDGE activate
 exit-address-family

# ---- 规则1:SYN Flood 限速 ----
# 匹配:目的=10.0.0.0/24, TCP, dport=80, flags=SYN
# 动作:限速 10000pps(超限丢弃)
flowspec rule SYN_FLOOD_WEB
 match destination-prefix 10.0.0.0/24
 match protocol tcp
 match destination-port =80
 match tcp-flags SYN
 then rate-limit 10000

# ---- 规则2:Memcached UDP 放大直接丢弃 ----
# 匹配:目的=10.0.0.0/24, UDP, dport=11211
# 动作:丢弃
flowspec rule MEMCACHED_AMP
 match destination-prefix 10.0.0.0/24
 match protocol udp
 match destination-port =11211
 then discard

# ---- 规则3:NTP 放大丢弃 ----
flowspec rule NTP_AMP
 match destination-prefix 10.0.0.0/24
 match protocol udp
 match source-port =123
 match packet-length >200
 then discard

# ---- 规则4:大包 UDP Flood 限速 ----
# 匹配:目的=10.0.0.0/24, UDP, 包长>1000
# 动作:限速 1Gbps
flowspec rule UDP_FLOOD_LARGE
 match destination-prefix 10.0.0.0/24
 match protocol udp
 match packet-length >1000
 then rate-limit 1000000

# ---- 规则5:ICMP Flood 限速 ----
flowspec rule ICMP_FLOOD
 match destination-prefix 10.0.0.0/24
 match protocol icmp
 then rate-limit 100000

# ---- 规则6:IP 碎片攻击丢弃 ----
flowspec rule FRAG_ATTACK
 match destination-prefix 10.0.0.0/24
 match fragment is-fragment
 then discard

FlowSpec 的关键优势在于分布式生效:一条规则通过 BGP 传递到所有上游和对等路由器,在流量汇聚到目标之前就在网络边缘丢弃,从根源上释放带宽。


十一、SDN 动态防御——OpenFlow 实时规则下发

SDN 架构下,控制器持续监控网络流量指标(SYN 速率、新连接速率、异常协议分布),当检测到攻击时自动通过 OpenFlow 下发流表规则,实现秒级响应。

#!/usr/bin/env python3
"""
SDN 控制器动态防御脚本(基于 Ryu 控制器框架)
检测 SYN Flood 并自动下发限速流表
"""

from ryu.base import app_manager
from ryu.controller import ofp_event
from ryu.controller.handler import CONFIG_DISPATCHER, MAIN_DISPATCHER
from ryu.controller.handler import set_ev_cls
from ryu.ofproto import ofproto_v1_3
from collections import defaultdict
import time

class SynFloodDefense(app_manager.RyuApp):
    OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION]

    # 阈值:单IP每秒SYN超过100个视为攻击
    SYN_RATE_THRESHOLD = 100
    # 检测窗口
    WINDOW = 5  # 秒

    def __init__(self, *args, **kwargs):
        super(SynFloodDefense, self).__init__(*args, **kwargs)
        self.syn_counter = defaultdict(int)  # src_ip -> SYN count
        self.blocked_ips = set()
        self.window_start = time.time()

    @set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER)
    def features_handler(self, ev):
        """交换机连接时下发默认流表:SYN包上送控制器"""
        datapath = ev.msg.datapath
        parser = datapath.ofproto_parser

        # Table 0: 匹配TCP SYN包,上送控制器
        match = parser.OFPMatch(
            eth_type=0x0800, ip_proto=6,
            tcp_flags=0x02  # SYN flag
        )
        actions = [parser.OFPActionOutput(datapath.ofproto.OFPP_CONTROLLER)]
        self.add_flow(datapath, 10, match, actions)

        # 默认流表:正常转发
        match = parser.OFPMatch()
        actions = [parser.OFPActionOutput(datapath.ofproto.OFPP_NORMAL)]
        self.add_flow(datapath, 0, match, actions)

    @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)
    def packet_in_handler(self, ev):
        """收到上送的SYN包,进行速率统计"""
        msg = ev.msg
        datapath = msg.datapath
        parser = datapath.ofproto_parser

        # 解析源IP
        pkt = msg.data
        # 简化:从原始报文中提取源IP(实际用 ryu.lib.packet 解析)
        src_ip = self._extract_src_ip(pkt)
        if not src_ip:
            return

        # 检查是否已被封禁
        if src_ip in self.blocked_ips:
            return

        # 计数
        self.syn_counter[src_ip] += 1

        # 检测窗口到期
        now = time.time()
        if now - self.window_start >= self.WINDOW:
            for ip, count in self.syn_counter.items():
                rate = count / self.WINDOW
                if rate > self.SYN_RATE_THRESHOLD:
                    self.logger.warning(
                        f"[ALERT] SYN Flood detected: {ip} "
                        f"rate={rate:.0f} SYN/s, blocking..."
                    )
                    self._block_ip(datapath, ip)
                    self.blocked_ips.add(ip)

            # 重置计数器
            self.syn_counter.clear()
            self.window_start = now

    def _block_ip(self, datapath, src_ip):
        """下发丢弃流表,封禁攻击源"""
        parser = datapath.ofproto_parser
        match = parser.OFPMatch(
            eth_type=0x0800, ipv4_src=src_ip, ip_proto=6,
            tcp_flags=0x02
        )
        # 空动作 = 丢弃
        self.add_flow(datapath, 100, match, [])

    def add_flow(self, datapath, priority, match, actions):
        """下发流表项"""
        parser = datapath.ofproto_parser
        ofp = datapath.ofproto

        inst = [parser.OFPInstructionActions(
            ofp.OFPIT_APPLY_ACTIONS, actions
        )]
        mod = parser.OFPFlowMod(
            datapath=datapath, priority=priority,
            match=match, instructions=inst
        )
        datapath.send_msg(mod)

    def _extract_src_ip(self, raw_pkt):
        """从原始以太网帧中提取源IP(简化版)"""
        # 以太网头14字节 + IP头前12字节到达源IP
        if len(raw_pkt) < 26:
            return None
        ip_header = raw_pkt[14:]
        if ip_header[0] >> 4 != 4:  # 非IPv4
            return None
        src_ip_bytes = ip_header[12:16]
        return '.'.join(str(b) for b in src_ip_bytes)

十二、防御体系总结与选型矩阵

12.1 防御层次对照表

攻击类型 首选防御 补充防御 适用场景
SYN Flood SYN Cookie FlowSpec限速 + 半连接队列调优 单机/集群
ACK Flood conntrack状态过滤 FlowSpec TCP标志限速 有状态防火墙
TCP连接耗尽 connlimit + 应用层超时 连接配额 + WAF行为分析 Web服务器
ICMP Flood icmp_ratelimit ACL + FlowSpec丢弃 边缘路由器
UDP Flood uRPF + ACL FlowSpec限速 + 应用层验证 边缘/清洗中心
UDP放大 关闭反射器公网暴露 FlowSpec丢弃 + Anycast清洗 全网联动
IP碎片攻击 ipfrag限制 FlowSpec分片匹配丢弃 内核/路由器

12.2 防御纵深架构

完整的生产级防御体系应构建为纵深防御:

[上游运营商 FlowSpec/RTBH]
         ↓ 过滤已知攻击特征
[Anycast 清洗中心] ← 全局引流 + 流量清洗
         ↓ 净流量回注
[边缘路由器 uRPF/ACL] ← 源地址验证
         ↓
[防火墙 conntrack/限速] ← 状态检测 + 连接限速
         ↓
[主机内核 SYN Cookie/sysctl] ← 协议栈加固
         ↓
[应用层 WAF/连接配额] ← 业务逻辑防护

每一层过滤不同维度的攻击流量,纵深叠加后即使单层被突破,后续层仍能提供保护。SYN Cookie 防御 SYN Flood,uRPF 防御伪造源,FlowSpec 在网络边缘丢弃放大流量,Anycast 清洗中心吸收超大带宽攻击——各层各司其职,构成完整的 L3/L4 攻防体系。


十三、结语

L3/L4 直接流量攻击的本质是协议设计缺陷与资源不对称的利用:TCP 三次握手的资源预分配成就了 SYN Flood,UDP 的无连接特性催生了反射放大。防御的核心不在于单一技术银弹,而在于理解每一层攻击的协议机理后,针对性地构建纵深防御体系。

从内核参数调优(sysctl)到协议状态编码(SYN Cookie),从单机连接限速到全网 BGP FlowSpec 联动,从有状态检测到 Anycast 清洗中心分布式吸收——每一环都是攻防博弈中不可或缺的一层。在 Tbps 级攻击已成常态的今天,只有将协议理解、内核调优、网络架构与自动化检测(SDN/IDS)深度融合,才能在 L3/L4 攻防实战中立于不败之地。


本文涉及的所有攻击命令仅供授权安全测试参考,未经授权在网络环境中执行 DDoS 攻击属于违法行为。