Section outline

    • 把 zip 文件下载到 VM 里, 然后用 unzip 命令解压缩。

  •  
    SYN 泛洪是一种 DoS 攻击的形式,攻击者向受害者的 TCP 端口发送许多 SYN 请求,但并不完成三次握手。攻击者要么使用仿冒的IP地址,要么不再继续握手的过程。通过这种攻击,攻击者可以将受害者的半连接队列塞满。半连接指的是已经完成 SYN、SYN-ACK,但还没有得到最终的ACK的连接。当这个队列满了之后,受害者没法接受新的连接请求。下图展示了该攻击过程。
     
    TCP 泛洪攻击

    半连接队列的大小是操作系统里的一个设置。在Ubuntu操作系统中,我们可以通过以下命令检查这个设置。操作系统根据系统的内存大小设置该值,内存越多,值就越大。

    # sysctl net.ipv4.tcp_max_syn_backlog
    net.ipv4.tcp_max_syn_backlog = 128

    我们可以用 "netstat -nat" 查看队列的使用情况, 也就是半连接的数量。这种连接的状态是 SYN-RECV。如果三次握手已经完成,连接的状态将会是 ESTABLISHED。

    SYN Cookie 防御机制。在默认情况下,Ubuntu 的 SYN 泛洪攻击的防御机制是打开的。这个机制被称为 SYN cookie。一旦机器检测到自己在遭受 SYN 泛洪攻击,这立即启动这个防御机制。在我们的受害者容器中,我们已经关闭了这一机制(见  docker-compose.yml 文件中的 sysctls 条目)。我们可以使用下面的 sysctl 命令来开关这一机制:

    # sysctl -a | grep syncookies     (显示 SYN cookie 状态)
    # sysctl -w net.ipv4.tcp_syncookies=0 (关闭 SYN cookie)
    # sysctl -w net.ipv4.tcp_syncookies=1 (打开 SYN cookie)

    为了能在容器中使用 sysctl 改变系统变量,需要将"privileged: true"。这个配置条目添加到受害者容器中。如果没有这项配置,运行上述命令后会看到如下错误,因为容器没有权限进行修改操作。

    # sysctl -w net.ipv4.tcp_syncookies=1
    sysctl: setting key "net.ipv4.tcp_syncookies": Read-only file system
  • 我们提供了一个名为 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 之间的速度竞争。

  • 除了TCP缓存问题外,任务1中提到的所有问题都可以通过以足够快的速度发送伪造的 SYN 数据包而解决。我们可以通过用C语言实现这一目的,实验设置中提供了名为 synflood.c 的 C 程序。请在主机上编译该程序,并在攻击者容器上向目标机器发起攻击。
    // 在宿主机虚拟机上编译代码。
    $ gcc -o synflood synflood.c
    
    // 对于使用苹果芯片的机器,需要使用静态绑定。
    $ gcc -static -o synflood synflood.c
    
    // 从攻击者容器发起攻击。
    # synflood 10.9.0.5 23

    在发起攻击之前,请将队列大小恢复至原始值。将结果与使用 Python 的结果进行比较,并解释其差异的原因。
  • 防止 SYN 泛洪攻击的一个有效方法是使用一个叫 SYN cookies 的技术。在上面的几个任务里,SYN cookie 机制被关闭了。在这个任务里,请启用 SYN cookie 机制(命令如下),然后重新做任务 2(用 C 程序进行攻击),请描述你观察到的结果。
    sysctl -w net.ipv4.tcp_syncookies=1 (打开 SYN cookie)
     
  • 你需要提交一份带有截图的详细实验报告来描述你所做的工作和你观察到的现象。你还需要对一些有趣或令人惊讶的观察结果进行解释。请同时列出重要的代码段并附上解释。只是简单地附上代码不加以解释不会获得学分。实验报告的提交方式会由你的老师进行具体安排。