网络攻防技术-嗅探与欺骗实验(Packet Sniffing and Spoofing Lab)

作业题目

包嗅探和欺骗是网络安全中的两个重要概念;它们是网络通信中的两大威胁。能够理解这两种威胁对于理解网络中的安全措施至关重要。有许多包嗅探和欺骗工具,如Wireshark、Tcpdump、Netwox等。其中一些工具被安全专家以及攻击者广泛使用。能够使用这些工具对学生来说很重要,但对于网络安全课程的学生来说,更重要的是了解这些工具是如何工作的,即包嗅探和欺骗是如何在软件中实现的。

本实验的目标是让学生掌握大多数嗅探和欺骗工具的基本技术。学生们将使用一些简单的嗅探和欺骗程序,阅读它们的源代码,修改它们,并最终对这些程序的技术方面有深入的了解。在本实验结束时,学生应该能够编写自己的嗅探和欺骗程序。

 

 

实验步骤及结果

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的工作原理如下:

  1. 源主机发送第一个数据包到目标主机,并将其生存时间(TTL)字段设置为1。这个数据包到达第一个路由器后,由于TTL的限制,该路由器会将其丢弃,并向源主机发送一个ICMP “Time Exceeded”错误消息。
  2. 源主机接收到ICMP错误消息后,记录下第一个路由器的IP地址。这样就确定了路径中的第一个跳。
  3. 源主机增加TTL字段的值,将第二个数据包发送到目标主机。这个数据包到达第二个路由器后,同样由于TTL的限制,该路由器会将其丢弃,并向源主机发送一个ICMP Time Exceeded错误消息。
  4. 源主机接收到第二个ICMP错误消息后,记录下第二个路由器的IP地址。这样就确定了路径中的第二个跳。
  5. 源主机不断增加TTL字段的值,重复上述过程,直到数据包到达目标主机。每经过一个路由器,就记录下该路由器的IP地址。
  6. 当数据包到达目标主机后,目标主机会向源主机发送一个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,此机器运行上面的程序,结果如下:

 

 

posted @ 2025-04-29 12:07  Antoniiiia  阅读(171)  评论(0)    收藏  举报