网络攻防技术-嗅探与欺骗实验(Packet Sniffing and Spoofing Lab)
1 作业题目
包嗅探和欺骗是网络安全中的两个重要概念;它们是网络通信中的两大威胁。能够理解这两种威胁对于理解网络中的安全措施至关重要。有许多包嗅探和欺骗工具,如Wireshark、Tcpdump、Netwox等。其中一些工具被安全专家以及攻击者广泛使用。能够使用这些工具对学生来说很重要,但对于网络安全课程的学生来说,更重要的是了解这些工具是如何工作的,即包嗅探和欺骗是如何在软件中实现的。
本实验的目标是让学生掌握大多数嗅探和欺骗工具的基本技术。学生们将使用一些简单的嗅探和欺骗程序,阅读它们的源代码,修改它们,并最终对这些程序的技术方面有深入的了解。在本实验结束时,学生应该能够编写自己的嗅探和欺骗程序。
2 实验步骤及结果
Lab Task Set 1: Using Scapy to Sniff and Spoof Packets
Task 1.1: Sniffing Packets
Task 1.1A
使用dcbuild与dcup命令构建并启动容器。

这将创建这样一个网络拓扑结构

使用如下命令安装scapy

最简单的一个嗅探代码,使用了Scapy库来进行数据包嗅探,并指定了过滤条件为仅捕获ICMP类型的数据包。利用ifconfig得到接口名:


运行示例代码,使用下面的命令向目标主机发送Ping报文,-I br-7fea39530383 参数指定使用网络接口br-7fea39530383 来发送ping请求。

网卡捕获的数据包如下:

Task 1.1B
• Capture only the ICMP packet
只捕捉ICMP数据包,代码修改如下:

手动PING

运行结果如下,可以看到捕获到了ICMP数据包:

• Capture any TCP packet that comes from a particular IP and with a destination port number 23.
只捕捉来自特定IP,且目标端口号为23的TCP数据包,查看自己的IP地址如下:

本机ip地址:10.0.2.1
设定一个ip地址:10.0.2.2
创建发送数据包的代码:datapak.py,并指定tcp端口是23
修改sniffer.py文件:

运行datapak.py

捕获了前三次发的包

• Capture packets comes from or to go to a particular subnet. You can pick any subnet, such as 128.230.0.0/16; you should not pick the subnet that your VM is attached to.
修改嗅探的过滤条件:

修改datapak.py文件

运行datapak.py文件后嗅探结果如下:

Task 1.2: Spoofing ICMP Packets
安装wireshark:

root权限下启动wireshark,以忽略报错

准备抓包

运行发包程序


发现收到报文

Task 1.3: Traceroute
traceroute是一种网络诊断工具,用于确定数据包从源主机到目标主机的路径。它通过发送一系列的数据包,并观察每个数据包经过的路由器,从而揭示了数据包在网络中的传输路径。
traceroute的工作原理如下:
- 源主机发送第一个数据包到目标主机,并将其生存时间(TTL)字段设置为1。这个数据包到达第一个路由器后,由于TTL的限制,该路由器会将其丢弃,并向源主机发送一个ICMP “Time Exceeded”错误消息。
- 源主机接收到ICMP错误消息后,记录下第一个路由器的IP地址。这样就确定了路径中的第一个跳。
- 源主机增加TTL字段的值,将第二个数据包发送到目标主机。这个数据包到达第二个路由器后,同样由于TTL的限制,该路由器会将其丢弃,并向源主机发送一个ICMP Time Exceeded错误消息。
- 源主机接收到第二个ICMP错误消息后,记录下第二个路由器的IP地址。这样就确定了路径中的第二个跳。
- 源主机不断增加TTL字段的值,重复上述过程,直到数据包到达目标主机。每经过一个路由器,就记录下该路由器的IP地址。
- 当数据包到达目标主机后,目标主机会向源主机发送一个ICMP Echo Reply消息,表示数据包已成功到达。
通过重复上述过程,traceroute就能够逐步确定数据包经过的路由器,并测量每个跳的往返时间(RTT)。通过分析这些信息,可以获得从源主机到目标主机的完整路径以及每个跳的延迟。
创建trace.py文件,模拟一个traceroute,循环每次TTL+1,中间节点都发回ICMP TTL字段过期的错误信息,目的节点发回ICMP reply就结束:

运行结果如下:

Task 1.4: Sniffing and-then Spoofing
要求我们编写一个程序,嗅探局域网中的所有ICMP回显请求(不论目标主机是不是我们本身),然后对发送ICMP回显的主机发送一个ICMP回显回复进行ICMP欺骗。
首先测试ping以下三个地址,前两个无法ping通,最后一个可以ping通。

编写下面的程序并运行

当ping其他主机时,结果如下所示。ttl=64的数据包为我们伪造的数据包,显示truncated表示输出被截断。(DUP!)的出现表示某些ICMP回显请求数据包被重复接收了多次。

