网络攻防--嗅探与欺骗

一、实验内容

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

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

二、实验步骤与结果

一)、Lab Task Set 1: Using Scapy to Sniff and Spoof Packets

使用python,通过简单的测试,构造了一个IP数据包并且打印出了其中的信息。

Task 1.1: Sniffifing Packets

首先获取网络接口名称。因为我们使用组合文件创建了容器,所以将会创建一个新的网络接口用于链接虚拟机和容器。

Task1.1a

执行python编写的嗅探代码。

并且在另一个控制台使用ping发出icmp数据包。

使用chmod a+x sniffer.py赋予程序执行权限,运行程序。可以看到程序已经成功嗅探到了数据包。

注意,如果不使用sudo执行程序的话,将会出现下面的报错。说明嗅探需要高权限,如果没有高权限,将禁止嗅探。

Task1.1b

可以在嗅探程序中设置过滤器来筛选需要嗅探的数据包。只返回ICMP

1.只嗅探ICMP数据包。

结果如下,可以看到嗅探到的数据包都是ICMP。

2.指定IP为10.9.0.1和端口号为23。并编写一个python代码发送一个符合要求的包。

3.指定子网128.230.0.0/16。

发送数据包。

可以看到成功嗅探到也只嗅探到该子网的数据包。

Task1.2: Spoofifing ICMP Packets

使用欺骗ICMP包来请求回复。

可以在我们的嗅探程序中发现我们发送的请求被人接受,然后对方发出ARP请求。

Task1.3: Traceroute

使用Scapy来估计虚拟机和选定目的地之间的路由器数量方面的距离。代码如下:

发送我们的数据包。

在wireshark中,我们可以看到不同的路由器返回的包,可以通过这些包来判断路由器的数量。

Task1.4: Sniffifing and-then Spoofifing

在同一个局域网下的两个机器,其中一个机器使用ping指令,另一个机器只要检测到ICMP请求,不管对方的目标IP是多少,立刻回复。这样,不管ping的机器是不是打开的,都会收到回复。

编写代码如下:

1.ping baidu.com

先在一个控制台ping baidu.com,可以看到,发送回来的ICMP包ttl都是49。

运行程序,开始嗅探以及欺骗。

此时再使用ping baidu.com 指令。可以看到收到了ttl为64的包,这就是我们发送的包。

2.Ping 8.8.8.8

运行嗅探和欺骗程序。

可以看到ttl为64的ICMP包就是我们发送的包。

二)、Lab Task Set 2: Writing Programs to Sniff and Spoof Packets

Task 2.1: Writing Packet Sniffifing Program

根据实验手册,使用C语言编写的嗅探程序如下:

编译运行之后,使用ping baidu.com指令进行测试。

为程序附加执行权限后,使用root权限运行程序,可以看到成功嗅探到包。

2.1a:Understanding How a Sniffer Works

问题1:概括函数调用顺序用意。

首先handle使用pcap_open_live打开一个活跃监听句柄,其次利用pcap_compile在监听句柄中编译一个过滤器语法,并在编译错误时结束程序;如果编译通过,则开始监听,直至程序退出。

问题2:为什么需要root权限?

因为如果任意权限用户都能够监听,将会导致安全危机与隐私泄露。当没有root权限时,进程将由于没有监听网络区域的存储器的访问权限,出现权限冲突而退出。

问题3:测试在关闭和打开混杂模式的情况下有什么不同。

测试思路:使用10.9.0.5向10.9.0.6发送ping报文,并在主机或10.9.0.1使用程序进行监听。若开启混杂则应收到,否则没有响应。

进入10.9.0.5,并且使用ping 10.9.0.6。

首先打开混杂模式,可以看到成功嗅探到数据包。然后关闭混杂模式,可以看到此时无法嗅探到数据包。

Task2.1b:: Writing Filters.

编写如下代码,需要再执行程序之后根据提示输入你的筛选条件。

1.icmp and dst host 10.9.0.5 and src host 10.9.0.6

2.tcp and dst portrange 10-100。此处使用远程连接登录通过23号端口发送数据包。

task2.1c: Sniffing Passwords

观察wireshark中接受到的包发现,数据包携带的信息都从数据包的第66位开始,所以我们这里选择直接输出66位的信息。

运行代码。

使用远程连接登录,并且输入用户名和密码

可以看到,成功打印除了用户的名称和密码。

代码:

#include <pcap.h>
#include <stdio.h>
#include <stdlib.h>