Lab Task Set 2: Writing Programs to Sniff and Spoof Packets
Task 2.1: Writing Packet Sniffing Program
Task 2.1A: Understanding How a Sniffer Works
使用pcap库进行网络嗅探。回调函数got_packet,用于处理捕获到的数据包。在main函数中,打开了一个网络接口的pcap会话,指定了接口名为enp0s3。编译了一个BPF过滤器,只捕获协议类型为ICMP的IP数据包。代码如下:
#include <pcap.h> #include <stdio.h> #include <arpa/inet.h> /* Ethernet header */ struct ethheader { u_char ether_dhost[6]; /* destination host address */ u_char ether_shost[6]; /* source host address */ u_short ether_type; /* protocol type (IP, ARP, RARP, etc) */ }; /* IP Header */ struct ipheader { unsigned char iph_ihl:4, //IP header length iph_ver:4; //IP version unsigned char iph_tos; //Type of service unsigned short int iph_len; //IP Packet length (data + header) unsigned short int iph_ident; //Identification unsigned short int iph_flag:3, //Fragmentation flags iph_offset:13; //Flags offset unsigned char iph_ttl; //Time to Live unsigned char iph_protocol; //Protocol type unsigned short int iph_chksum; //IP datagram checksum struct in_addr iph_sourceip; //Source IP address struct in_addr iph_destip; //Destination IP address }; void got_packet(u_char *args, const struct pcap_pkthdr *header, const u_char *packet) { struct ethheader *eth = (struct ethheader *)packet; if (ntohs(eth->ether_type) == 0x0800) { // 0x0800 is IP type struct ipheader * ip = (struct ipheader *) (packet + sizeof(struct ethheader)); printf(" From: %s\n", inet_ntoa(ip->iph_sourceip)); printf(" To: %s\n", inet_ntoa(ip->iph_destip)); /* determine protocol */ switch(ip->iph_protocol) { case IPPROTO_TCP: printf(" Protocol: TCP\n\n"); return; case IPPROTO_UDP: printf(" Protocol: UDP\n\n"); return; case IPPROTO_ICMP: printf(" Protocol: ICMP\n\n"); return; default: printf(" Protocol: others\n\n"); return; } } } /*1.pcap_lookupdev():查找可用的网络接口。(可选) 2.pcap_open_live():打开选择的网络接口以进行数据包捕获。 3.pcap_compile():编译BPF(Berkeley Packet Filter)过滤器,用于选择特定类型的数据包。 4.pcap_setfilter():设置捕获过滤器,以便仅捕获满足特定条件的数据包。 5.pcap_loop():开始捕获数据包的循环。 6.在循环中,对每个捕获到的数据包,使用自定义的回调函数进行处理。 7.pcap_close():关闭捕获会话,释放资源。 创建捕获数据包的句柄。这个句柄包含了与捕获会话相关的信息和状态,如网络接口、捕获过滤器等。 struct bpf_program用于表示编译后的BPF(Berkeley Packet Filter)过滤器程序。BPF过滤器是一种用于选择特定类型的数据包的过滤器,可以用于在数据包捕获过程中进行数据包过滤*/ int main() { pcap_t *handle; char errbuf[PCAP_ERRBUF_SIZE]; struct bpf_program fp; char filter_exp[] = "tcp and dst portrange 10-100"; bpf_u_int32 net; // Step 1: Open live pcap session on NIC with name ens33 /*ens33为自己选定的接口名称,可替换; BUFSIZ:捕获数据包时使用的缓冲区大小。BUFSIZ是一个预定义的常量,表示缓冲区的大小。 1000:设置超时值,指定在捕获数据包时等待的最长时间(以毫秒为单位)。 errbuf:用于存储错误消息的缓冲区。如果在打开网络接口时发生错误,错误消息将被写入到errbuf中。*/ handle = pcap_open_live("ens33", BUFSIZ, 1, 1000, errbuf); printf("listening on network card, ret: %p...\n", handle); // Step 2: Compile filter_exp into BPF psuedo-code /*使用pcap_compile编译过滤器表达式filter_exp。handle是之前打开的捕获会话句柄,&fp是指向struct bpf_program的指针,用于存储编译后的过滤器程序。filter_exp是一个字符串,表示要应用于数据包捕获的过滤条件。0表示不使用优化选项,net是一个网络地址,用于确定过滤器表达式中的网络地址。pcap_setfilter(handle, &fp)将编译后的过滤器程序fp应用于捕获会话。*/ printf("try to compile filter...\n"); pcap_compile(handle, &fp, filter_exp, 0, net); printf("try to set filter...\n"); pcap_setfilter(handle, &fp); // Step 3: Capture packets /*使用pcap_loop开始捕获,got_packet是自定义的回调函数。-1表示捕获无限数量的数据包,也可以指定一个正整数来限制捕获的数据包数量。pcap_close用于关闭捕获会话句柄,释放资源。*/ printf("start to sniff...\n"); pcap_loop(handle, -1, got_packet, NULL); pcap_close(handle); //Close the handle return 0; }
编译运行结果如下:

Q1: 描述在你的嗅探程序中的库函数的调用
A1: 第一步,启动pcap监听网卡。
第二步,编译BPF过滤器并设置过滤器。
第三步,设置嗅探的处理函数。
最后,关闭嗅探即可。
Q2: 为什么需要root权限才能运行嗅探程序?不使用root权限运行该程序会在哪里报错?
A2: 嗅探数据包是一个高权限的操作,因为涉及隐私,安全相关问题。如果普通用户也能嗅探数据包,那么他就能窃取别人的隐私,甚至盗取账号密码等等。不使用root权限运行该程序。对比如下,可以看到在没有权限时第一步监听网卡就失败了。
Q3: 打开嗅探程序的混杂模式。打开和关闭这个模式有什么区别?
A3: 使用混杂模式可以监听所在网段下其他机器的数据包,关闭则不能。打开混杂模式,可以监听到本网段的机器 ping baidu.com的数据包,关闭后则嗅探不到。
Task 2.1B: Writing Filters
这部分主要是写一些过滤器。这部分还是复用 task 2.1A的代码,只是修改其中的过滤器而已。Pcap过滤器的例子:
• Capture the ICMP packets between two specific hosts.
只捕捉两个特定主机之间的ICMP包,使用的过滤器为 icmp and src hostand dst host 10.0.2.4, 只捕捉从 发送到 10.0.2.4的ICMP包。
这边改成了百度的IP:

结果如下,可以看到全是从百度IP发送到 10.0.2.4的ICMP包,没有其他类型的包。


• Capture the TCP packets with a destination port number in the range from 10 to 100.
将代码修改回去,可捕捉目的端口在10到100之间的TCP包:

运行的结果如下,用浏览器访问baidu.com的包,可以看到没有别的类型的包:

Task 2.1C: Sniffing Passwords
·Telnet传输的命令和数据都是明文,可以被任何中间人轻松拦截和审查。
·用户名、密码和交互内容均不加密,可被截取使用。
·没有身份验证机制,任何人都可以假冒其他用户登入。
·数据在传输过程中可被篡改,无法完整校验。
进入一个docker创建的虚拟host——10.9.0.5中

然后编写下面的代码,运行在IP地址为10.9.0.1的网络接口上,监听目的端口为23的TCP报文(telnet使用23端口)

在docker容器中尝试远程登陆另一台主机10.9.0.6

观察嗅探程序的结果,在10.9.0.1上运行的嗅探程序嗅探到了10.9.0.5的登录到10.9.0.6的数据报文,获得了登陆密码。

Task 2.2: Spoofing
myheader.h(太长了,详细见代码)

checksum.c

Task 2.2A: Write a spoofing program
这部分主要是伪造IP包。这里伪造是UDP包, 代码如下:
spoof.c

task22A

编译运行,查看后台wireshark,可以看到我们伪造的UDP包


Task 2.2B: Spoof an ICMP Echo Request
这部分是伪造ICMP Echo请求。伪造的代码如下, 其中源IP10.0.2.5是局域网内另一个虚拟机的IP。

使用gcc -o task22B task22B.c spoof.c checksum.c -lpcap 进行编译, sudo ./task22B运行,查看后台的wireshark,如下:
可以看到我们发送的源IP为10.0.2.5, 目的IP为8.8.8.8的ICMP包,并且还有回复

Q4: 能把IP包的长度设置为任意数值,而不管实际的包的大小吗?
A4: 将代码中设置长度该成下面的代码,运行可知其为28B,修改长度为10B,wireshark没有捕捉到包,说明没有发出去;修改成1000B,可以正常发送出去,且收到了相应。说明可以调大length,不能调小length。
Q5: 使用 raw socket 编程, 我们要计算IP头部的checksum吗?
A5: 不用计算IP头部的checksum,但是需要计算ICMP头部的checksum。
Q6: 为什么使用raw socket 编程需要root权限?没有root权限执行时程序会在哪里报错?
A6:因为能任意读取发送包意味着很大的安全风险,所以需要root权限。使用普通权限运行结果如下:

Task 2.3: Sniff and then Spoof
准备两个在同一个局域网的虚拟机,这部分主要是同时嗅探和伪造包,实现一个机器ping任意IP x,另一个机器伪造ICMP回复请求,使得其有回复,而IP x所对应的机器可能根本不存在。
代码如下task23.c

使用 gcc -o task23 task23.c checksum.c spoof.c -lpcap编译程序,sudo ./task23运行后去ping 1.1.1.1

使用另一台机器ping 1.1.1.1,此机器运行上面的程序,结果如下:

 
                     
                    
                 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号