void got_packet(u_char *args, const struct pcap_pkthdr *header, const u_char *packet)
{
	printf("Got a packet. Dta:");
	printf("\t%s\n",(char *)(packet+66));
}
int main()
{
	pcap_t *handle;
	char errbuf[PCAP_ERRBUF_SIZE];
	struct bpf_program fp;
	printf("type in your filter:\n");
	char filter_exp[100] ={0};
	fgets(filter_exp,100,stdin);
	int a=sizeof(filter_exp);
	filter_exp[a-1]='\0';
	printf("your filter is:\n");
	printf("%s\n",filter_exp);
	bpf_u_int32 net;
	
	handle = pcap_open_live("br-fec681197df8", BUFSIZ, 1, 1000, errbuf);
	
	pcap_compile(handle, &fp, filter_exp, 0, net);
	if (pcap_setfilter(handle, &fp) !=0) {
		pcap_perror(handle, "Error:");
		exit(EXIT_FAILURE);
	}
	
	pcap_loop(handle, -1, got_packet, NULL);
	pcap_close(handle); //Close the handle
	return 0;
}


Task2.2A: Write a spoofing program.

编写程序当程序抓到包时,发送自己编写的虚假的ICMP请求信息。

再wireshark中抓包,可以看到,成功发送。说明程序可以嗅探到包,也可以成功发送虚假的ICMP包。

Task 2.2B: Spoof an ICMP Echo Request.

编写代码,发送100个虚假的ICMP请求报文信息。

在10.9.0.1运行代码,发送虚假的源IP为 10.9.0.5,目的IP为8.8.8.8的icmp请求包。

使用wireshark抓包,可以看到,发送到8.8.8.8的包的源IP变成了10.0.2.4,说明原来的包在经过主虚拟机之后,源IP被修改。同时8.8.8.8发送的回复包的目的IP为10.9.0.5,说明我们欺骗成功。

问题4:可以将IP数据包的长度字段设置为任意值吗?

确实可以随机设置IP报文头中的报文长度参数,但必须保证向socket发送的原始报文是正确的长度,这样当下一条进行转发时会将错误的长度进行修正(即根据我们现有的报文写一个新的发送出去)。如果长度参数与我们发送的大小一致,则可能发送出去报文之后检验和不匹配,无法收到回显报文。

问题5:使用原始套接字必须计算IP报头的校验值吗?

是的,必须计算检验和,否则默认填写0x00,在终点服务器会检验不通过直接丢弃该报文。导致我们无法成功将构造的报文发送出去。

问题6:为什么需要使用root权限运行原始套接字?没有root权限会在哪里失败?

不允许无权限的用户执行发送、接收报文的操作。

Task 2.3: Sniff and then Spoof.

修改之前的欺骗代码,将发送的欺骗包的源IP修改为抓到的数据包的目的IP,将发送的欺骗包的目的IP修改为抓到的数据包的源IP。再将数据包发送出去。

在10.9.0.1上ping 10.9.0.6

启动我们的抓包-欺骗代码。

使用wireshark在any抓包,观察得到的ICMP包,可以观察到带有我们编写的特征的ICMP回复包,并且源IP和目的IP与请求包相对应,说明欺骗成功。

代码参考:(我记得我是参考了别人的代码,但是我现在找不到出处了TvT)
#include <pcap.h>
#include <stdio.h>
#include <stdlib.h>
#include<string.h>
#include<netinet/in.h>
#include<time.h>
#include <signal.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/timerfd.h>
#include <unistd.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <netdb.h>
#include <errno.h>
#include <malloc.h>
#include <stdint.h>
#include <pthread.h>
#include <arpa/inet.h>
#include <asm/byteorder.h>

#define BYTE u_char
#define DWORD u_long
#define USHORT u_short

//IP报头
typedef struct IP_HEADER {
#if defined(__LITTLE_ENDIAN_BITFIELD)
	__u8	ihl:4,			//4位头部长度 一位4个字节,,最多15*4个字节(可选项)
		version:4;		//4位版本号
#elif defined (__BIG_ENDIAN_BITFIELD)
	__u8	version:4,
  		ihl:4;
#else
#error	"Please fix <asm/byteorder.h>"
#endif
	__u8	tos;			//8位服务类型
	__be16	tot_len;		//16位总长度
	__be16	id;			//16位标识符
	__be16	frag_off;		//3位标志加13位片偏移
	__u8	ttl;			//8位生存时间
	__u8	protocol;		//8位上层协议号
	__sum16	check;		//16位校验和
	__be32	saddr;			//32位源IP地址
	__be32	daddr;			//32位目的IP地址
	/*The options start here. */
} IP_HEADER;

//ICMP报头
typedef struct ICMP_HEADER
{
    u_char type;    //8位类型字段
    u_char code;    //8位代码字段
    u_short cksum; //16位校验和
    u_short id;    //16位标识符
    u_short seq;   //16位序列号
} ICMP_HEADER;

//计算网际校验和函数
u_short checksum(u_short* pBuf, int iSize)
{
    unsigned long cksum = 0;
    while (iSize > 1)
    {
        cksum += *pBuf++;
        iSize -= sizeof(u_short);
    }
    if (iSize)//如果 iSize 为正,即为奇数个字节
    {
        cksum += *(u_char*)pBuf; //则在末尾补上一个字节,使之有偶数个字节
    }
    cksum = (cksum >> 16) + (cksum & 0xffff);
    cksum += (cksum >> 16);
    return (u_short)(~cksum);
}


int main()
{
	int i=0;
	
	for(i=0;i<100;i++)
	{
		int sd;
	struct sockaddr_in sin;
	char buffer[1024]; // You can change the buffer size
	/* Create a raw socket with IP protocol. The IPPROTO_RAW parameter
	 * tells the sytem that the IP header is already included;
	 * this prevents the OS from adding another IP header. */
	
	//构造ICMP回显请求消息,并以TTL递增的顺序发送报文
	//ICMP类型字段
	const BYTE ICMP_ECHO_REQUEST = 8;    //请求回显
	const BYTE ICMP_ECHO_REPLY = 0;    //回显应答
	const BYTE ICMP_TIMEOUT = 11;   //传输超时

	//其他常量定义
	const int DEF_ICMP_DATA_SIZE = 32;    //ICMP报文默认数据字段长度
	const int MAX_ICMP_PACKET_SIZE = 1024;  //ICMP报文最大长度(包括报头)
	const DWORD DEF_ICMP_TIMEOUT = 3000;  //回显应答超时时间
	const int DEF_MAX_HOP = 30;    //最大跳站数
	
	//填充ICMP报文中每次发送时不变的字段
	char * IcmpSendBuf = buffer+sizeof(IP_HEADER);//发送缓冲区
	memset(IcmpSendBuf, 0, sizeof(IcmpSendBuf));               //初始化发送缓冲区
	//char IcmpRecvBuf[MAX_ICMP_PACKET_SIZE];                    //接收缓冲区
	//memset(IcmpRecvBuf, 0, sizeof(IcmpRecvBuf));               //初始化接收缓冲区
	
	/*填写ICMP头,回显请求*/
	ICMP_HEADER* pIcmpHeader = (ICMP_HEADER*)IcmpSendBuf;
	pIcmpHeader->type = ICMP_ECHO_REQUEST; 
	pIcmpHeader->code = 0; 
	/*ID字段为当前进程号*/
	//pIcmpHeader->id = (USHORT)GetCurrentProcessId();
	pIcmpHeader->id = 0x002a;
	memset(IcmpSendBuf + sizeof(ICMP_HEADER), 'E', DEF_ICMP_DATA_SIZE);//数据字段
	//填充ICMP报文中每次发送变化的字段
        ((ICMP_HEADER*)IcmpSendBuf)->cksum = 0;                   //校验和先置为0
        //((ICMP_HEADER*)IcmpSendBuf)->seq = htons(usSeqNo++);      //填充序列号
        ((ICMP_HEADER*)IcmpSendBuf)->seq = 256;
        ((ICMP_HEADER*)IcmpSendBuf)->cksum = checksum((USHORT*)IcmpSendBuf, sizeof(ICMP_HEADER) + DEF_ICMP_DATA_SIZE); //计算校验和
	//printf("%d$$$$$$$$$$\n",sizeof(char));
	IP_HEADER* pIPHeader = (IP_HEADER*)buffer;
	pIPHeader->version	= 4;
	pIPHeader->ihl		= 5;
	pIPHeader->tos		= 0;
	pIPHeader->tot_len	= (sizeof(IP_HEADER) + sizeof(ICMP_HEADER) + DEF_ICMP_DATA_SIZE);
	pIPHeader->id		= 1;
	pIPHeader->frag_off	= 0x000;
	pIPHeader->ttl		= 100;
	pIPHeader->protocol	= 1;	//TCP的协议号为6,UDP的协议号为17。ICMP的协议号为1,IGMP的协议号为2
	pIPHeader->saddr	= inet_addr("10.9.0.5");
	pIPHeader->daddr	= inet_addr("8.8.8.8");
	//memcpy(&pIPHeader->saddr,packet+30,4);
	//memcpy(&pIPHeader->daddr,packet+26,4);
	
	pIPHeader->check	= checksum((USHORT*)buffer, sizeof(IP_HEADER)+sizeof(ICMP_HEADER)+DEF_ICMP_DATA_SIZE);
	
	sd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
	if(sd < 0) {
		perror("socket() error"); exit(-1);
	}
	/* This data structure is needed when sending the packets
	 * using sockets. Normally, we need to fill out several
	 * fields, but for raw sockets, we only need to fill out
	 * this one field */
	sin.sin_family = AF_INET;
	// Here you can construct the IP packet using buffer[]
	// - construct the IP header ...
	// - construct the TCP/UDP/ICMP header ...
	// - fill in the data part if needed ...
	// Note: you should pay attention to the network/host byte order.
	/* Send out the IP packet.
	 * ip_len is the actual size of the packet. */
	int ip_len = (sizeof(IP_HEADER) + sizeof(ICMP_HEADER) + DEF_ICMP_DATA_SIZE);
	if(sendto(sd, buffer, ip_len, 0, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
		perror("sendto() error"); exit(-1);
	}
	else {
		printf("SEND OUT!!!%d\n",pIPHeader->tot_len);
	}

	}
	return 0;
}
  • 14
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值